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',
1073 // why are we validating the weights?
1074 if (Roo.bootstrap.Button.weights.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 (Roo.bootstrap.Button.weights.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 (Roo.bootstrap.Button.weights.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); // this modifies 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.weights.map(function(w) { return 'btn-' + w; } ) );
1358 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1360 var outline = this.outline ? 'outline-' : '';
1361 if (str == 'default') {
1362 this.el.addClass('btn-default btn-outline-secondary');
1365 this.el.addClass('btn-' + outline + str);
1370 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1372 Roo.bootstrap.Button.weights = [
1392 * @class Roo.bootstrap.Column
1393 * @extends Roo.bootstrap.Component
1394 * Bootstrap Column class
1395 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1396 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1397 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1398 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1399 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1400 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1401 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1402 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1405 * @cfg {Boolean} hidden (true|false) hide the element
1406 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1407 * @cfg {String} fa (ban|check|...) font awesome icon
1408 * @cfg {Number} fasize (1|2|....) font awsome size
1410 * @cfg {String} icon (info-sign|check|...) glyphicon name
1412 * @cfg {String} html content of column.
1415 * Create a new Column
1416 * @param {Object} config The config object
1419 Roo.bootstrap.Column = function(config){
1420 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1423 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1441 getAutoCreate : function(){
1442 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1450 var sizes = ['xs','sm','md','lg'];
1451 sizes.map(function(size ,ix){
1452 //Roo.log( size + ':' + settings[size]);
1454 if (settings[size+'off'] !== false) {
1455 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1458 if (settings[size] === false) {
1462 if (!settings[size]) { // 0 = hidden
1463 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1465 for (var i = ix; i > -1; i--) {
1466 cfg.cls += ' d-' + sizes[i] + '-none';
1472 cfg.cls += ' col-' + size + '-' + settings[size] + (
1473 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1479 cfg.cls += ' hidden';
1482 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1483 cfg.cls +=' alert alert-' + this.alert;
1487 if (this.html.length) {
1488 cfg.html = this.html;
1492 if (this.fasize > 1) {
1493 fasize = ' fa-' + this.fasize + 'x';
1495 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1500 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1519 * @class Roo.bootstrap.Container
1520 * @extends Roo.bootstrap.Component
1521 * Bootstrap Container class
1522 * @cfg {Boolean} jumbotron is it a jumbotron element
1523 * @cfg {String} html content of element
1524 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1525 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1526 * @cfg {String} header content of header (for panel)
1527 * @cfg {String} footer content of footer (for panel)
1528 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1529 * @cfg {String} tag (header|aside|section) type of HTML tag.
1530 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1531 * @cfg {String} fa font awesome icon
1532 * @cfg {String} icon (info-sign|check|...) glyphicon name
1533 * @cfg {Boolean} hidden (true|false) hide the element
1534 * @cfg {Boolean} expandable (true|false) default false
1535 * @cfg {Boolean} expanded (true|false) default true
1536 * @cfg {String} rheader contet on the right of header
1537 * @cfg {Boolean} clickable (true|false) default false
1541 * Create a new Container
1542 * @param {Object} config The config object
1545 Roo.bootstrap.Container = function(config){
1546 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1552 * After the panel has been expand
1554 * @param {Roo.bootstrap.Container} this
1559 * After the panel has been collapsed
1561 * @param {Roo.bootstrap.Container} this
1566 * When a element is chick
1567 * @param {Roo.bootstrap.Container} this
1568 * @param {Roo.EventObject} e
1574 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1592 getChildContainer : function() {
1598 if (this.panel.length) {
1599 return this.el.select('.panel-body',true).first();
1606 getAutoCreate : function(){
1609 tag : this.tag || 'div',
1613 if (this.jumbotron) {
1614 cfg.cls = 'jumbotron';
1619 // - this is applied by the parent..
1621 // cfg.cls = this.cls + '';
1624 if (this.sticky.length) {
1626 var bd = Roo.get(document.body);
1627 if (!bd.hasClass('bootstrap-sticky')) {
1628 bd.addClass('bootstrap-sticky');
1629 Roo.select('html',true).setStyle('height', '100%');
1632 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1636 if (this.well.length) {
1637 switch (this.well) {
1640 cfg.cls +=' well well-' +this.well;
1649 cfg.cls += ' hidden';
1653 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1654 cfg.cls +=' alert alert-' + this.alert;
1659 if (this.panel.length) {
1660 cfg.cls += ' panel panel-' + this.panel;
1662 if (this.header.length) {
1666 if(this.expandable){
1668 cfg.cls = cfg.cls + ' expandable';
1672 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1680 cls : 'panel-title',
1681 html : (this.expandable ? ' ' : '') + this.header
1685 cls: 'panel-header-right',
1691 cls : 'panel-heading',
1692 style : this.expandable ? 'cursor: pointer' : '',
1700 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1705 if (this.footer.length) {
1707 cls : 'panel-footer',
1716 body.html = this.html || cfg.html;
1717 // prefix with the icons..
1719 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1722 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1727 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1728 cfg.cls = 'container';
1734 initEvents: function()
1736 if(this.expandable){
1737 var headerEl = this.headerEl();
1740 headerEl.on('click', this.onToggleClick, this);
1745 this.el.on('click', this.onClick, this);
1750 onToggleClick : function()
1752 var headerEl = this.headerEl();
1768 if(this.fireEvent('expand', this)) {
1770 this.expanded = true;
1772 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1774 this.el.select('.panel-body',true).first().removeClass('hide');
1776 var toggleEl = this.toggleEl();
1782 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1787 collapse : function()
1789 if(this.fireEvent('collapse', this)) {
1791 this.expanded = false;
1793 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1794 this.el.select('.panel-body',true).first().addClass('hide');
1796 var toggleEl = this.toggleEl();
1802 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1806 toggleEl : function()
1808 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1812 return this.el.select('.panel-heading .fa',true).first();
1815 headerEl : function()
1817 if(!this.el || !this.panel.length || !this.header.length){
1821 return this.el.select('.panel-heading',true).first()
1826 if(!this.el || !this.panel.length){
1830 return this.el.select('.panel-body',true).first()
1833 titleEl : function()
1835 if(!this.el || !this.panel.length || !this.header.length){
1839 return this.el.select('.panel-title',true).first();
1842 setTitle : function(v)
1844 var titleEl = this.titleEl();
1850 titleEl.dom.innerHTML = v;
1853 getTitle : function()
1856 var titleEl = this.titleEl();
1862 return titleEl.dom.innerHTML;
1865 setRightTitle : function(v)
1867 var t = this.el.select('.panel-header-right',true).first();
1873 t.dom.innerHTML = v;
1876 onClick : function(e)
1880 this.fireEvent('click', this, e);
1887 * This is BS4's Card element.. - similar to our containers probably..
1891 * @class Roo.bootstrap.Card
1892 * @extends Roo.bootstrap.Component
1893 * Bootstrap Card class
1896 * possible... may not be implemented..
1897 * @cfg {String} header_image src url of image.
1898 * @cfg {String|Object} header
1899 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1901 * @cfg {String} title
1902 * @cfg {String} subtitle
1903 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1904 * @cfg {String} footer
1906 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1908 * @cfg {String} margin (0|1|2|3|4|5|auto)
1909 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1910 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1911 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1912 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1913 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1914 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1916 * @cfg {String} padding (0|1|2|3|4|5)
1917 * @cfg {String} padding_top (0|1|2|3|4|5)
1918 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1919 * @cfg {String} padding_left (0|1|2|3|4|5)
1920 * @cfg {String} padding_right (0|1|2|3|4|5)
1921 * @cfg {String} padding_x (0|1|2|3|4|5)
1922 * @cfg {String} padding_y (0|1|2|3|4|5)
1924 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1925 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1926 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1927 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1928 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1930 * @config {Boolean} dragable if this card can be dragged.
1931 * @config {String} drag_group group for drag
1932 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1933 * @config {String} drop_group group for drag
1935 * @config {Boolean} collapsable can the body be collapsed.
1936 * @config {Boolean} collapsed is the body collapsed when rendered...
1937 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1938 * @config {Boolean} rotated is the body rotated when rendered...
1941 * Create a new Container
1942 * @param {Object} config The config object
1945 Roo.bootstrap.Card = function(config){
1946 Roo.bootstrap.Card.superclass.constructor.call(this, config);
1952 * When a element a card is dropped
1953 * @param {Roo.bootstrap.Card} this
1956 * @param {Roo.bootstrap.Card} move_card the card being dropped?
1957 * @param {String} position 'above' or 'below'
1958 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
1964 * When a element a card is rotate
1965 * @param {Roo.bootstrap.Element} this
1966 * @param {Roo.Element} n the node being dropped?
1967 * @param {Boolean} rotate status
1975 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
1980 margin: '', /// may be better in component?
2010 collapsable : false,
2019 childContainer : false,
2020 dropEl : false, /// the dom placeholde element that indicates drop location.
2022 layoutCls : function()
2026 Roo.log(this.margin_bottom.length);
2027 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2028 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2030 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2031 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2033 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2034 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2038 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2039 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2040 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2044 // more generic support?
2052 // Roo.log("Call onRender: " + this.xtype);
2053 /* We are looking at something like this.
2055 <img src="..." class="card-img-top" alt="...">
2056 <div class="card-body">
2057 <h5 class="card-title">Card title</h5>
2058 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2060 >> this bit is really the body...
2061 <div> << we will ad dthis in hopefully it will not break shit.
2063 ** card text does not actually have any styling...
2065 <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>
2068 <a href="#" class="card-link">Card link</a>
2071 <div class="card-footer">
2072 <small class="text-muted">Last updated 3 mins ago</small>
2076 getAutoCreate : function(){
2084 if (this.weight.length && this.weight != 'light') {
2085 cfg.cls += ' text-white';
2087 cfg.cls += ' text-dark'; // need as it's nested..
2089 if (this.weight.length) {
2090 cfg.cls += ' bg-' + this.weight;
2093 cfg.cls += this.layoutCls();
2096 var hdr_ctr = false;
2097 if (this.header.length) {
2099 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2100 cls : 'card-header',
2108 cls : 'card-header d-none',
2114 if (this.collapsable) {
2117 cls : 'd-block user-select-none',
2121 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2126 hdr.cn.push(hdr_ctr);
2131 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2136 if (this.header_image.length) {
2139 cls : 'card-img-top',
2140 src: this.header_image // escape?
2145 cls : 'card-img-top d-none'
2151 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2155 if (this.collapsable || this.rotateable) {
2158 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2165 if (this.title.length) {
2169 src: this.title // escape?
2173 if (this.subtitle.length) {
2177 src: this.subtitle // escape?
2183 cls : 'roo-card-body-ctr'
2186 if (this.html.length) {
2192 // fixme ? handle objects?
2194 if (this.footer.length) {
2197 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2202 cfg.cn.push({cls : 'card-footer d-none'});
2211 getCardHeader : function()
2213 var ret = this.el.select('.card-header',true).first();
2214 if (ret.hasClass('d-none')) {
2215 ret.removeClass('d-none');
2220 getCardFooter : function()
2222 var ret = this.el.select('.card-footer',true).first();
2223 if (ret.hasClass('d-none')) {
2224 ret.removeClass('d-none');
2229 getCardImageTop : function()
2231 var ret = this.el.select('.card-img-top',true).first();
2232 if (ret.hasClass('d-none')) {
2233 ret.removeClass('d-none');
2239 getChildContainer : function()
2245 return this.el.select('.roo-card-body-ctr',true).first();
2248 initEvents: function()
2251 this.bodyEl = this.getChildContainer();
2253 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2254 containerScroll: true,
2255 ddGroup: this.drag_group || 'default_card_drag_group'
2257 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2259 if (this.dropable) {
2260 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2261 containerScroll: true,
2262 ddGroup: this.drop_group || 'default_card_drag_group'
2264 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2265 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2266 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2267 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2268 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2271 if (this.collapsable) {
2272 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2274 if (this.rotateable) {
2275 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2277 this.collapsableEl = this.el.select('.roo-collapsable').first();
2279 this.footerEl = this.el.select('.card-footer').first();
2280 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2281 this.headerEl = this.el.select('.roo-card-header-ctr').first();
2284 this.el.addClass('roo-card-rotated');
2285 this.fireEvent('rotate', this, true);
2289 getDragData : function(e)
2291 var target = this.getEl();
2293 //this.handleSelection(e);
2298 nodes: this.getEl(),
2303 dragData.ddel = target.dom ; // the div element
2304 Roo.log(target.getWidth( ));
2305 dragData.ddel.style.width = target.getWidth() + 'px';
2312 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2313 * whole Element becomes the target, and this causes the drop gesture to append.
2315 getTargetFromEvent : function(e, dragged_card_el)
2317 var target = e.getTarget();
2318 while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2319 target = target.parentNode;
2330 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2331 // see if target is one of the 'cards'...
2334 //Roo.log(this.items.length);
2337 var last_card_n = 0;
2339 for (var i = 0;i< this.items.length;i++) {
2341 if (!this.items[i].el.hasClass('card')) {
2344 pos = this.getDropPoint(e, this.items[i].el.dom);
2346 cards_len = ret.cards.length;
2347 //Roo.log(this.items[i].el.dom.id);
2348 ret.cards.push(this.items[i]);
2350 if (ret.card_n < 0 && pos == 'above') {
2351 ret.position = cards_len > 0 ? 'below' : pos;
2352 ret.items_n = i > 0 ? i - 1 : 0;
2353 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2354 ret.card = ret.cards[ret.card_n];
2357 if (!ret.cards.length) {
2359 ret.position = 'below';
2363 // could not find a card.. stick it at the end..
2364 if (ret.card_n < 0) {
2365 ret.card_n = last_card_n;
2366 ret.card = ret.cards[last_card_n];
2367 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2368 ret.position = 'below';
2371 if (this.items[ret.items_n].el == dragged_card_el) {
2375 if (ret.position == 'below') {
2376 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2378 if (card_after && card_after.el == dragged_card_el) {
2385 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2387 if (card_before && card_before.el == dragged_card_el) {
2394 onNodeEnter : function(n, dd, e, data){
2397 onNodeOver : function(n, dd, e, data)
2400 var target_info = this.getTargetFromEvent(e,data.source.el);
2401 if (target_info === false) {
2402 this.dropPlaceHolder('hide');
2405 Roo.log(['getTargetFromEvent', target_info ]);
2408 this.dropPlaceHolder('show', target_info,data);
2412 onNodeOut : function(n, dd, e, data){
2413 this.dropPlaceHolder('hide');
2416 onNodeDrop : function(n, dd, e, data)
2419 // call drop - return false if
2421 // this could actually fail - if the Network drops..
2422 // we will ignore this at present..- client should probably reload
2423 // the whole set of cards if stuff like that fails.
2426 var info = this.getTargetFromEvent(e,data.source.el);
2427 if (info === false) {
2430 this.dropPlaceHolder('hide');
2436 this.acceptCard(data.source, info.position, info.card, info.items_n);
2440 firstChildCard : function()
2442 for (var i = 0;i< this.items.length;i++) {
2444 if (!this.items[i].el.hasClass('card')) {
2447 return this.items[i];
2449 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2454 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2457 acceptCard : function(move_card, position, next_to_card )
2459 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2463 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2466 var dom = move_card.el.dom;
2467 dom.parentNode.removeChild(dom);
2470 if (next_to_card !== false) {
2471 var cardel = next_to_card.el.dom;
2473 if (position == 'above') {
2474 cardel.parentNode.insertBefore(dom, cardel);
2475 } else if (cardel.nextSibling) {
2476 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2478 cardel.parentNode.append(dom);
2481 // card container???
2482 this.bodyEl.dom.append(dom);
2485 //FIXME HANDLE card = true
2487 // add this to the correct place in items.
2491 // remove Card from items.
2493 var old_parent = move_card.parent();
2495 old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2497 if (this.items.length) {
2499 //Roo.log([info.items_n, info.position, this.items.length]);
2500 for (var i =0; i < this.items.length; i++) {
2501 if (i == to_items_n && position == 'above') {
2502 nitems.push(move_card);
2504 nitems.push(this.items[i]);
2505 if (i == to_items_n && position == 'below') {
2506 nitems.push(move_card);
2509 this.items = nitems;
2510 Roo.log(this.items);
2512 this.items.push(move_card);
2515 move_card.parentId = this.id;
2523 /** Decide whether to drop above or below a View node. */
2524 getDropPoint : function(e, n, dd)
2529 if (n == this.bodyEl.dom) {
2532 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2533 var c = t + (b - t) / 2;
2534 var y = Roo.lib.Event.getPageY(e);
2541 onToggleCollapse : function(e)
2543 if (this.collapsed) {
2544 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2545 this.collapsableEl.addClass('show');
2546 this.collapsed = false;
2549 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2550 this.collapsableEl.removeClass('show');
2551 this.collapsed = true;
2556 onToggleRotate : function(e)
2558 this.collapsableEl.removeClass('show');
2559 this.footerEl.removeClass('d-none');
2560 this.el.removeClass('roo-card-rotated');
2561 this.el.removeClass('d-none');
2564 this.collapsableEl.addClass('show');
2565 this.rotated = false;
2566 this.fireEvent('rotate', this, this.rotated);
2569 this.el.addClass('roo-card-rotated');
2570 this.footerEl.addClass('d-none');
2571 this.el.select('.roo-collapsable').removeClass('show');
2573 this.rotated = true;
2574 this.fireEvent('rotate', this, this.rotated);
2578 dropPlaceHolder: function (action, info, data)
2580 if (this.dropEl === false) {
2581 this.dropEl = Roo.DomHelper.append(this.bodyEl, {
2585 this.dropEl.removeClass(['d-none', 'd-block']);
2586 if (action == 'hide') {
2588 this.dropEl.addClass('d-none');
2591 // FIXME - info.card == true!!!
2592 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2594 if (info.card !== true) {
2595 var cardel = info.card.el.dom;
2597 if (info.position == 'above') {
2598 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2599 } else if (cardel.nextSibling) {
2600 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2602 cardel.parentNode.append(this.dropEl.dom);
2605 // card container???
2606 this.bodyEl.dom.append(this.dropEl.dom);
2609 this.dropEl.addClass('d-block roo-card-dropzone');
2611 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2618 setHeaderText: function(html)
2620 this.headerEl.dom.innerHTML = html;
2629 * Card header - holder for the card header elements.
2634 * @class Roo.bootstrap.CardHeader
2635 * @extends Roo.bootstrap.Element
2636 * Bootstrap CardHeader class
2638 * Create a new Card Header - that you can embed children into
2639 * @param {Object} config The config object
2642 Roo.bootstrap.CardHeader = function(config){
2643 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2646 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2649 container_method : 'getCardHeader'
2662 * Card footer - holder for the card footer elements.
2667 * @class Roo.bootstrap.CardFooter
2668 * @extends Roo.bootstrap.Element
2669 * Bootstrap CardFooter class
2671 * Create a new Card Footer - that you can embed children into
2672 * @param {Object} config The config object
2675 Roo.bootstrap.CardFooter = function(config){
2676 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2679 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2682 container_method : 'getCardFooter'
2695 * Card header - holder for the card header elements.
2700 * @class Roo.bootstrap.CardImageTop
2701 * @extends Roo.bootstrap.Element
2702 * Bootstrap CardImageTop class
2704 * Create a new Card Image Top container
2705 * @param {Object} config The config object
2708 Roo.bootstrap.CardImageTop = function(config){
2709 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2712 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2715 container_method : 'getCardImageTop'
2733 * @class Roo.bootstrap.Img
2734 * @extends Roo.bootstrap.Component
2735 * Bootstrap Img class
2736 * @cfg {Boolean} imgResponsive false | true
2737 * @cfg {String} border rounded | circle | thumbnail
2738 * @cfg {String} src image source
2739 * @cfg {String} alt image alternative text
2740 * @cfg {String} href a tag href
2741 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2742 * @cfg {String} xsUrl xs image source
2743 * @cfg {String} smUrl sm image source
2744 * @cfg {String} mdUrl md image source
2745 * @cfg {String} lgUrl lg image source
2748 * Create a new Input
2749 * @param {Object} config The config object
2752 Roo.bootstrap.Img = function(config){
2753 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2759 * The img click event for the img.
2760 * @param {Roo.EventObject} e
2766 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2768 imgResponsive: true,
2778 getAutoCreate : function()
2780 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2781 return this.createSingleImg();
2786 cls: 'roo-image-responsive-group',
2791 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2793 if(!_this[size + 'Url']){
2799 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2800 html: _this.html || cfg.html,
2801 src: _this[size + 'Url']
2804 img.cls += ' roo-image-responsive-' + size;
2806 var s = ['xs', 'sm', 'md', 'lg'];
2808 s.splice(s.indexOf(size), 1);
2810 Roo.each(s, function(ss){
2811 img.cls += ' hidden-' + ss;
2814 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2815 cfg.cls += ' img-' + _this.border;
2819 cfg.alt = _this.alt;
2832 a.target = _this.target;
2836 cfg.cn.push((_this.href) ? a : img);
2843 createSingleImg : function()
2847 cls: (this.imgResponsive) ? 'img-responsive' : '',
2849 src : 'about:blank' // just incase src get's set to undefined?!?
2852 cfg.html = this.html || cfg.html;
2854 cfg.src = this.src || cfg.src;
2856 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2857 cfg.cls += ' img-' + this.border;
2874 a.target = this.target;
2879 return (this.href) ? a : cfg;
2882 initEvents: function()
2885 this.el.on('click', this.onClick, this);
2890 onClick : function(e)
2892 Roo.log('img onclick');
2893 this.fireEvent('click', this, e);
2896 * Sets the url of the image - used to update it
2897 * @param {String} url the url of the image
2900 setSrc : function(url)
2904 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2905 this.el.dom.src = url;
2909 this.el.select('img', true).first().dom.src = url;
2925 * @class Roo.bootstrap.Link
2926 * @extends Roo.bootstrap.Component
2927 * Bootstrap Link Class
2928 * @cfg {String} alt image alternative text
2929 * @cfg {String} href a tag href
2930 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2931 * @cfg {String} html the content of the link.
2932 * @cfg {String} anchor name for the anchor link
2933 * @cfg {String} fa - favicon
2935 * @cfg {Boolean} preventDefault (true | false) default false
2939 * Create a new Input
2940 * @param {Object} config The config object
2943 Roo.bootstrap.Link = function(config){
2944 Roo.bootstrap.Link.superclass.constructor.call(this, config);
2950 * The img click event for the img.
2951 * @param {Roo.EventObject} e
2957 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
2961 preventDefault: false,
2967 getAutoCreate : function()
2969 var html = this.html || '';
2971 if (this.fa !== false) {
2972 html = '<i class="fa fa-' + this.fa + '"></i>';
2977 // anchor's do not require html/href...
2978 if (this.anchor === false) {
2980 cfg.href = this.href || '#';
2982 cfg.name = this.anchor;
2983 if (this.html !== false || this.fa !== false) {
2986 if (this.href !== false) {
2987 cfg.href = this.href;
2991 if(this.alt !== false){
2996 if(this.target !== false) {
2997 cfg.target = this.target;
3003 initEvents: function() {
3005 if(!this.href || this.preventDefault){
3006 this.el.on('click', this.onClick, this);
3010 onClick : function(e)
3012 if(this.preventDefault){
3015 //Roo.log('img onclick');
3016 this.fireEvent('click', this, e);
3029 * @class Roo.bootstrap.Header
3030 * @extends Roo.bootstrap.Component
3031 * Bootstrap Header class
3032 * @cfg {String} html content of header
3033 * @cfg {Number} level (1|2|3|4|5|6) default 1
3036 * Create a new Header
3037 * @param {Object} config The config object
3041 Roo.bootstrap.Header = function(config){
3042 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3053 getAutoCreate : function(){
3058 tag: 'h' + (1 *this.level),
3059 html: this.html || ''
3071 * Ext JS Library 1.1.1
3072 * Copyright(c) 2006-2007, Ext JS, LLC.
3074 * Originally Released Under LGPL - original licence link has changed is not relivant.
3077 * <script type="text/javascript">
3081 * @class Roo.bootstrap.MenuMgr
3082 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3085 Roo.bootstrap.MenuMgr = function(){
3086 var menus, active, groups = {}, attached = false, lastShow = new Date();
3088 // private - called when first menu is created
3091 active = new Roo.util.MixedCollection();
3092 Roo.get(document).addKeyListener(27, function(){
3093 if(active.length > 0){
3101 if(active && active.length > 0){
3102 var c = active.clone();
3112 if(active.length < 1){
3113 Roo.get(document).un("mouseup", onMouseDown);
3121 var last = active.last();
3122 lastShow = new Date();
3125 Roo.get(document).on("mouseup", onMouseDown);
3130 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3131 m.parentMenu.activeChild = m;
3132 }else if(last && last.isVisible()){
3133 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3138 function onBeforeHide(m){
3140 m.activeChild.hide();
3142 if(m.autoHideTimer){
3143 clearTimeout(m.autoHideTimer);
3144 delete m.autoHideTimer;
3149 function onBeforeShow(m){
3150 var pm = m.parentMenu;
3151 if(!pm && !m.allowOtherMenus){
3153 }else if(pm && pm.activeChild && active != m){
3154 pm.activeChild.hide();
3158 // private this should really trigger on mouseup..
3159 function onMouseDown(e){
3160 Roo.log("on Mouse Up");
3162 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3163 Roo.log("MenuManager hideAll");
3172 function onBeforeCheck(mi, state){
3174 var g = groups[mi.group];
3175 for(var i = 0, l = g.length; i < l; i++){
3177 g[i].setChecked(false);
3186 * Hides all menus that are currently visible
3188 hideAll : function(){
3193 register : function(menu){
3197 menus[menu.id] = menu;
3198 menu.on("beforehide", onBeforeHide);
3199 menu.on("hide", onHide);
3200 menu.on("beforeshow", onBeforeShow);
3201 menu.on("show", onShow);
3203 if(g && menu.events["checkchange"]){
3207 groups[g].push(menu);
3208 menu.on("checkchange", onCheck);
3213 * Returns a {@link Roo.menu.Menu} object
3214 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3215 * be used to generate and return a new Menu instance.
3217 get : function(menu){
3218 if(typeof menu == "string"){ // menu id
3220 }else if(menu.events){ // menu instance
3223 /*else if(typeof menu.length == 'number'){ // array of menu items?
3224 return new Roo.bootstrap.Menu({items:menu});
3225 }else{ // otherwise, must be a config
3226 return new Roo.bootstrap.Menu(menu);
3233 unregister : function(menu){
3234 delete menus[menu.id];
3235 menu.un("beforehide", onBeforeHide);
3236 menu.un("hide", onHide);
3237 menu.un("beforeshow", onBeforeShow);
3238 menu.un("show", onShow);
3240 if(g && menu.events["checkchange"]){
3241 groups[g].remove(menu);
3242 menu.un("checkchange", onCheck);
3247 registerCheckable : function(menuItem){
3248 var g = menuItem.group;
3253 groups[g].push(menuItem);
3254 menuItem.on("beforecheckchange", onBeforeCheck);
3259 unregisterCheckable : function(menuItem){
3260 var g = menuItem.group;
3262 groups[g].remove(menuItem);
3263 menuItem.un("beforecheckchange", onBeforeCheck);
3275 * @class Roo.bootstrap.Menu
3276 * @extends Roo.bootstrap.Component
3277 * Bootstrap Menu class - container for MenuItems
3278 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3279 * @cfg {bool} hidden if the menu should be hidden when rendered.
3280 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3281 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3285 * @param {Object} config The config object
3289 Roo.bootstrap.Menu = function(config){
3290 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3291 if (this.registerMenu && this.type != 'treeview') {
3292 Roo.bootstrap.MenuMgr.register(this);
3299 * Fires before this menu is displayed (return false to block)
3300 * @param {Roo.menu.Menu} this
3305 * Fires before this menu is hidden (return false to block)
3306 * @param {Roo.menu.Menu} this
3311 * Fires after this menu is displayed
3312 * @param {Roo.menu.Menu} this
3317 * Fires after this menu is hidden
3318 * @param {Roo.menu.Menu} this
3323 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3324 * @param {Roo.menu.Menu} this
3325 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3326 * @param {Roo.EventObject} e
3331 * Fires when the mouse is hovering over this menu
3332 * @param {Roo.menu.Menu} this
3333 * @param {Roo.EventObject} e
3334 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3339 * Fires when the mouse exits this menu
3340 * @param {Roo.menu.Menu} this
3341 * @param {Roo.EventObject} e
3342 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3347 * Fires when a menu item contained in this menu is clicked
3348 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3349 * @param {Roo.EventObject} e
3353 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3356 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3360 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3363 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3365 registerMenu : true,
3367 menuItems :false, // stores the menu items..
3377 getChildContainer : function() {
3381 getAutoCreate : function(){
3383 //if (['right'].indexOf(this.align)!==-1) {
3384 // cfg.cn[1].cls += ' pull-right'
3390 cls : 'dropdown-menu' ,
3391 style : 'z-index:1000'
3395 if (this.type === 'submenu') {
3396 cfg.cls = 'submenu active';
3398 if (this.type === 'treeview') {
3399 cfg.cls = 'treeview-menu';
3404 initEvents : function() {
3406 // Roo.log("ADD event");
3407 // Roo.log(this.triggerEl.dom);
3409 this.triggerEl.on('click', this.onTriggerClick, this);
3411 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3414 if (this.triggerEl.hasClass('nav-item')) {
3415 // dropdown toggle on the 'a' in BS4?
3416 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3418 this.triggerEl.addClass('dropdown-toggle');
3421 this.el.on('touchstart' , this.onTouch, this);
3423 this.el.on('click' , this.onClick, this);
3425 this.el.on("mouseover", this.onMouseOver, this);
3426 this.el.on("mouseout", this.onMouseOut, this);
3430 findTargetItem : function(e)
3432 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3436 //Roo.log(t); Roo.log(t.id);
3438 //Roo.log(this.menuitems);
3439 return this.menuitems.get(t.id);
3441 //return this.items.get(t.menuItemId);
3447 onTouch : function(e)
3449 Roo.log("menu.onTouch");
3450 //e.stopEvent(); this make the user popdown broken
3454 onClick : function(e)
3456 Roo.log("menu.onClick");
3458 var t = this.findTargetItem(e);
3459 if(!t || t.isContainer){
3464 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3465 if(t == this.activeItem && t.shouldDeactivate(e)){
3466 this.activeItem.deactivate();
3467 delete this.activeItem;
3471 this.setActiveItem(t, true);
3479 Roo.log('pass click event');
3483 this.fireEvent("click", this, t, e);
3487 if(!t.href.length || t.href == '#'){
3488 (function() { _this.hide(); }).defer(100);
3493 onMouseOver : function(e){
3494 var t = this.findTargetItem(e);
3497 // if(t.canActivate && !t.disabled){
3498 // this.setActiveItem(t, true);
3502 this.fireEvent("mouseover", this, e, t);
3504 isVisible : function(){
3505 return !this.hidden;
3507 onMouseOut : function(e){
3508 var t = this.findTargetItem(e);
3511 // if(t == this.activeItem && t.shouldDeactivate(e)){
3512 // this.activeItem.deactivate();
3513 // delete this.activeItem;
3516 this.fireEvent("mouseout", this, e, t);
3521 * Displays this menu relative to another element
3522 * @param {String/HTMLElement/Roo.Element} element The element to align to
3523 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3524 * the element (defaults to this.defaultAlign)
3525 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3527 show : function(el, pos, parentMenu)
3529 if (false === this.fireEvent("beforeshow", this)) {
3530 Roo.log("show canceled");
3533 this.parentMenu = parentMenu;
3538 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3541 * Displays this menu at a specific xy position
3542 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3543 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3545 showAt : function(xy, parentMenu, /* private: */_e){
3546 this.parentMenu = parentMenu;
3551 this.fireEvent("beforeshow", this);
3552 //xy = this.el.adjustForConstraints(xy);
3556 this.hideMenuItems();
3557 this.hidden = false;
3558 this.triggerEl.addClass('open');
3559 this.el.addClass('show');
3561 // reassign x when hitting right
3562 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3563 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3566 // reassign y when hitting bottom
3567 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3568 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3571 // but the list may align on trigger left or trigger top... should it be a properity?
3573 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3578 this.fireEvent("show", this);
3584 this.doFocus.defer(50, this);
3588 doFocus : function(){
3590 this.focusEl.focus();
3595 * Hides this menu and optionally all parent menus
3596 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3598 hide : function(deep)
3600 if (false === this.fireEvent("beforehide", this)) {
3601 Roo.log("hide canceled");
3604 this.hideMenuItems();
3605 if(this.el && this.isVisible()){
3607 if(this.activeItem){
3608 this.activeItem.deactivate();
3609 this.activeItem = null;
3611 this.triggerEl.removeClass('open');;
3612 this.el.removeClass('show');
3614 this.fireEvent("hide", this);
3616 if(deep === true && this.parentMenu){
3617 this.parentMenu.hide(true);
3621 onTriggerClick : function(e)
3623 Roo.log('trigger click');
3625 var target = e.getTarget();
3627 Roo.log(target.nodeName.toLowerCase());
3629 if(target.nodeName.toLowerCase() === 'i'){
3635 onTriggerPress : function(e)
3637 Roo.log('trigger press');
3638 //Roo.log(e.getTarget());
3639 // Roo.log(this.triggerEl.dom);
3641 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3642 var pel = Roo.get(e.getTarget());
3643 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3644 Roo.log('is treeview or dropdown?');
3648 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3652 if (this.isVisible()) {
3657 this.show(this.triggerEl, '?', false);
3660 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3667 hideMenuItems : function()
3669 Roo.log("hide Menu Items");
3674 this.el.select('.open',true).each(function(aa) {
3676 aa.removeClass('open');
3680 addxtypeChild : function (tree, cntr) {
3681 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3683 this.menuitems.add(comp);
3695 this.getEl().dom.innerHTML = '';
3696 this.menuitems.clear();
3710 * @class Roo.bootstrap.MenuItem
3711 * @extends Roo.bootstrap.Component
3712 * Bootstrap MenuItem class
3713 * @cfg {String} html the menu label
3714 * @cfg {String} href the link
3715 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3716 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3717 * @cfg {Boolean} active used on sidebars to highlight active itesm
3718 * @cfg {String} fa favicon to show on left of menu item.
3719 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3723 * Create a new MenuItem
3724 * @param {Object} config The config object
3728 Roo.bootstrap.MenuItem = function(config){
3729 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3734 * The raw click event for the entire grid.
3735 * @param {Roo.bootstrap.MenuItem} this
3736 * @param {Roo.EventObject} e
3742 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3746 preventDefault: false,
3747 isContainer : false,
3751 getAutoCreate : function(){
3753 if(this.isContainer){
3756 cls: 'dropdown-menu-item '
3766 cls : 'dropdown-item',
3771 if (this.fa !== false) {
3774 cls : 'fa fa-' + this.fa
3783 cls: 'dropdown-menu-item',
3786 if (this.parent().type == 'treeview') {
3787 cfg.cls = 'treeview-menu';
3790 cfg.cls += ' active';
3795 anc.href = this.href || cfg.cn[0].href ;
3796 ctag.html = this.html || cfg.cn[0].html ;
3800 initEvents: function()
3802 if (this.parent().type == 'treeview') {
3803 this.el.select('a').on('click', this.onClick, this);
3807 this.menu.parentType = this.xtype;
3808 this.menu.triggerEl = this.el;
3809 this.menu = this.addxtype(Roo.apply({}, this.menu));
3813 onClick : function(e)
3815 Roo.log('item on click ');
3817 if(this.preventDefault){
3820 //this.parent().hideMenuItems();
3822 this.fireEvent('click', this, e);
3841 * @class Roo.bootstrap.MenuSeparator
3842 * @extends Roo.bootstrap.Component
3843 * Bootstrap MenuSeparator class
3846 * Create a new MenuItem
3847 * @param {Object} config The config object
3851 Roo.bootstrap.MenuSeparator = function(config){
3852 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3855 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3857 getAutoCreate : function(){
3876 * @class Roo.bootstrap.Modal
3877 * @extends Roo.bootstrap.Component
3878 * Bootstrap Modal class
3879 * @cfg {String} title Title of dialog
3880 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3881 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3882 * @cfg {Boolean} specificTitle default false
3883 * @cfg {Array} buttons Array of buttons or standard button set..
3884 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3885 * @cfg {Boolean} animate default true
3886 * @cfg {Boolean} allow_close default true
3887 * @cfg {Boolean} fitwindow default false
3888 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3889 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3890 * @cfg {String} size (sm|lg|xl) default empty
3891 * @cfg {Number} max_width set the max width of modal
3892 * @cfg {Boolean} editableTitle can the title be edited
3897 * Create a new Modal Dialog
3898 * @param {Object} config The config object
3901 Roo.bootstrap.Modal = function(config){
3902 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3907 * The raw btnclick event for the button
3908 * @param {Roo.EventObject} e
3913 * Fire when dialog resize
3914 * @param {Roo.bootstrap.Modal} this
3915 * @param {Roo.EventObject} e
3919 * @event titlechanged
3920 * Fire when the editable title has been changed
3921 * @param {Roo.bootstrap.Modal} this
3922 * @param {Roo.EventObject} value
3924 "titlechanged" : true
3927 this.buttons = this.buttons || [];
3930 this.tmpl = Roo.factory(this.tmpl);
3935 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3937 title : 'test dialog',
3947 specificTitle: false,
3949 buttonPosition: 'right',
3971 editableTitle : false,
3973 onRender : function(ct, position)
3975 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3978 var cfg = Roo.apply({}, this.getAutoCreate());
3981 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3983 //if (!cfg.name.length) {
3987 cfg.cls += ' ' + this.cls;
3990 cfg.style = this.style;
3992 this.el = Roo.get(document.body).createChild(cfg, position);
3994 //var type = this.el.dom.type;
3997 if(this.tabIndex !== undefined){
3998 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4001 this.dialogEl = this.el.select('.modal-dialog',true).first();
4002 this.bodyEl = this.el.select('.modal-body',true).first();
4003 this.closeEl = this.el.select('.modal-header .close', true).first();
4004 this.headerEl = this.el.select('.modal-header',true).first();
4005 this.titleEl = this.el.select('.modal-title',true).first();
4006 this.footerEl = this.el.select('.modal-footer',true).first();
4008 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4010 //this.el.addClass("x-dlg-modal");
4012 if (this.buttons.length) {
4013 Roo.each(this.buttons, function(bb) {
4014 var b = Roo.apply({}, bb);
4015 b.xns = b.xns || Roo.bootstrap;
4016 b.xtype = b.xtype || 'Button';
4017 if (typeof(b.listeners) == 'undefined') {
4018 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4021 var btn = Roo.factory(b);
4023 btn.render(this.getButtonContainer());
4027 // render the children.
4030 if(typeof(this.items) != 'undefined'){
4031 var items = this.items;
4034 for(var i =0;i < items.length;i++) {
4035 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4039 this.items = nitems;
4041 // where are these used - they used to be body/close/footer
4045 //this.el.addClass([this.fieldClass, this.cls]);
4049 getAutoCreate : function()
4051 // we will default to modal-body-overflow - might need to remove or make optional later.
4053 cls : 'modal-body enable-modal-body-overflow ',
4054 html : this.html || ''
4059 cls : 'modal-title',
4063 if(this.specificTitle){ // WTF is this?
4068 if (this.allow_close && Roo.bootstrap.version == 3) {
4078 if (this.editableTitle) {
4080 cls: 'form-control roo-editable-title d-none',
4086 if (this.allow_close && Roo.bootstrap.version == 4) {
4096 if(this.size.length){
4097 size = 'modal-' + this.size;
4100 var footer = Roo.bootstrap.version == 3 ?
4102 cls : 'modal-footer',
4106 cls: 'btn-' + this.buttonPosition
4111 { // BS4 uses mr-auto on left buttons....
4112 cls : 'modal-footer'
4123 cls: "modal-dialog " + size,
4126 cls : "modal-content",
4129 cls : 'modal-header',
4144 modal.cls += ' fade';
4150 getChildContainer : function() {
4155 getButtonContainer : function() {
4157 return Roo.bootstrap.version == 4 ?
4158 this.el.select('.modal-footer',true).first()
4159 : this.el.select('.modal-footer div',true).first();
4162 initEvents : function()
4164 if (this.allow_close) {
4165 this.closeEl.on('click', this.hide, this);
4167 Roo.EventManager.onWindowResize(this.resize, this, true);
4168 if (this.editableTitle) {
4169 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4170 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4171 this.headerEditEl.on('keyup', function(e) {
4172 if(e.isNavKeyPress()){
4173 this.toggleHeaderInput(false)
4176 this.headerEditEl.on('blur', function(e) {
4177 this.toggleHeaderInput(false)
4186 this.maskEl.setSize(
4187 Roo.lib.Dom.getViewWidth(true),
4188 Roo.lib.Dom.getViewHeight(true)
4191 if (this.fitwindow) {
4195 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4196 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4201 if(this.max_width !== 0) {
4203 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4206 this.setSize(w, this.height);
4210 if(this.max_height) {
4211 this.setSize(w,Math.min(
4213 Roo.lib.Dom.getViewportHeight(true) - 60
4219 if(!this.fit_content) {
4220 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4224 this.setSize(w, Math.min(
4226 this.headerEl.getHeight() +
4227 this.footerEl.getHeight() +
4228 this.getChildHeight(this.bodyEl.dom.childNodes),
4229 Roo.lib.Dom.getViewportHeight(true) - 60)
4235 setSize : function(w,h)
4246 if (!this.rendered) {
4250 //this.el.setStyle('display', 'block');
4251 this.el.removeClass('hideing');
4252 this.el.dom.style.display='block';
4254 Roo.get(document.body).addClass('modal-open');
4256 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4259 this.el.addClass('show');
4260 this.el.addClass('in');
4263 this.el.addClass('show');
4264 this.el.addClass('in');
4267 // not sure how we can show data in here..
4269 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4272 Roo.get(document.body).addClass("x-body-masked");
4274 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4275 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4276 this.maskEl.dom.style.display = 'block';
4277 this.maskEl.addClass('show');
4282 this.fireEvent('show', this);
4284 // set zindex here - otherwise it appears to be ignored...
4285 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4288 this.items.forEach( function(e) {
4289 e.layout ? e.layout() : false;
4297 if(this.fireEvent("beforehide", this) !== false){
4299 this.maskEl.removeClass('show');
4301 this.maskEl.dom.style.display = '';
4302 Roo.get(document.body).removeClass("x-body-masked");
4303 this.el.removeClass('in');
4304 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4306 if(this.animate){ // why
4307 this.el.addClass('hideing');
4308 this.el.removeClass('show');
4310 if (!this.el.hasClass('hideing')) {
4311 return; // it's been shown again...
4314 this.el.dom.style.display='';
4316 Roo.get(document.body).removeClass('modal-open');
4317 this.el.removeClass('hideing');
4321 this.el.removeClass('show');
4322 this.el.dom.style.display='';
4323 Roo.get(document.body).removeClass('modal-open');
4326 this.fireEvent('hide', this);
4329 isVisible : function()
4332 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4336 addButton : function(str, cb)
4340 var b = Roo.apply({}, { html : str } );
4341 b.xns = b.xns || Roo.bootstrap;
4342 b.xtype = b.xtype || 'Button';
4343 if (typeof(b.listeners) == 'undefined') {
4344 b.listeners = { click : cb.createDelegate(this) };
4347 var btn = Roo.factory(b);
4349 btn.render(this.getButtonContainer());
4355 setDefaultButton : function(btn)
4357 //this.el.select('.modal-footer').()
4360 resizeTo: function(w,h)
4362 this.dialogEl.setWidth(w);
4364 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4366 this.bodyEl.setHeight(h - diff);
4368 this.fireEvent('resize', this);
4371 setContentSize : function(w, h)
4375 onButtonClick: function(btn,e)
4378 this.fireEvent('btnclick', btn.name, e);
4381 * Set the title of the Dialog
4382 * @param {String} str new Title
4384 setTitle: function(str) {
4385 this.titleEl.dom.innerHTML = str;
4389 * Set the body of the Dialog
4390 * @param {String} str new Title
4392 setBody: function(str) {
4393 this.bodyEl.dom.innerHTML = str;
4396 * Set the body of the Dialog using the template
4397 * @param {Obj} data - apply this data to the template and replace the body contents.
4399 applyBody: function(obj)
4402 Roo.log("Error - using apply Body without a template");
4405 this.tmpl.overwrite(this.bodyEl, obj);
4408 getChildHeight : function(child_nodes)
4412 child_nodes.length == 0
4417 var child_height = 0;
4419 for(var i = 0; i < child_nodes.length; i++) {
4422 * for modal with tabs...
4423 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4425 var layout_childs = child_nodes[i].childNodes;
4427 for(var j = 0; j < layout_childs.length; j++) {
4429 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4431 var layout_body_childs = layout_childs[j].childNodes;
4433 for(var k = 0; k < layout_body_childs.length; k++) {
4435 if(layout_body_childs[k].classList.contains('navbar')) {
4436 child_height += layout_body_childs[k].offsetHeight;
4440 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4442 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4444 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4446 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4447 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4462 child_height += child_nodes[i].offsetHeight;
4463 // Roo.log(child_nodes[i].offsetHeight);
4466 return child_height;
4468 toggleHeaderInput : function(is_edit)
4471 if (is_edit && this.is_header_editing) {
4472 return; // already editing..
4476 this.headerEditEl.dom.value = this.title;
4477 this.headerEditEl.removeClass('d-none');
4478 this.headerEditEl.dom.focus();
4479 this.titleEl.addClass('d-none');
4481 this.is_header_editing = true;
4484 // flip back to not editing.
4485 this.title = this.headerEditEl.dom.value;
4486 this.headerEditEl.addClass('d-none');
4487 this.titleEl.removeClass('d-none');
4488 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4489 this.is_header_editing = false;
4490 this.fireEvent('titlechanged', this, this.title);
4499 Roo.apply(Roo.bootstrap.Modal, {
4501 * Button config that displays a single OK button
4510 * Button config that displays Yes and No buttons
4526 * Button config that displays OK and Cancel buttons
4541 * Button config that displays Yes, No and Cancel buttons
4566 * messagebox - can be used as a replace
4570 * @class Roo.MessageBox
4571 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4575 Roo.Msg.alert('Status', 'Changes saved successfully.');
4577 // Prompt for user data:
4578 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4580 // process text value...
4584 // Show a dialog using config options:
4586 title:'Save Changes?',
4587 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4588 buttons: Roo.Msg.YESNOCANCEL,
4595 Roo.bootstrap.MessageBox = function(){
4596 var dlg, opt, mask, waitTimer;
4597 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4598 var buttons, activeTextEl, bwidth;
4602 var handleButton = function(button){
4604 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4608 var handleHide = function(){
4610 dlg.el.removeClass(opt.cls);
4613 // Roo.TaskMgr.stop(waitTimer);
4614 // waitTimer = null;
4619 var updateButtons = function(b){
4622 buttons["ok"].hide();
4623 buttons["cancel"].hide();
4624 buttons["yes"].hide();
4625 buttons["no"].hide();
4626 dlg.footerEl.hide();
4630 dlg.footerEl.show();
4631 for(var k in buttons){
4632 if(typeof buttons[k] != "function"){
4635 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4636 width += buttons[k].el.getWidth()+15;
4646 var handleEsc = function(d, k, e){
4647 if(opt && opt.closable !== false){
4657 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4658 * @return {Roo.BasicDialog} The BasicDialog element
4660 getDialog : function(){
4662 dlg = new Roo.bootstrap.Modal( {
4665 //constraintoviewport:false,
4667 //collapsible : false,
4672 //buttonAlign:"center",
4673 closeClick : function(){
4674 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4677 handleButton("cancel");
4682 dlg.on("hide", handleHide);
4684 //dlg.addKeyListener(27, handleEsc);
4686 this.buttons = buttons;
4687 var bt = this.buttonText;
4688 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4689 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4690 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4691 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4693 bodyEl = dlg.bodyEl.createChild({
4695 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4696 '<textarea class="roo-mb-textarea"></textarea>' +
4697 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4699 msgEl = bodyEl.dom.firstChild;
4700 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4701 textboxEl.enableDisplayMode();
4702 textboxEl.addKeyListener([10,13], function(){
4703 if(dlg.isVisible() && opt && opt.buttons){
4706 }else if(opt.buttons.yes){
4707 handleButton("yes");
4711 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4712 textareaEl.enableDisplayMode();
4713 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4714 progressEl.enableDisplayMode();
4716 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4717 var pf = progressEl.dom.firstChild;
4719 pp = Roo.get(pf.firstChild);
4720 pp.setHeight(pf.offsetHeight);
4728 * Updates the message box body text
4729 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4730 * the XHTML-compliant non-breaking space character '&#160;')
4731 * @return {Roo.MessageBox} This message box
4733 updateText : function(text)
4735 if(!dlg.isVisible() && !opt.width){
4736 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4737 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4739 msgEl.innerHTML = text || ' ';
4741 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4742 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4744 Math.min(opt.width || cw , this.maxWidth),
4745 Math.max(opt.minWidth || this.minWidth, bwidth)
4748 activeTextEl.setWidth(w);
4750 if(dlg.isVisible()){
4751 dlg.fixedcenter = false;
4753 // to big, make it scroll. = But as usual stupid IE does not support
4756 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4757 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4758 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4760 bodyEl.dom.style.height = '';
4761 bodyEl.dom.style.overflowY = '';
4764 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4766 bodyEl.dom.style.overflowX = '';
4769 dlg.setContentSize(w, bodyEl.getHeight());
4770 if(dlg.isVisible()){
4771 dlg.fixedcenter = true;
4777 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4778 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4779 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4780 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4781 * @return {Roo.MessageBox} This message box
4783 updateProgress : function(value, text){
4785 this.updateText(text);
4788 if (pp) { // weird bug on my firefox - for some reason this is not defined
4789 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4790 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4796 * Returns true if the message box is currently displayed
4797 * @return {Boolean} True if the message box is visible, else false
4799 isVisible : function(){
4800 return dlg && dlg.isVisible();
4804 * Hides the message box if it is displayed
4807 if(this.isVisible()){
4813 * Displays a new message box, or reinitializes an existing message box, based on the config options
4814 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4815 * The following config object properties are supported:
4817 Property Type Description
4818 ---------- --------------- ------------------------------------------------------------------------------------
4819 animEl String/Element An id or Element from which the message box should animate as it opens and
4820 closes (defaults to undefined)
4821 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4822 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4823 closable Boolean False to hide the top-right close button (defaults to true). Note that
4824 progress and wait dialogs will ignore this property and always hide the
4825 close button as they can only be closed programmatically.
4826 cls String A custom CSS class to apply to the message box element
4827 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4828 displayed (defaults to 75)
4829 fn Function A callback function to execute after closing the dialog. The arguments to the
4830 function will be btn (the name of the button that was clicked, if applicable,
4831 e.g. "ok"), and text (the value of the active text field, if applicable).
4832 Progress and wait dialogs will ignore this option since they do not respond to
4833 user actions and can only be closed programmatically, so any required function
4834 should be called by the same code after it closes the dialog.
4835 icon String A CSS class that provides a background image to be used as an icon for
4836 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4837 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4838 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4839 modal Boolean False to allow user interaction with the page while the message box is
4840 displayed (defaults to true)
4841 msg String A string that will replace the existing message box body text (defaults
4842 to the XHTML-compliant non-breaking space character ' ')
4843 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4844 progress Boolean True to display a progress bar (defaults to false)
4845 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4846 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4847 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4848 title String The title text
4849 value String The string value to set into the active textbox element if displayed
4850 wait Boolean True to display a progress bar (defaults to false)
4851 width Number The width of the dialog in pixels
4858 msg: 'Please enter your address:',
4860 buttons: Roo.MessageBox.OKCANCEL,
4863 animEl: 'addAddressBtn'
4866 * @param {Object} config Configuration options
4867 * @return {Roo.MessageBox} This message box
4869 show : function(options)
4872 // this causes nightmares if you show one dialog after another
4873 // especially on callbacks..
4875 if(this.isVisible()){
4878 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4879 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4880 Roo.log("New Dialog Message:" + options.msg )
4881 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4882 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4885 var d = this.getDialog();
4887 d.setTitle(opt.title || " ");
4888 d.closeEl.setDisplayed(opt.closable !== false);
4889 activeTextEl = textboxEl;
4890 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4895 textareaEl.setHeight(typeof opt.multiline == "number" ?
4896 opt.multiline : this.defaultTextHeight);
4897 activeTextEl = textareaEl;
4906 progressEl.setDisplayed(opt.progress === true);
4908 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4910 this.updateProgress(0);
4911 activeTextEl.dom.value = opt.value || "";
4913 dlg.setDefaultButton(activeTextEl);
4915 var bs = opt.buttons;
4919 }else if(bs && bs.yes){
4920 db = buttons["yes"];
4922 dlg.setDefaultButton(db);
4924 bwidth = updateButtons(opt.buttons);
4925 this.updateText(opt.msg);
4927 d.el.addClass(opt.cls);
4929 d.proxyDrag = opt.proxyDrag === true;
4930 d.modal = opt.modal !== false;
4931 d.mask = opt.modal !== false ? mask : false;
4933 // force it to the end of the z-index stack so it gets a cursor in FF
4934 document.body.appendChild(dlg.el.dom);
4935 d.animateTarget = null;
4936 d.show(options.animEl);
4942 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
4943 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4944 * and closing the message box when the process is complete.
4945 * @param {String} title The title bar text
4946 * @param {String} msg The message box body text
4947 * @return {Roo.MessageBox} This message box
4949 progress : function(title, msg){
4956 minWidth: this.minProgressWidth,
4963 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4964 * If a callback function is passed it will be called after the user clicks the button, and the
4965 * id of the button that was clicked will be passed as the only parameter to the callback
4966 * (could also be the top-right close button).
4967 * @param {String} title The title bar text
4968 * @param {String} msg The message box body text
4969 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4970 * @param {Object} scope (optional) The scope of the callback function
4971 * @return {Roo.MessageBox} This message box
4973 alert : function(title, msg, fn, scope)
4988 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
4989 * interaction while waiting for a long-running process to complete that does not have defined intervals.
4990 * You are responsible for closing the message box when the process is complete.
4991 * @param {String} msg The message box body text
4992 * @param {String} title (optional) The title bar text
4993 * @return {Roo.MessageBox} This message box
4995 wait : function(msg, title){
5006 waitTimer = Roo.TaskMgr.start({
5008 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5016 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5017 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5018 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5019 * @param {String} title The title bar text
5020 * @param {String} msg The message box body text
5021 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5022 * @param {Object} scope (optional) The scope of the callback function
5023 * @return {Roo.MessageBox} This message box
5025 confirm : function(title, msg, fn, scope){
5029 buttons: this.YESNO,
5038 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5039 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5040 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5041 * (could also be the top-right close button) and the text that was entered will be passed as the two
5042 * parameters to the callback.
5043 * @param {String} title The title bar text
5044 * @param {String} msg The message box body text
5045 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5046 * @param {Object} scope (optional) The scope of the callback function
5047 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5048 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5049 * @return {Roo.MessageBox} This message box
5051 prompt : function(title, msg, fn, scope, multiline){
5055 buttons: this.OKCANCEL,
5060 multiline: multiline,
5067 * Button config that displays a single OK button
5072 * Button config that displays Yes and No buttons
5075 YESNO : {yes:true, no:true},
5077 * Button config that displays OK and Cancel buttons
5080 OKCANCEL : {ok:true, cancel:true},
5082 * Button config that displays Yes, No and Cancel buttons
5085 YESNOCANCEL : {yes:true, no:true, cancel:true},
5088 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5091 defaultTextHeight : 75,
5093 * The maximum width in pixels of the message box (defaults to 600)
5098 * The minimum width in pixels of the message box (defaults to 100)
5103 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5104 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5107 minProgressWidth : 250,
5109 * An object containing the default button text strings that can be overriden for localized language support.
5110 * Supported properties are: ok, cancel, yes and no.
5111 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5124 * Shorthand for {@link Roo.MessageBox}
5126 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5127 Roo.Msg = Roo.Msg || Roo.MessageBox;
5136 * @class Roo.bootstrap.Navbar
5137 * @extends Roo.bootstrap.Component
5138 * Bootstrap Navbar class
5141 * Create a new Navbar
5142 * @param {Object} config The config object
5146 Roo.bootstrap.Navbar = function(config){
5147 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5151 * @event beforetoggle
5152 * Fire before toggle the menu
5153 * @param {Roo.EventObject} e
5155 "beforetoggle" : true
5159 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5168 getAutoCreate : function(){
5171 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5175 initEvents :function ()
5177 //Roo.log(this.el.select('.navbar-toggle',true));
5178 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5185 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5187 var size = this.el.getSize();
5188 this.maskEl.setSize(size.width, size.height);
5189 this.maskEl.enableDisplayMode("block");
5198 getChildContainer : function()
5200 if (this.el && this.el.select('.collapse').getCount()) {
5201 return this.el.select('.collapse',true).first();
5216 onToggle : function()
5219 if(this.fireEvent('beforetoggle', this) === false){
5222 var ce = this.el.select('.navbar-collapse',true).first();
5224 if (!ce.hasClass('show')) {
5234 * Expand the navbar pulldown
5236 expand : function ()
5239 var ce = this.el.select('.navbar-collapse',true).first();
5240 if (ce.hasClass('collapsing')) {
5243 ce.dom.style.height = '';
5245 ce.addClass('in'); // old...
5246 ce.removeClass('collapse');
5247 ce.addClass('show');
5248 var h = ce.getHeight();
5250 ce.removeClass('show');
5251 // at this point we should be able to see it..
5252 ce.addClass('collapsing');
5254 ce.setHeight(0); // resize it ...
5255 ce.on('transitionend', function() {
5256 //Roo.log('done transition');
5257 ce.removeClass('collapsing');
5258 ce.addClass('show');
5259 ce.removeClass('collapse');
5261 ce.dom.style.height = '';
5262 }, this, { single: true} );
5264 ce.dom.scrollTop = 0;
5267 * Collapse the navbar pulldown
5269 collapse : function()
5271 var ce = this.el.select('.navbar-collapse',true).first();
5273 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5274 // it's collapsed or collapsing..
5277 ce.removeClass('in'); // old...
5278 ce.setHeight(ce.getHeight());
5279 ce.removeClass('show');
5280 ce.addClass('collapsing');
5282 ce.on('transitionend', function() {
5283 ce.dom.style.height = '';
5284 ce.removeClass('collapsing');
5285 ce.addClass('collapse');
5286 }, this, { single: true} );
5306 * @class Roo.bootstrap.NavSimplebar
5307 * @extends Roo.bootstrap.Navbar
5308 * Bootstrap Sidebar class
5310 * @cfg {Boolean} inverse is inverted color
5312 * @cfg {String} type (nav | pills | tabs)
5313 * @cfg {Boolean} arrangement stacked | justified
5314 * @cfg {String} align (left | right) alignment
5316 * @cfg {Boolean} main (true|false) main nav bar? default false
5317 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5319 * @cfg {String} tag (header|footer|nav|div) default is nav
5321 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5325 * Create a new Sidebar
5326 * @param {Object} config The config object
5330 Roo.bootstrap.NavSimplebar = function(config){
5331 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5334 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5350 getAutoCreate : function(){
5354 tag : this.tag || 'div',
5355 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5357 if (['light','white'].indexOf(this.weight) > -1) {
5358 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5360 cfg.cls += ' bg-' + this.weight;
5363 cfg.cls += ' navbar-inverse';
5367 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5369 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5378 cls: 'nav nav-' + this.xtype,
5384 this.type = this.type || 'nav';
5385 if (['tabs','pills'].indexOf(this.type) != -1) {
5386 cfg.cn[0].cls += ' nav-' + this.type
5390 if (this.type!=='nav') {
5391 Roo.log('nav type must be nav/tabs/pills')
5393 cfg.cn[0].cls += ' navbar-nav'
5399 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5400 cfg.cn[0].cls += ' nav-' + this.arrangement;
5404 if (this.align === 'right') {
5405 cfg.cn[0].cls += ' navbar-right';
5430 * navbar-expand-md fixed-top
5434 * @class Roo.bootstrap.NavHeaderbar
5435 * @extends Roo.bootstrap.NavSimplebar
5436 * Bootstrap Sidebar class
5438 * @cfg {String} brand what is brand
5439 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5440 * @cfg {String} brand_href href of the brand
5441 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5442 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5443 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5444 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5447 * Create a new Sidebar
5448 * @param {Object} config The config object
5452 Roo.bootstrap.NavHeaderbar = function(config){
5453 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5457 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5464 desktopCenter : false,
5467 getAutoCreate : function(){
5470 tag: this.nav || 'nav',
5471 cls: 'navbar navbar-expand-md',
5477 if (this.desktopCenter) {
5478 cn.push({cls : 'container', cn : []});
5486 cls: 'navbar-toggle navbar-toggler',
5487 'data-toggle': 'collapse',
5492 html: 'Toggle navigation'
5496 cls: 'icon-bar navbar-toggler-icon'
5509 cn.push( Roo.bootstrap.version == 4 ? btn : {
5511 cls: 'navbar-header',
5520 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5524 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5526 if (['light','white'].indexOf(this.weight) > -1) {
5527 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5529 cfg.cls += ' bg-' + this.weight;
5532 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5533 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5535 // tag can override this..
5537 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5540 if (this.brand !== '') {
5541 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5542 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5544 href: this.brand_href ? this.brand_href : '#',
5545 cls: 'navbar-brand',
5553 cfg.cls += ' main-nav';
5561 getHeaderChildContainer : function()
5563 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5564 return this.el.select('.navbar-header',true).first();
5567 return this.getChildContainer();
5570 getChildContainer : function()
5573 return this.el.select('.roo-navbar-collapse',true).first();
5578 initEvents : function()
5580 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5582 if (this.autohide) {
5587 Roo.get(document).on('scroll',function(e) {
5588 var ns = Roo.get(document).getScroll().top;
5589 var os = prevScroll;
5593 ft.removeClass('slideDown');
5594 ft.addClass('slideUp');
5597 ft.removeClass('slideUp');
5598 ft.addClass('slideDown');
5619 * @class Roo.bootstrap.NavSidebar
5620 * @extends Roo.bootstrap.Navbar
5621 * Bootstrap Sidebar class
5624 * Create a new Sidebar
5625 * @param {Object} config The config object
5629 Roo.bootstrap.NavSidebar = function(config){
5630 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5633 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5635 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5637 getAutoCreate : function(){
5642 cls: 'sidebar sidebar-nav'
5664 * @class Roo.bootstrap.NavGroup
5665 * @extends Roo.bootstrap.Component
5666 * Bootstrap NavGroup class
5667 * @cfg {String} align (left|right)
5668 * @cfg {Boolean} inverse
5669 * @cfg {String} type (nav|pills|tab) default nav
5670 * @cfg {String} navId - reference Id for navbar.
5674 * Create a new nav group
5675 * @param {Object} config The config object
5678 Roo.bootstrap.NavGroup = function(config){
5679 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5682 Roo.bootstrap.NavGroup.register(this);
5686 * Fires when the active item changes
5687 * @param {Roo.bootstrap.NavGroup} this
5688 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5689 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5696 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5707 getAutoCreate : function()
5709 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5715 if (Roo.bootstrap.version == 4) {
5716 if (['tabs','pills'].indexOf(this.type) != -1) {
5717 cfg.cls += ' nav-' + this.type;
5719 // trying to remove so header bar can right align top?
5720 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5721 // do not use on header bar...
5722 cfg.cls += ' navbar-nav';
5727 if (['tabs','pills'].indexOf(this.type) != -1) {
5728 cfg.cls += ' nav-' + this.type
5730 if (this.type !== 'nav') {
5731 Roo.log('nav type must be nav/tabs/pills')
5733 cfg.cls += ' navbar-nav'
5737 if (this.parent() && this.parent().sidebar) {
5740 cls: 'dashboard-menu sidebar-menu'
5746 if (this.form === true) {
5749 cls: 'navbar-form form-inline'
5751 //nav navbar-right ml-md-auto
5752 if (this.align === 'right') {
5753 cfg.cls += ' navbar-right ml-md-auto';
5755 cfg.cls += ' navbar-left';
5759 if (this.align === 'right') {
5760 cfg.cls += ' navbar-right ml-md-auto';
5762 cfg.cls += ' mr-auto';
5766 cfg.cls += ' navbar-inverse';
5774 * sets the active Navigation item
5775 * @param {Roo.bootstrap.NavItem} the new current navitem
5777 setActiveItem : function(item)
5780 Roo.each(this.navItems, function(v){
5785 v.setActive(false, true);
5792 item.setActive(true, true);
5793 this.fireEvent('changed', this, item, prev);
5798 * gets the active Navigation item
5799 * @return {Roo.bootstrap.NavItem} the current navitem
5801 getActive : function()
5805 Roo.each(this.navItems, function(v){
5816 indexOfNav : function()
5820 Roo.each(this.navItems, function(v,i){
5831 * adds a Navigation item
5832 * @param {Roo.bootstrap.NavItem} the navitem to add
5834 addItem : function(cfg)
5836 if (this.form && Roo.bootstrap.version == 4) {
5839 var cn = new Roo.bootstrap.NavItem(cfg);
5841 cn.parentId = this.id;
5842 cn.onRender(this.el, null);
5846 * register a Navigation item
5847 * @param {Roo.bootstrap.NavItem} the navitem to add
5849 register : function(item)
5851 this.navItems.push( item);
5852 item.navId = this.navId;
5857 * clear all the Navigation item
5860 clearAll : function()
5863 this.el.dom.innerHTML = '';
5866 getNavItem: function(tabId)
5869 Roo.each(this.navItems, function(e) {
5870 if (e.tabId == tabId) {
5880 setActiveNext : function()
5882 var i = this.indexOfNav(this.getActive());
5883 if (i > this.navItems.length) {
5886 this.setActiveItem(this.navItems[i+1]);
5888 setActivePrev : function()
5890 var i = this.indexOfNav(this.getActive());
5894 this.setActiveItem(this.navItems[i-1]);
5896 clearWasActive : function(except) {
5897 Roo.each(this.navItems, function(e) {
5898 if (e.tabId != except.tabId && e.was_active) {
5899 e.was_active = false;
5906 getWasActive : function ()
5909 Roo.each(this.navItems, function(e) {
5924 Roo.apply(Roo.bootstrap.NavGroup, {
5928 * register a Navigation Group
5929 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5931 register : function(navgrp)
5933 this.groups[navgrp.navId] = navgrp;
5937 * fetch a Navigation Group based on the navigation ID
5938 * @param {string} the navgroup to add
5939 * @returns {Roo.bootstrap.NavGroup} the navgroup
5941 get: function(navId) {
5942 if (typeof(this.groups[navId]) == 'undefined') {
5944 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5946 return this.groups[navId] ;
5961 * @class Roo.bootstrap.NavItem
5962 * @extends Roo.bootstrap.Component
5963 * Bootstrap Navbar.NavItem class
5964 * @cfg {String} href link to
5965 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5967 * @cfg {String} html content of button
5968 * @cfg {String} badge text inside badge
5969 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5970 * @cfg {String} glyphicon DEPRICATED - use fa
5971 * @cfg {String} icon DEPRICATED - use fa
5972 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5973 * @cfg {Boolean} active Is item active
5974 * @cfg {Boolean} disabled Is item disabled
5976 * @cfg {Boolean} preventDefault (true | false) default false
5977 * @cfg {String} tabId the tab that this item activates.
5978 * @cfg {String} tagtype (a|span) render as a href or span?
5979 * @cfg {Boolean} animateRef (true|false) link to element default false
5982 * Create a new Navbar Item
5983 * @param {Object} config The config object
5985 Roo.bootstrap.NavItem = function(config){
5986 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5991 * The raw click event for the entire grid.
5992 * @param {Roo.EventObject} e
5997 * Fires when the active item active state changes
5998 * @param {Roo.bootstrap.NavItem} this
5999 * @param {boolean} state the new state
6005 * Fires when scroll to element
6006 * @param {Roo.bootstrap.NavItem} this
6007 * @param {Object} options
6008 * @param {Roo.EventObject} e
6016 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6025 preventDefault : false,
6033 button_outline : false,
6037 getAutoCreate : function(){
6045 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6047 if (this.disabled) {
6048 cfg.cls += ' disabled';
6052 if (this.button_weight.length) {
6053 cfg.tag = this.href ? 'a' : 'button';
6054 cfg.html = this.html || '';
6055 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6057 cfg.href = this.href;
6060 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6063 // menu .. should add dropdown-menu class - so no need for carat..
6065 if (this.badge !== '') {
6067 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6072 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6076 href : this.href || "#",
6077 html: this.html || ''
6080 if (this.tagtype == 'a') {
6081 cfg.cn[0].cls = 'nav-link';
6084 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6087 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6089 if(this.glyphicon) {
6090 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6095 cfg.cn[0].html += " <span class='caret'></span>";
6099 if (this.badge !== '') {
6101 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6109 onRender : function(ct, position)
6111 // Roo.log("Call onRender: " + this.xtype);
6112 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6116 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6117 this.navLink = this.el.select('.nav-link',true).first();
6122 initEvents: function()
6124 if (typeof (this.menu) != 'undefined') {
6125 this.menu.parentType = this.xtype;
6126 this.menu.triggerEl = this.el;
6127 this.menu = this.addxtype(Roo.apply({}, this.menu));
6130 this.el.select('a',true).on('click', this.onClick, this);
6132 if(this.tagtype == 'span'){
6133 this.el.select('span',true).on('click', this.onClick, this);
6136 // at this point parent should be available..
6137 this.parent().register(this);
6140 onClick : function(e)
6142 if (e.getTarget('.dropdown-menu-item')) {
6143 // did you click on a menu itemm.... - then don't trigger onclick..
6148 this.preventDefault ||
6151 Roo.log("NavItem - prevent Default?");
6155 if (this.disabled) {
6159 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6160 if (tg && tg.transition) {
6161 Roo.log("waiting for the transitionend");
6167 //Roo.log("fire event clicked");
6168 if(this.fireEvent('click', this, e) === false){
6172 if(this.tagtype == 'span'){
6176 //Roo.log(this.href);
6177 var ael = this.el.select('a',true).first();
6180 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6181 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6182 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6183 return; // ignore... - it's a 'hash' to another page.
6185 Roo.log("NavItem - prevent Default?");
6187 this.scrollToElement(e);
6191 var p = this.parent();
6193 if (['tabs','pills'].indexOf(p.type)!==-1) {
6194 if (typeof(p.setActiveItem) !== 'undefined') {
6195 p.setActiveItem(this);
6199 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6200 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6201 // remove the collapsed menu expand...
6202 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6206 isActive: function () {
6209 setActive : function(state, fire, is_was_active)
6211 if (this.active && !state && this.navId) {
6212 this.was_active = true;
6213 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6215 nv.clearWasActive(this);
6219 this.active = state;
6222 this.el.removeClass('active');
6223 this.navLink ? this.navLink.removeClass('active') : false;
6224 } else if (!this.el.hasClass('active')) {
6226 this.el.addClass('active');
6227 if (Roo.bootstrap.version == 4 && this.navLink ) {
6228 this.navLink.addClass('active');
6233 this.fireEvent('changed', this, state);
6236 // show a panel if it's registered and related..
6238 if (!this.navId || !this.tabId || !state || is_was_active) {
6242 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6246 var pan = tg.getPanelByName(this.tabId);
6250 // if we can not flip to new panel - go back to old nav highlight..
6251 if (false == tg.showPanel(pan)) {
6252 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6254 var onav = nv.getWasActive();
6256 onav.setActive(true, false, true);
6265 // this should not be here...
6266 setDisabled : function(state)
6268 this.disabled = state;
6270 this.el.removeClass('disabled');
6271 } else if (!this.el.hasClass('disabled')) {
6272 this.el.addClass('disabled');
6278 * Fetch the element to display the tooltip on.
6279 * @return {Roo.Element} defaults to this.el
6281 tooltipEl : function()
6283 return this.el.select('' + this.tagtype + '', true).first();
6286 scrollToElement : function(e)
6288 var c = document.body;
6291 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6293 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6294 c = document.documentElement;
6297 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6303 var o = target.calcOffsetsTo(c);
6310 this.fireEvent('scrollto', this, options, e);
6312 Roo.get(c).scrollTo('top', options.value, true);
6325 * <span> icon </span>
6326 * <span> text </span>
6327 * <span>badge </span>
6331 * @class Roo.bootstrap.NavSidebarItem
6332 * @extends Roo.bootstrap.NavItem
6333 * Bootstrap Navbar.NavSidebarItem class
6334 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6335 * {Boolean} open is the menu open
6336 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6337 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6338 * {String} buttonSize (sm|md|lg)the extra classes for the button
6339 * {Boolean} showArrow show arrow next to the text (default true)
6341 * Create a new Navbar Button
6342 * @param {Object} config The config object
6344 Roo.bootstrap.NavSidebarItem = function(config){
6345 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6350 * The raw click event for the entire grid.
6351 * @param {Roo.EventObject} e
6356 * Fires when the active item active state changes
6357 * @param {Roo.bootstrap.NavSidebarItem} this
6358 * @param {boolean} state the new state
6366 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6368 badgeWeight : 'default',
6374 buttonWeight : 'default',
6380 getAutoCreate : function(){
6385 href : this.href || '#',
6391 if(this.buttonView){
6394 href : this.href || '#',
6395 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6408 cfg.cls += ' active';
6411 if (this.disabled) {
6412 cfg.cls += ' disabled';
6415 cfg.cls += ' open x-open';
6418 if (this.glyphicon || this.icon) {
6419 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6420 a.cn.push({ tag : 'i', cls : c }) ;
6423 if(!this.buttonView){
6426 html : this.html || ''
6433 if (this.badge !== '') {
6434 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6440 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6443 a.cls += ' dropdown-toggle treeview' ;
6449 initEvents : function()
6451 if (typeof (this.menu) != 'undefined') {
6452 this.menu.parentType = this.xtype;
6453 this.menu.triggerEl = this.el;
6454 this.menu = this.addxtype(Roo.apply({}, this.menu));
6457 this.el.on('click', this.onClick, this);
6459 if(this.badge !== ''){
6460 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6465 onClick : function(e)
6472 if(this.preventDefault){
6476 this.fireEvent('click', this, e);
6479 disable : function()
6481 this.setDisabled(true);
6486 this.setDisabled(false);
6489 setDisabled : function(state)
6491 if(this.disabled == state){
6495 this.disabled = state;
6498 this.el.addClass('disabled');
6502 this.el.removeClass('disabled');
6507 setActive : function(state)
6509 if(this.active == state){
6513 this.active = state;
6516 this.el.addClass('active');
6520 this.el.removeClass('active');
6525 isActive: function ()
6530 setBadge : function(str)
6536 this.badgeEl.dom.innerHTML = str;
6553 * @class Roo.bootstrap.Row
6554 * @extends Roo.bootstrap.Component
6555 * Bootstrap Row class (contains columns...)
6559 * @param {Object} config The config object
6562 Roo.bootstrap.Row = function(config){
6563 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6566 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6568 getAutoCreate : function(){
6587 * @class Roo.bootstrap.Pagination
6588 * @extends Roo.bootstrap.Component
6589 * Bootstrap Pagination class
6590 * @cfg {String} size xs | sm | md | lg
6591 * @cfg {Boolean} inverse false | true
6594 * Create a new Pagination
6595 * @param {Object} config The config object
6598 Roo.bootstrap.Pagination = function(config){
6599 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6602 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6608 getAutoCreate : function(){
6614 cfg.cls += ' inverse';
6620 cfg.cls += " " + this.cls;
6638 * @class Roo.bootstrap.PaginationItem
6639 * @extends Roo.bootstrap.Component
6640 * Bootstrap PaginationItem class
6641 * @cfg {String} html text
6642 * @cfg {String} href the link
6643 * @cfg {Boolean} preventDefault (true | false) default true
6644 * @cfg {Boolean} active (true | false) default false
6645 * @cfg {Boolean} disabled default false
6649 * Create a new PaginationItem
6650 * @param {Object} config The config object
6654 Roo.bootstrap.PaginationItem = function(config){
6655 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6660 * The raw click event for the entire grid.
6661 * @param {Roo.EventObject} e
6667 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6671 preventDefault: true,
6676 getAutoCreate : function(){
6682 href : this.href ? this.href : '#',
6683 html : this.html ? this.html : ''
6693 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6697 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6703 initEvents: function() {
6705 this.el.on('click', this.onClick, this);
6708 onClick : function(e)
6710 Roo.log('PaginationItem on click ');
6711 if(this.preventDefault){
6719 this.fireEvent('click', this, e);
6735 * @class Roo.bootstrap.Slider
6736 * @extends Roo.bootstrap.Component
6737 * Bootstrap Slider class
6740 * Create a new Slider
6741 * @param {Object} config The config object
6744 Roo.bootstrap.Slider = function(config){
6745 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6748 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6750 getAutoCreate : function(){
6754 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6758 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6770 * Ext JS Library 1.1.1
6771 * Copyright(c) 2006-2007, Ext JS, LLC.
6773 * Originally Released Under LGPL - original licence link has changed is not relivant.
6776 * <script type="text/javascript">
6781 * @class Roo.grid.ColumnModel
6782 * @extends Roo.util.Observable
6783 * This is the default implementation of a ColumnModel used by the Grid. It defines
6784 * the columns in the grid.
6787 var colModel = new Roo.grid.ColumnModel([
6788 {header: "Ticker", width: 60, sortable: true, locked: true},
6789 {header: "Company Name", width: 150, sortable: true},
6790 {header: "Market Cap.", width: 100, sortable: true},
6791 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6792 {header: "Employees", width: 100, sortable: true, resizable: false}
6797 * The config options listed for this class are options which may appear in each
6798 * individual column definition.
6799 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6801 * @param {Object} config An Array of column config objects. See this class's
6802 * config objects for details.
6804 Roo.grid.ColumnModel = function(config){
6806 * The config passed into the constructor
6808 this.config = config;
6811 // if no id, create one
6812 // if the column does not have a dataIndex mapping,
6813 // map it to the order it is in the config
6814 for(var i = 0, len = config.length; i < len; i++){
6816 if(typeof c.dataIndex == "undefined"){
6819 if(typeof c.renderer == "string"){
6820 c.renderer = Roo.util.Format[c.renderer];
6822 if(typeof c.id == "undefined"){
6825 if(c.editor && c.editor.xtype){
6826 c.editor = Roo.factory(c.editor, Roo.grid);
6828 if(c.editor && c.editor.isFormField){
6829 c.editor = new Roo.grid.GridEditor(c.editor);
6831 this.lookup[c.id] = c;
6835 * The width of columns which have no width specified (defaults to 100)
6838 this.defaultWidth = 100;
6841 * Default sortable of columns which have no sortable specified (defaults to false)
6844 this.defaultSortable = false;
6848 * @event widthchange
6849 * Fires when the width of a column changes.
6850 * @param {ColumnModel} this
6851 * @param {Number} columnIndex The column index
6852 * @param {Number} newWidth The new width
6854 "widthchange": true,
6856 * @event headerchange
6857 * Fires when the text of a header changes.
6858 * @param {ColumnModel} this
6859 * @param {Number} columnIndex The column index
6860 * @param {Number} newText The new header text
6862 "headerchange": true,
6864 * @event hiddenchange
6865 * Fires when a column is hidden or "unhidden".
6866 * @param {ColumnModel} this
6867 * @param {Number} columnIndex The column index
6868 * @param {Boolean} hidden true if hidden, false otherwise
6870 "hiddenchange": true,
6872 * @event columnmoved
6873 * Fires when a column is moved.
6874 * @param {ColumnModel} this
6875 * @param {Number} oldIndex
6876 * @param {Number} newIndex
6878 "columnmoved" : true,
6880 * @event columlockchange
6881 * Fires when a column's locked state is changed
6882 * @param {ColumnModel} this
6883 * @param {Number} colIndex
6884 * @param {Boolean} locked true if locked
6886 "columnlockchange" : true
6888 Roo.grid.ColumnModel.superclass.constructor.call(this);
6890 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6892 * @cfg {String} header The header text to display in the Grid view.
6895 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6896 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6897 * specified, the column's index is used as an index into the Record's data Array.
6900 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6901 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6904 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6905 * Defaults to the value of the {@link #defaultSortable} property.
6906 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6909 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6912 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6915 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6918 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6921 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6922 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6923 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6924 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6927 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6930 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6933 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6936 * @cfg {String} cursor (Optional)
6939 * @cfg {String} tooltip (Optional)
6942 * @cfg {Number} xs (Optional)
6945 * @cfg {Number} sm (Optional)
6948 * @cfg {Number} md (Optional)
6951 * @cfg {Number} lg (Optional)
6954 * Returns the id of the column at the specified index.
6955 * @param {Number} index The column index
6956 * @return {String} the id
6958 getColumnId : function(index){
6959 return this.config[index].id;
6963 * Returns the column for a specified id.
6964 * @param {String} id The column id
6965 * @return {Object} the column
6967 getColumnById : function(id){
6968 return this.lookup[id];
6973 * Returns the column for a specified dataIndex.
6974 * @param {String} dataIndex The column dataIndex
6975 * @return {Object|Boolean} the column or false if not found
6977 getColumnByDataIndex: function(dataIndex){
6978 var index = this.findColumnIndex(dataIndex);
6979 return index > -1 ? this.config[index] : false;
6983 * Returns the index for a specified column id.
6984 * @param {String} id The column id
6985 * @return {Number} the index, or -1 if not found
6987 getIndexById : function(id){
6988 for(var i = 0, len = this.config.length; i < len; i++){
6989 if(this.config[i].id == id){
6997 * Returns the index for a specified column dataIndex.
6998 * @param {String} dataIndex The column dataIndex
6999 * @return {Number} the index, or -1 if not found
7002 findColumnIndex : function(dataIndex){
7003 for(var i = 0, len = this.config.length; i < len; i++){
7004 if(this.config[i].dataIndex == dataIndex){
7012 moveColumn : function(oldIndex, newIndex){
7013 var c = this.config[oldIndex];
7014 this.config.splice(oldIndex, 1);
7015 this.config.splice(newIndex, 0, c);
7016 this.dataMap = null;
7017 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7020 isLocked : function(colIndex){
7021 return this.config[colIndex].locked === true;
7024 setLocked : function(colIndex, value, suppressEvent){
7025 if(this.isLocked(colIndex) == value){
7028 this.config[colIndex].locked = value;
7030 this.fireEvent("columnlockchange", this, colIndex, value);
7034 getTotalLockedWidth : function(){
7036 for(var i = 0; i < this.config.length; i++){
7037 if(this.isLocked(i) && !this.isHidden(i)){
7038 this.totalWidth += this.getColumnWidth(i);
7044 getLockedCount : function(){
7045 for(var i = 0, len = this.config.length; i < len; i++){
7046 if(!this.isLocked(i)){
7051 return this.config.length;
7055 * Returns the number of columns.
7058 getColumnCount : function(visibleOnly){
7059 if(visibleOnly === true){
7061 for(var i = 0, len = this.config.length; i < len; i++){
7062 if(!this.isHidden(i)){
7068 return this.config.length;
7072 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7073 * @param {Function} fn
7074 * @param {Object} scope (optional)
7075 * @return {Array} result
7077 getColumnsBy : function(fn, scope){
7079 for(var i = 0, len = this.config.length; i < len; i++){
7080 var c = this.config[i];
7081 if(fn.call(scope||this, c, i) === true){
7089 * Returns true if the specified column is sortable.
7090 * @param {Number} col The column index
7093 isSortable : function(col){
7094 if(typeof this.config[col].sortable == "undefined"){
7095 return this.defaultSortable;
7097 return this.config[col].sortable;
7101 * Returns the rendering (formatting) function defined for the column.
7102 * @param {Number} col The column index.
7103 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7105 getRenderer : function(col){
7106 if(!this.config[col].renderer){
7107 return Roo.grid.ColumnModel.defaultRenderer;
7109 return this.config[col].renderer;
7113 * Sets the rendering (formatting) function for a column.
7114 * @param {Number} col The column index
7115 * @param {Function} fn The function to use to process the cell's raw data
7116 * to return HTML markup for the grid view. The render function is called with
7117 * the following parameters:<ul>
7118 * <li>Data value.</li>
7119 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7120 * <li>css A CSS style string to apply to the table cell.</li>
7121 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7122 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7123 * <li>Row index</li>
7124 * <li>Column index</li>
7125 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7127 setRenderer : function(col, fn){
7128 this.config[col].renderer = fn;
7132 * Returns the width for the specified column.
7133 * @param {Number} col The column index
7136 getColumnWidth : function(col){
7137 return this.config[col].width * 1 || this.defaultWidth;
7141 * Sets the width for a column.
7142 * @param {Number} col The column index
7143 * @param {Number} width The new width
7145 setColumnWidth : function(col, width, suppressEvent){
7146 this.config[col].width = width;
7147 this.totalWidth = null;
7149 this.fireEvent("widthchange", this, col, width);
7154 * Returns the total width of all columns.
7155 * @param {Boolean} includeHidden True to include hidden column widths
7158 getTotalWidth : function(includeHidden){
7159 if(!this.totalWidth){
7160 this.totalWidth = 0;
7161 for(var i = 0, len = this.config.length; i < len; i++){
7162 if(includeHidden || !this.isHidden(i)){
7163 this.totalWidth += this.getColumnWidth(i);
7167 return this.totalWidth;
7171 * Returns the header for the specified column.
7172 * @param {Number} col The column index
7175 getColumnHeader : function(col){
7176 return this.config[col].header;
7180 * Sets the header for a column.
7181 * @param {Number} col The column index
7182 * @param {String} header The new header
7184 setColumnHeader : function(col, header){
7185 this.config[col].header = header;
7186 this.fireEvent("headerchange", this, col, header);
7190 * Returns the tooltip for the specified column.
7191 * @param {Number} col The column index
7194 getColumnTooltip : function(col){
7195 return this.config[col].tooltip;
7198 * Sets the tooltip for a column.
7199 * @param {Number} col The column index
7200 * @param {String} tooltip The new tooltip
7202 setColumnTooltip : function(col, tooltip){
7203 this.config[col].tooltip = tooltip;
7207 * Returns the dataIndex for the specified column.
7208 * @param {Number} col The column index
7211 getDataIndex : function(col){
7212 return this.config[col].dataIndex;
7216 * Sets the dataIndex for a column.
7217 * @param {Number} col The column index
7218 * @param {Number} dataIndex The new dataIndex
7220 setDataIndex : function(col, dataIndex){
7221 this.config[col].dataIndex = dataIndex;
7227 * Returns true if the cell is editable.
7228 * @param {Number} colIndex The column index
7229 * @param {Number} rowIndex The row index - this is nto actually used..?
7232 isCellEditable : function(colIndex, rowIndex){
7233 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7237 * Returns the editor defined for the cell/column.
7238 * return false or null to disable editing.
7239 * @param {Number} colIndex The column index
7240 * @param {Number} rowIndex The row index
7243 getCellEditor : function(colIndex, rowIndex){
7244 return this.config[colIndex].editor;
7248 * Sets if a column is editable.
7249 * @param {Number} col The column index
7250 * @param {Boolean} editable True if the column is editable
7252 setEditable : function(col, editable){
7253 this.config[col].editable = editable;
7258 * Returns true if the column is hidden.
7259 * @param {Number} colIndex The column index
7262 isHidden : function(colIndex){
7263 return this.config[colIndex].hidden;
7268 * Returns true if the column width cannot be changed
7270 isFixed : function(colIndex){
7271 return this.config[colIndex].fixed;
7275 * Returns true if the column can be resized
7278 isResizable : function(colIndex){
7279 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7282 * Sets if a column is hidden.
7283 * @param {Number} colIndex The column index
7284 * @param {Boolean} hidden True if the column is hidden
7286 setHidden : function(colIndex, hidden){
7287 this.config[colIndex].hidden = hidden;
7288 this.totalWidth = null;
7289 this.fireEvent("hiddenchange", this, colIndex, hidden);
7293 * Sets the editor for a column.
7294 * @param {Number} col The column index
7295 * @param {Object} editor The editor object
7297 setEditor : function(col, editor){
7298 this.config[col].editor = editor;
7302 Roo.grid.ColumnModel.defaultRenderer = function(value)
7304 if(typeof value == "object") {
7307 if(typeof value == "string" && value.length < 1){
7311 return String.format("{0}", value);
7314 // Alias for backwards compatibility
7315 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7318 * Ext JS Library 1.1.1
7319 * Copyright(c) 2006-2007, Ext JS, LLC.
7321 * Originally Released Under LGPL - original licence link has changed is not relivant.
7324 * <script type="text/javascript">
7328 * @class Roo.LoadMask
7329 * A simple utility class for generically masking elements while loading data. If the element being masked has
7330 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7331 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7332 * element's UpdateManager load indicator and will be destroyed after the initial load.
7334 * Create a new LoadMask
7335 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7336 * @param {Object} config The config object
7338 Roo.LoadMask = function(el, config){
7339 this.el = Roo.get(el);
7340 Roo.apply(this, config);
7342 this.store.on('beforeload', this.onBeforeLoad, this);
7343 this.store.on('load', this.onLoad, this);
7344 this.store.on('loadexception', this.onLoadException, this);
7345 this.removeMask = false;
7347 var um = this.el.getUpdateManager();
7348 um.showLoadIndicator = false; // disable the default indicator
7349 um.on('beforeupdate', this.onBeforeLoad, this);
7350 um.on('update', this.onLoad, this);
7351 um.on('failure', this.onLoad, this);
7352 this.removeMask = true;
7356 Roo.LoadMask.prototype = {
7358 * @cfg {Boolean} removeMask
7359 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7360 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7364 * The text to display in a centered loading message box (defaults to 'Loading...')
7368 * @cfg {String} msgCls
7369 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7371 msgCls : 'x-mask-loading',
7374 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7380 * Disables the mask to prevent it from being displayed
7382 disable : function(){
7383 this.disabled = true;
7387 * Enables the mask so that it can be displayed
7389 enable : function(){
7390 this.disabled = false;
7393 onLoadException : function()
7397 if (typeof(arguments[3]) != 'undefined') {
7398 Roo.MessageBox.alert("Error loading",arguments[3]);
7402 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7403 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7410 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7415 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7419 onBeforeLoad : function(){
7421 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7426 destroy : function(){
7428 this.store.un('beforeload', this.onBeforeLoad, this);
7429 this.store.un('load', this.onLoad, this);
7430 this.store.un('loadexception', this.onLoadException, this);
7432 var um = this.el.getUpdateManager();
7433 um.un('beforeupdate', this.onBeforeLoad, this);
7434 um.un('update', this.onLoad, this);
7435 um.un('failure', this.onLoad, this);
7446 * @class Roo.bootstrap.Table
7447 * @extends Roo.bootstrap.Component
7448 * Bootstrap Table class
7449 * @cfg {String} cls table class
7450 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7451 * @cfg {String} bgcolor Specifies the background color for a table
7452 * @cfg {Number} border Specifies whether the table cells should have borders or not
7453 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7454 * @cfg {Number} cellspacing Specifies the space between cells
7455 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7456 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7457 * @cfg {String} sortable Specifies that the table should be sortable
7458 * @cfg {String} summary Specifies a summary of the content of a table
7459 * @cfg {Number} width Specifies the width of a table
7460 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7462 * @cfg {boolean} striped Should the rows be alternative striped
7463 * @cfg {boolean} bordered Add borders to the table
7464 * @cfg {boolean} hover Add hover highlighting
7465 * @cfg {boolean} condensed Format condensed
7466 * @cfg {boolean} responsive Format condensed
7467 * @cfg {Boolean} loadMask (true|false) default false
7468 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7469 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7470 * @cfg {Boolean} rowSelection (true|false) default false
7471 * @cfg {Boolean} cellSelection (true|false) default false
7472 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7473 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7474 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7475 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7479 * Create a new Table
7480 * @param {Object} config The config object
7483 Roo.bootstrap.Table = function(config){
7484 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7489 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7490 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7491 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7492 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7494 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7496 this.sm.grid = this;
7497 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7498 this.sm = this.selModel;
7499 this.sm.xmodule = this.xmodule || false;
7502 if (this.cm && typeof(this.cm.config) == 'undefined') {
7503 this.colModel = new Roo.grid.ColumnModel(this.cm);
7504 this.cm = this.colModel;
7505 this.cm.xmodule = this.xmodule || false;
7508 this.store= Roo.factory(this.store, Roo.data);
7509 this.ds = this.store;
7510 this.ds.xmodule = this.xmodule || false;
7513 if (this.footer && this.store) {
7514 this.footer.dataSource = this.ds;
7515 this.footer = Roo.factory(this.footer);
7522 * Fires when a cell is clicked
7523 * @param {Roo.bootstrap.Table} this
7524 * @param {Roo.Element} el
7525 * @param {Number} rowIndex
7526 * @param {Number} columnIndex
7527 * @param {Roo.EventObject} e
7531 * @event celldblclick
7532 * Fires when a cell is double clicked
7533 * @param {Roo.bootstrap.Table} this
7534 * @param {Roo.Element} el
7535 * @param {Number} rowIndex
7536 * @param {Number} columnIndex
7537 * @param {Roo.EventObject} e
7539 "celldblclick" : true,
7542 * Fires when a row is clicked
7543 * @param {Roo.bootstrap.Table} this
7544 * @param {Roo.Element} el
7545 * @param {Number} rowIndex
7546 * @param {Roo.EventObject} e
7550 * @event rowdblclick
7551 * Fires when a row is double clicked
7552 * @param {Roo.bootstrap.Table} this
7553 * @param {Roo.Element} el
7554 * @param {Number} rowIndex
7555 * @param {Roo.EventObject} e
7557 "rowdblclick" : true,
7560 * Fires when a mouseover occur
7561 * @param {Roo.bootstrap.Table} this
7562 * @param {Roo.Element} el
7563 * @param {Number} rowIndex
7564 * @param {Number} columnIndex
7565 * @param {Roo.EventObject} e
7570 * Fires when a mouseout occur
7571 * @param {Roo.bootstrap.Table} this
7572 * @param {Roo.Element} el
7573 * @param {Number} rowIndex
7574 * @param {Number} columnIndex
7575 * @param {Roo.EventObject} e
7580 * Fires when a row is rendered, so you can change add a style to it.
7581 * @param {Roo.bootstrap.Table} this
7582 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7586 * @event rowsrendered
7587 * Fires when all the rows have been rendered
7588 * @param {Roo.bootstrap.Table} this
7590 'rowsrendered' : true,
7592 * @event contextmenu
7593 * The raw contextmenu event for the entire grid.
7594 * @param {Roo.EventObject} e
7596 "contextmenu" : true,
7598 * @event rowcontextmenu
7599 * Fires when a row is right clicked
7600 * @param {Roo.bootstrap.Table} this
7601 * @param {Number} rowIndex
7602 * @param {Roo.EventObject} e
7604 "rowcontextmenu" : true,
7606 * @event cellcontextmenu
7607 * Fires when a cell is right clicked
7608 * @param {Roo.bootstrap.Table} this
7609 * @param {Number} rowIndex
7610 * @param {Number} cellIndex
7611 * @param {Roo.EventObject} e
7613 "cellcontextmenu" : true,
7615 * @event headercontextmenu
7616 * Fires when a header is right clicked
7617 * @param {Roo.bootstrap.Table} this
7618 * @param {Number} columnIndex
7619 * @param {Roo.EventObject} e
7621 "headercontextmenu" : true
7625 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7651 rowSelection : false,
7652 cellSelection : false,
7655 // Roo.Element - the tbody
7657 // Roo.Element - thead element
7660 container: false, // used by gridpanel...
7666 auto_hide_footer : false,
7668 getAutoCreate : function()
7670 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7677 if (this.scrollBody) {
7678 cfg.cls += ' table-body-fixed';
7681 cfg.cls += ' table-striped';
7685 cfg.cls += ' table-hover';
7687 if (this.bordered) {
7688 cfg.cls += ' table-bordered';
7690 if (this.condensed) {
7691 cfg.cls += ' table-condensed';
7693 if (this.responsive) {
7694 cfg.cls += ' table-responsive';
7698 cfg.cls+= ' ' +this.cls;
7701 // this lot should be simplifed...
7714 ].forEach(function(k) {
7722 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7725 if(this.store || this.cm){
7726 if(this.headerShow){
7727 cfg.cn.push(this.renderHeader());
7730 cfg.cn.push(this.renderBody());
7732 if(this.footerShow){
7733 cfg.cn.push(this.renderFooter());
7735 // where does this come from?
7736 //cfg.cls+= ' TableGrid';
7739 return { cn : [ cfg ] };
7742 initEvents : function()
7744 if(!this.store || !this.cm){
7747 if (this.selModel) {
7748 this.selModel.initEvents();
7752 //Roo.log('initEvents with ds!!!!');
7754 this.mainBody = this.el.select('tbody', true).first();
7755 this.mainHead = this.el.select('thead', true).first();
7756 this.mainFoot = this.el.select('tfoot', true).first();
7762 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7763 e.on('click', _this.sort, _this);
7766 this.mainBody.on("click", this.onClick, this);
7767 this.mainBody.on("dblclick", this.onDblClick, this);
7769 // why is this done????? = it breaks dialogs??
7770 //this.parent().el.setStyle('position', 'relative');
7774 this.footer.parentId = this.id;
7775 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7778 this.el.select('tfoot tr td').first().addClass('hide');
7783 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7786 this.store.on('load', this.onLoad, this);
7787 this.store.on('beforeload', this.onBeforeLoad, this);
7788 this.store.on('update', this.onUpdate, this);
7789 this.store.on('add', this.onAdd, this);
7790 this.store.on("clear", this.clear, this);
7792 this.el.on("contextmenu", this.onContextMenu, this);
7794 this.mainBody.on('scroll', this.onBodyScroll, this);
7796 this.cm.on("headerchange", this.onHeaderChange, this);
7798 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7802 onContextMenu : function(e, t)
7804 this.processEvent("contextmenu", e);
7807 processEvent : function(name, e)
7809 if (name != 'touchstart' ) {
7810 this.fireEvent(name, e);
7813 var t = e.getTarget();
7815 var cell = Roo.get(t);
7821 if(cell.findParent('tfoot', false, true)){
7825 if(cell.findParent('thead', false, true)){
7827 if(e.getTarget().nodeName.toLowerCase() != 'th'){
7828 cell = Roo.get(t).findParent('th', false, true);
7830 Roo.log("failed to find th in thead?");
7831 Roo.log(e.getTarget());
7836 var cellIndex = cell.dom.cellIndex;
7838 var ename = name == 'touchstart' ? 'click' : name;
7839 this.fireEvent("header" + ename, this, cellIndex, e);
7844 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7845 cell = Roo.get(t).findParent('td', false, true);
7847 Roo.log("failed to find th in tbody?");
7848 Roo.log(e.getTarget());
7853 var row = cell.findParent('tr', false, true);
7854 var cellIndex = cell.dom.cellIndex;
7855 var rowIndex = row.dom.rowIndex - 1;
7859 this.fireEvent("row" + name, this, rowIndex, e);
7863 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7869 onMouseover : function(e, el)
7871 var cell = Roo.get(el);
7877 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7878 cell = cell.findParent('td', false, true);
7881 var row = cell.findParent('tr', false, true);
7882 var cellIndex = cell.dom.cellIndex;
7883 var rowIndex = row.dom.rowIndex - 1; // start from 0
7885 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7889 onMouseout : function(e, el)
7891 var cell = Roo.get(el);
7897 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7898 cell = cell.findParent('td', false, true);
7901 var row = cell.findParent('tr', false, true);
7902 var cellIndex = cell.dom.cellIndex;
7903 var rowIndex = row.dom.rowIndex - 1; // start from 0
7905 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7909 onClick : 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 // why??? - should these not be based on SelectionModel?
7935 if(this.cellSelection){
7936 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7939 if(this.rowSelection){
7940 this.fireEvent('rowclick', this, row, rowIndex, e);
7946 onDblClick : function(e,el)
7948 var cell = Roo.get(el);
7950 if(!cell || (!this.cellSelection && !this.rowSelection)){
7954 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7955 cell = cell.findParent('td', false, true);
7958 if(!cell || typeof(cell) == 'undefined'){
7962 var row = cell.findParent('tr', false, true);
7964 if(!row || typeof(row) == 'undefined'){
7968 var cellIndex = cell.dom.cellIndex;
7969 var rowIndex = this.getRowIndex(row);
7971 if(this.cellSelection){
7972 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7975 if(this.rowSelection){
7976 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7980 sort : function(e,el)
7982 var col = Roo.get(el);
7984 if(!col.hasClass('sortable')){
7988 var sort = col.attr('sort');
7991 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7995 this.store.sortInfo = {field : sort, direction : dir};
7998 Roo.log("calling footer first");
7999 this.footer.onClick('first');
8002 this.store.load({ params : { start : 0 } });
8006 renderHeader : function()
8014 this.totalWidth = 0;
8016 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8018 var config = cm.config[i];
8022 cls : 'x-hcol-' + i,
8024 html: cm.getColumnHeader(i)
8029 if(typeof(config.sortable) != 'undefined' && config.sortable){
8031 c.html = '<i class="glyphicon"></i>' + c.html;
8034 // could use BS4 hidden-..-down
8036 if(typeof(config.lgHeader) != 'undefined'){
8037 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8040 if(typeof(config.mdHeader) != 'undefined'){
8041 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8044 if(typeof(config.smHeader) != 'undefined'){
8045 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8048 if(typeof(config.xsHeader) != 'undefined'){
8049 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8056 if(typeof(config.tooltip) != 'undefined'){
8057 c.tooltip = config.tooltip;
8060 if(typeof(config.colspan) != 'undefined'){
8061 c.colspan = config.colspan;
8064 if(typeof(config.hidden) != 'undefined' && config.hidden){
8065 c.style += ' display:none;';
8068 if(typeof(config.dataIndex) != 'undefined'){
8069 c.sort = config.dataIndex;
8074 if(typeof(config.align) != 'undefined' && config.align.length){
8075 c.style += ' text-align:' + config.align + ';';
8078 if(typeof(config.width) != 'undefined'){
8079 c.style += ' width:' + config.width + 'px;';
8080 this.totalWidth += config.width;
8082 this.totalWidth += 100; // assume minimum of 100 per column?
8085 if(typeof(config.cls) != 'undefined'){
8086 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8089 ['xs','sm','md','lg'].map(function(size){
8091 if(typeof(config[size]) == 'undefined'){
8095 if (!config[size]) { // 0 = hidden
8096 // BS 4 '0' is treated as hide that column and below.
8097 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8101 c.cls += ' col-' + size + '-' + config[size] + (
8102 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8114 renderBody : function()
8124 colspan : this.cm.getColumnCount()
8134 renderFooter : function()
8144 colspan : this.cm.getColumnCount()
8158 // Roo.log('ds onload');
8163 var ds = this.store;
8165 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8166 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8167 if (_this.store.sortInfo) {
8169 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8170 e.select('i', true).addClass(['glyphicon-arrow-up']);
8173 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8174 e.select('i', true).addClass(['glyphicon-arrow-down']);
8179 var tbody = this.mainBody;
8181 if(ds.getCount() > 0){
8182 ds.data.each(function(d,rowIndex){
8183 var row = this.renderRow(cm, ds, rowIndex);
8185 tbody.createChild(row);
8189 if(row.cellObjects.length){
8190 Roo.each(row.cellObjects, function(r){
8191 _this.renderCellObject(r);
8198 var tfoot = this.el.select('tfoot', true).first();
8200 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8202 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8204 var total = this.ds.getTotalCount();
8206 if(this.footer.pageSize < total){
8207 this.mainFoot.show();
8211 Roo.each(this.el.select('tbody td', true).elements, function(e){
8212 e.on('mouseover', _this.onMouseover, _this);
8215 Roo.each(this.el.select('tbody td', true).elements, function(e){
8216 e.on('mouseout', _this.onMouseout, _this);
8218 this.fireEvent('rowsrendered', this);
8224 onUpdate : function(ds,record)
8226 this.refreshRow(record);
8230 onRemove : function(ds, record, index, isUpdate){
8231 if(isUpdate !== true){
8232 this.fireEvent("beforerowremoved", this, index, record);
8234 var bt = this.mainBody.dom;
8236 var rows = this.el.select('tbody > tr', true).elements;
8238 if(typeof(rows[index]) != 'undefined'){
8239 bt.removeChild(rows[index].dom);
8242 // if(bt.rows[index]){
8243 // bt.removeChild(bt.rows[index]);
8246 if(isUpdate !== true){
8247 //this.stripeRows(index);
8248 //this.syncRowHeights(index, index);
8250 this.fireEvent("rowremoved", this, index, record);
8254 onAdd : function(ds, records, rowIndex)
8256 //Roo.log('on Add called');
8257 // - note this does not handle multiple adding very well..
8258 var bt = this.mainBody.dom;
8259 for (var i =0 ; i < records.length;i++) {
8260 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8261 //Roo.log(records[i]);
8262 //Roo.log(this.store.getAt(rowIndex+i));
8263 this.insertRow(this.store, rowIndex + i, false);
8270 refreshRow : function(record){
8271 var ds = this.store, index;
8272 if(typeof record == 'number'){
8274 record = ds.getAt(index);
8276 index = ds.indexOf(record);
8278 return; // should not happen - but seems to
8281 this.insertRow(ds, index, true);
8283 this.onRemove(ds, record, index+1, true);
8285 //this.syncRowHeights(index, index);
8287 this.fireEvent("rowupdated", this, index, record);
8290 insertRow : function(dm, rowIndex, isUpdate){
8293 this.fireEvent("beforerowsinserted", this, rowIndex);
8295 //var s = this.getScrollState();
8296 var row = this.renderRow(this.cm, this.store, rowIndex);
8297 // insert before rowIndex..
8298 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8302 if(row.cellObjects.length){
8303 Roo.each(row.cellObjects, function(r){
8304 _this.renderCellObject(r);
8309 this.fireEvent("rowsinserted", this, rowIndex);
8310 //this.syncRowHeights(firstRow, lastRow);
8311 //this.stripeRows(firstRow);
8318 getRowDom : function(rowIndex)
8320 var rows = this.el.select('tbody > tr', true).elements;
8322 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8325 // returns the object tree for a tr..
8328 renderRow : function(cm, ds, rowIndex)
8330 var d = ds.getAt(rowIndex);
8334 cls : 'x-row-' + rowIndex,
8338 var cellObjects = [];
8340 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8341 var config = cm.config[i];
8343 var renderer = cm.getRenderer(i);
8347 if(typeof(renderer) !== 'undefined'){
8348 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8350 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8351 // and are rendered into the cells after the row is rendered - using the id for the element.
8353 if(typeof(value) === 'object'){
8363 rowIndex : rowIndex,
8368 this.fireEvent('rowclass', this, rowcfg);
8372 cls : rowcfg.rowClass + ' x-col-' + i,
8374 html: (typeof(value) === 'object') ? '' : value
8381 if(typeof(config.colspan) != 'undefined'){
8382 td.colspan = config.colspan;
8385 if(typeof(config.hidden) != 'undefined' && config.hidden){
8386 td.style += ' display:none;';
8389 if(typeof(config.align) != 'undefined' && config.align.length){
8390 td.style += ' text-align:' + config.align + ';';
8392 if(typeof(config.valign) != 'undefined' && config.valign.length){
8393 td.style += ' vertical-align:' + config.valign + ';';
8396 if(typeof(config.width) != 'undefined'){
8397 td.style += ' width:' + config.width + 'px;';
8400 if(typeof(config.cursor) != 'undefined'){
8401 td.style += ' cursor:' + config.cursor + ';';
8404 if(typeof(config.cls) != 'undefined'){
8405 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8408 ['xs','sm','md','lg'].map(function(size){
8410 if(typeof(config[size]) == 'undefined'){
8416 if (!config[size]) { // 0 = hidden
8417 // BS 4 '0' is treated as hide that column and below.
8418 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8422 td.cls += ' col-' + size + '-' + config[size] + (
8423 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8433 row.cellObjects = cellObjects;
8441 onBeforeLoad : function()
8450 this.el.select('tbody', true).first().dom.innerHTML = '';
8453 * Show or hide a row.
8454 * @param {Number} rowIndex to show or hide
8455 * @param {Boolean} state hide
8457 setRowVisibility : function(rowIndex, state)
8459 var bt = this.mainBody.dom;
8461 var rows = this.el.select('tbody > tr', true).elements;
8463 if(typeof(rows[rowIndex]) == 'undefined'){
8466 rows[rowIndex].dom.style.display = state ? '' : 'none';
8470 getSelectionModel : function(){
8472 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8474 return this.selModel;
8477 * Render the Roo.bootstrap object from renderder
8479 renderCellObject : function(r)
8483 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8485 var t = r.cfg.render(r.container);
8488 Roo.each(r.cfg.cn, function(c){
8490 container: t.getChildContainer(),
8493 _this.renderCellObject(child);
8498 getRowIndex : function(row)
8502 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8513 * Returns the grid's underlying element = used by panel.Grid
8514 * @return {Element} The element
8516 getGridEl : function(){
8520 * Forces a resize - used by panel.Grid
8521 * @return {Element} The element
8523 autoSize : function()
8525 //var ctr = Roo.get(this.container.dom.parentElement);
8526 var ctr = Roo.get(this.el.dom);
8528 var thd = this.getGridEl().select('thead',true).first();
8529 var tbd = this.getGridEl().select('tbody', true).first();
8530 var tfd = this.getGridEl().select('tfoot', true).first();
8532 var cw = ctr.getWidth();
8536 tbd.setWidth(ctr.getWidth());
8537 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8538 // this needs fixing for various usage - currently only hydra job advers I think..
8540 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8542 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8545 cw = Math.max(cw, this.totalWidth);
8546 this.getGridEl().select('tr',true).setWidth(cw);
8547 // resize 'expandable coloumn?
8549 return; // we doe not have a view in this design..
8552 onBodyScroll: function()
8554 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8556 this.mainHead.setStyle({
8557 'position' : 'relative',
8558 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8564 var scrollHeight = this.mainBody.dom.scrollHeight;
8566 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8568 var height = this.mainBody.getHeight();
8570 if(scrollHeight - height == scrollTop) {
8572 var total = this.ds.getTotalCount();
8574 if(this.footer.cursor + this.footer.pageSize < total){
8576 this.footer.ds.load({
8578 start : this.footer.cursor + this.footer.pageSize,
8579 limit : this.footer.pageSize
8589 onHeaderChange : function()
8591 var header = this.renderHeader();
8592 var table = this.el.select('table', true).first();
8594 this.mainHead.remove();
8595 this.mainHead = table.createChild(header, this.mainBody, false);
8598 onHiddenChange : function(colModel, colIndex, hidden)
8600 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8601 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8603 this.CSS.updateRule(thSelector, "display", "");
8604 this.CSS.updateRule(tdSelector, "display", "");
8607 this.CSS.updateRule(thSelector, "display", "none");
8608 this.CSS.updateRule(tdSelector, "display", "none");
8611 this.onHeaderChange();
8615 setColumnWidth: function(col_index, width)
8617 // width = "md-2 xs-2..."
8618 if(!this.colModel.config[col_index]) {
8622 var w = width.split(" ");
8624 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8626 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8629 for(var j = 0; j < w.length; j++) {
8635 var size_cls = w[j].split("-");
8637 if(!Number.isInteger(size_cls[1] * 1)) {
8641 if(!this.colModel.config[col_index][size_cls[0]]) {
8645 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8649 h_row[0].classList.replace(
8650 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8651 "col-"+size_cls[0]+"-"+size_cls[1]
8654 for(var i = 0; i < rows.length; i++) {
8656 var size_cls = w[j].split("-");
8658 if(!Number.isInteger(size_cls[1] * 1)) {
8662 if(!this.colModel.config[col_index][size_cls[0]]) {
8666 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8670 rows[i].classList.replace(
8671 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8672 "col-"+size_cls[0]+"-"+size_cls[1]
8676 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8691 * @class Roo.bootstrap.TableCell
8692 * @extends Roo.bootstrap.Component
8693 * Bootstrap TableCell class
8694 * @cfg {String} html cell contain text
8695 * @cfg {String} cls cell class
8696 * @cfg {String} tag cell tag (td|th) default td
8697 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8698 * @cfg {String} align Aligns the content in a cell
8699 * @cfg {String} axis Categorizes cells
8700 * @cfg {String} bgcolor Specifies the background color of a cell
8701 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8702 * @cfg {Number} colspan Specifies the number of columns a cell should span
8703 * @cfg {String} headers Specifies one or more header cells a cell is related to
8704 * @cfg {Number} height Sets the height of a cell
8705 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8706 * @cfg {Number} rowspan Sets the number of rows a cell should span
8707 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8708 * @cfg {String} valign Vertical aligns the content in a cell
8709 * @cfg {Number} width Specifies the width of a cell
8712 * Create a new TableCell
8713 * @param {Object} config The config object
8716 Roo.bootstrap.TableCell = function(config){
8717 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8720 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8740 getAutoCreate : function(){
8741 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8761 cfg.align=this.align
8767 cfg.bgcolor=this.bgcolor
8770 cfg.charoff=this.charoff
8773 cfg.colspan=this.colspan
8776 cfg.headers=this.headers
8779 cfg.height=this.height
8782 cfg.nowrap=this.nowrap
8785 cfg.rowspan=this.rowspan
8788 cfg.scope=this.scope
8791 cfg.valign=this.valign
8794 cfg.width=this.width
8813 * @class Roo.bootstrap.TableRow
8814 * @extends Roo.bootstrap.Component
8815 * Bootstrap TableRow class
8816 * @cfg {String} cls row class
8817 * @cfg {String} align Aligns the content in a table row
8818 * @cfg {String} bgcolor Specifies a background color for a table row
8819 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8820 * @cfg {String} valign Vertical aligns the content in a table row
8823 * Create a new TableRow
8824 * @param {Object} config The config object
8827 Roo.bootstrap.TableRow = function(config){
8828 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8831 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
8839 getAutoCreate : function(){
8840 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8850 cfg.align = this.align;
8853 cfg.bgcolor = this.bgcolor;
8856 cfg.charoff = this.charoff;
8859 cfg.valign = this.valign;
8877 * @class Roo.bootstrap.TableBody
8878 * @extends Roo.bootstrap.Component
8879 * Bootstrap TableBody class
8880 * @cfg {String} cls element class
8881 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8882 * @cfg {String} align Aligns the content inside the element
8883 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8884 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8887 * Create a new TableBody
8888 * @param {Object} config The config object
8891 Roo.bootstrap.TableBody = function(config){
8892 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8895 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8903 getAutoCreate : function(){
8904 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8918 cfg.align = this.align;
8921 cfg.charoff = this.charoff;
8924 cfg.valign = this.valign;
8931 // initEvents : function()
8938 // this.store = Roo.factory(this.store, Roo.data);
8939 // this.store.on('load', this.onLoad, this);
8941 // this.store.load();
8945 // onLoad: function ()
8947 // this.fireEvent('load', this);
8957 * Ext JS Library 1.1.1
8958 * Copyright(c) 2006-2007, Ext JS, LLC.
8960 * Originally Released Under LGPL - original licence link has changed is not relivant.
8963 * <script type="text/javascript">
8966 // as we use this in bootstrap.
8967 Roo.namespace('Roo.form');
8969 * @class Roo.form.Action
8970 * Internal Class used to handle form actions
8972 * @param {Roo.form.BasicForm} el The form element or its id
8973 * @param {Object} config Configuration options
8978 // define the action interface
8979 Roo.form.Action = function(form, options){
8981 this.options = options || {};
8984 * Client Validation Failed
8987 Roo.form.Action.CLIENT_INVALID = 'client';
8989 * Server Validation Failed
8992 Roo.form.Action.SERVER_INVALID = 'server';
8994 * Connect to Server Failed
8997 Roo.form.Action.CONNECT_FAILURE = 'connect';
8999 * Reading Data from Server Failed
9002 Roo.form.Action.LOAD_FAILURE = 'load';
9004 Roo.form.Action.prototype = {
9006 failureType : undefined,
9007 response : undefined,
9011 run : function(options){
9016 success : function(response){
9021 handleResponse : function(response){
9025 // default connection failure
9026 failure : function(response){
9028 this.response = response;
9029 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9030 this.form.afterAction(this, false);
9033 processResponse : function(response){
9034 this.response = response;
9035 if(!response.responseText){
9038 this.result = this.handleResponse(response);
9042 // utility functions used internally
9043 getUrl : function(appendParams){
9044 var url = this.options.url || this.form.url || this.form.el.dom.action;
9046 var p = this.getParams();
9048 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9054 getMethod : function(){
9055 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9058 getParams : function(){
9059 var bp = this.form.baseParams;
9060 var p = this.options.params;
9062 if(typeof p == "object"){
9063 p = Roo.urlEncode(Roo.applyIf(p, bp));
9064 }else if(typeof p == 'string' && bp){
9065 p += '&' + Roo.urlEncode(bp);
9068 p = Roo.urlEncode(bp);
9073 createCallback : function(){
9075 success: this.success,
9076 failure: this.failure,
9078 timeout: (this.form.timeout*1000),
9079 upload: this.form.fileUpload ? this.success : undefined
9084 Roo.form.Action.Submit = function(form, options){
9085 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9088 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9091 haveProgress : false,
9092 uploadComplete : false,
9094 // uploadProgress indicator.
9095 uploadProgress : function()
9097 if (!this.form.progressUrl) {
9101 if (!this.haveProgress) {
9102 Roo.MessageBox.progress("Uploading", "Uploading");
9104 if (this.uploadComplete) {
9105 Roo.MessageBox.hide();
9109 this.haveProgress = true;
9111 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9113 var c = new Roo.data.Connection();
9115 url : this.form.progressUrl,
9120 success : function(req){
9121 //console.log(data);
9125 rdata = Roo.decode(req.responseText)
9127 Roo.log("Invalid data from server..");
9131 if (!rdata || !rdata.success) {
9133 Roo.MessageBox.alert(Roo.encode(rdata));
9136 var data = rdata.data;
9138 if (this.uploadComplete) {
9139 Roo.MessageBox.hide();
9144 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9145 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9148 this.uploadProgress.defer(2000,this);
9151 failure: function(data) {
9152 Roo.log('progress url failed ');
9163 // run get Values on the form, so it syncs any secondary forms.
9164 this.form.getValues();
9166 var o = this.options;
9167 var method = this.getMethod();
9168 var isPost = method == 'POST';
9169 if(o.clientValidation === false || this.form.isValid()){
9171 if (this.form.progressUrl) {
9172 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9173 (new Date() * 1) + '' + Math.random());
9178 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9179 form:this.form.el.dom,
9180 url:this.getUrl(!isPost),
9182 params:isPost ? this.getParams() : null,
9183 isUpload: this.form.fileUpload,
9184 formData : this.form.formData
9187 this.uploadProgress();
9189 }else if (o.clientValidation !== false){ // client validation failed
9190 this.failureType = Roo.form.Action.CLIENT_INVALID;
9191 this.form.afterAction(this, false);
9195 success : function(response)
9197 this.uploadComplete= true;
9198 if (this.haveProgress) {
9199 Roo.MessageBox.hide();
9203 var result = this.processResponse(response);
9204 if(result === true || result.success){
9205 this.form.afterAction(this, true);
9209 this.form.markInvalid(result.errors);
9210 this.failureType = Roo.form.Action.SERVER_INVALID;
9212 this.form.afterAction(this, false);
9214 failure : function(response)
9216 this.uploadComplete= true;
9217 if (this.haveProgress) {
9218 Roo.MessageBox.hide();
9221 this.response = response;
9222 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9223 this.form.afterAction(this, false);
9226 handleResponse : function(response){
9227 if(this.form.errorReader){
9228 var rs = this.form.errorReader.read(response);
9231 for(var i = 0, len = rs.records.length; i < len; i++) {
9232 var r = rs.records[i];
9236 if(errors.length < 1){
9240 success : rs.success,
9246 ret = Roo.decode(response.responseText);
9250 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9260 Roo.form.Action.Load = function(form, options){
9261 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9262 this.reader = this.form.reader;
9265 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9270 Roo.Ajax.request(Roo.apply(
9271 this.createCallback(), {
9272 method:this.getMethod(),
9273 url:this.getUrl(false),
9274 params:this.getParams()
9278 success : function(response){
9280 var result = this.processResponse(response);
9281 if(result === true || !result.success || !result.data){
9282 this.failureType = Roo.form.Action.LOAD_FAILURE;
9283 this.form.afterAction(this, false);
9286 this.form.clearInvalid();
9287 this.form.setValues(result.data);
9288 this.form.afterAction(this, true);
9291 handleResponse : function(response){
9292 if(this.form.reader){
9293 var rs = this.form.reader.read(response);
9294 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9296 success : rs.success,
9300 return Roo.decode(response.responseText);
9304 Roo.form.Action.ACTION_TYPES = {
9305 'load' : Roo.form.Action.Load,
9306 'submit' : Roo.form.Action.Submit
9315 * @class Roo.bootstrap.Form
9316 * @extends Roo.bootstrap.Component
9317 * Bootstrap Form class
9318 * @cfg {String} method GET | POST (default POST)
9319 * @cfg {String} labelAlign top | left (default top)
9320 * @cfg {String} align left | right - for navbars
9321 * @cfg {Boolean} loadMask load mask when submit (default true)
9326 * @param {Object} config The config object
9330 Roo.bootstrap.Form = function(config){
9332 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9334 Roo.bootstrap.Form.popover.apply();
9338 * @event clientvalidation
9339 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9340 * @param {Form} this
9341 * @param {Boolean} valid true if the form has passed client-side validation
9343 clientvalidation: true,
9345 * @event beforeaction
9346 * Fires before any action is performed. Return false to cancel the action.
9347 * @param {Form} this
9348 * @param {Action} action The action to be performed
9352 * @event actionfailed
9353 * Fires when an action fails.
9354 * @param {Form} this
9355 * @param {Action} action The action that failed
9357 actionfailed : true,
9359 * @event actioncomplete
9360 * Fires when an action is completed.
9361 * @param {Form} this
9362 * @param {Action} action The action that completed
9364 actioncomplete : true
9368 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9371 * @cfg {String} method
9372 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9377 * The URL to use for form actions if one isn't supplied in the action options.
9380 * @cfg {Boolean} fileUpload
9381 * Set to true if this form is a file upload.
9385 * @cfg {Object} baseParams
9386 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9390 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9394 * @cfg {Sting} align (left|right) for navbar forms
9399 activeAction : null,
9402 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9403 * element by passing it or its id or mask the form itself by passing in true.
9406 waitMsgTarget : false,
9411 * @cfg {Boolean} errorMask (true|false) default false
9416 * @cfg {Number} maskOffset Default 100
9421 * @cfg {Boolean} maskBody
9425 getAutoCreate : function(){
9429 method : this.method || 'POST',
9430 id : this.id || Roo.id(),
9433 if (this.parent().xtype.match(/^Nav/)) {
9434 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9438 if (this.labelAlign == 'left' ) {
9439 cfg.cls += ' form-horizontal';
9445 initEvents : function()
9447 this.el.on('submit', this.onSubmit, this);
9448 // this was added as random key presses on the form where triggering form submit.
9449 this.el.on('keypress', function(e) {
9450 if (e.getCharCode() != 13) {
9453 // we might need to allow it for textareas.. and some other items.
9454 // check e.getTarget().
9456 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9460 Roo.log("keypress blocked");
9468 onSubmit : function(e){
9473 * Returns true if client-side validation on the form is successful.
9476 isValid : function(){
9477 var items = this.getItems();
9481 items.each(function(f){
9487 Roo.log('invalid field: ' + f.name);
9491 if(!target && f.el.isVisible(true)){
9497 if(this.errorMask && !valid){
9498 Roo.bootstrap.Form.popover.mask(this, target);
9505 * Returns true if any fields in this form have changed since their original load.
9508 isDirty : function(){
9510 var items = this.getItems();
9511 items.each(function(f){
9521 * Performs a predefined action (submit or load) or custom actions you define on this form.
9522 * @param {String} actionName The name of the action type
9523 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9524 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9525 * accept other config options):
9527 Property Type Description
9528 ---------------- --------------- ----------------------------------------------------------------------------------
9529 url String The url for the action (defaults to the form's url)
9530 method String The form method to use (defaults to the form's method, or POST if not defined)
9531 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9532 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9533 validate the form on the client (defaults to false)
9535 * @return {BasicForm} this
9537 doAction : function(action, options){
9538 if(typeof action == 'string'){
9539 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9541 if(this.fireEvent('beforeaction', this, action) !== false){
9542 this.beforeAction(action);
9543 action.run.defer(100, action);
9549 beforeAction : function(action){
9550 var o = action.options;
9555 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9557 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9560 // not really supported yet.. ??
9562 //if(this.waitMsgTarget === true){
9563 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9564 //}else if(this.waitMsgTarget){
9565 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9566 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9568 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9574 afterAction : function(action, success){
9575 this.activeAction = null;
9576 var o = action.options;
9581 Roo.get(document.body).unmask();
9587 //if(this.waitMsgTarget === true){
9588 // this.el.unmask();
9589 //}else if(this.waitMsgTarget){
9590 // this.waitMsgTarget.unmask();
9592 // Roo.MessageBox.updateProgress(1);
9593 // Roo.MessageBox.hide();
9600 Roo.callback(o.success, o.scope, [this, action]);
9601 this.fireEvent('actioncomplete', this, action);
9605 // failure condition..
9606 // we have a scenario where updates need confirming.
9607 // eg. if a locking scenario exists..
9608 // we look for { errors : { needs_confirm : true }} in the response.
9610 (typeof(action.result) != 'undefined') &&
9611 (typeof(action.result.errors) != 'undefined') &&
9612 (typeof(action.result.errors.needs_confirm) != 'undefined')
9615 Roo.log("not supported yet");
9618 Roo.MessageBox.confirm(
9619 "Change requires confirmation",
9620 action.result.errorMsg,
9625 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9635 Roo.callback(o.failure, o.scope, [this, action]);
9636 // show an error message if no failed handler is set..
9637 if (!this.hasListener('actionfailed')) {
9638 Roo.log("need to add dialog support");
9640 Roo.MessageBox.alert("Error",
9641 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9642 action.result.errorMsg :
9643 "Saving Failed, please check your entries or try again"
9648 this.fireEvent('actionfailed', this, action);
9653 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9654 * @param {String} id The value to search for
9657 findField : function(id){
9658 var items = this.getItems();
9659 var field = items.get(id);
9661 items.each(function(f){
9662 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9669 return field || null;
9672 * Mark fields in this form invalid in bulk.
9673 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9674 * @return {BasicForm} this
9676 markInvalid : function(errors){
9677 if(errors instanceof Array){
9678 for(var i = 0, len = errors.length; i < len; i++){
9679 var fieldError = errors[i];
9680 var f = this.findField(fieldError.id);
9682 f.markInvalid(fieldError.msg);
9688 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9689 field.markInvalid(errors[id]);
9693 //Roo.each(this.childForms || [], function (f) {
9694 // f.markInvalid(errors);
9701 * Set values for fields in this form in bulk.
9702 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9703 * @return {BasicForm} this
9705 setValues : function(values){
9706 if(values instanceof Array){ // array of objects
9707 for(var i = 0, len = values.length; i < len; i++){
9709 var f = this.findField(v.id);
9711 f.setValue(v.value);
9712 if(this.trackResetOnLoad){
9713 f.originalValue = f.getValue();
9717 }else{ // object hash
9720 if(typeof values[id] != 'function' && (field = this.findField(id))){
9722 if (field.setFromData &&
9724 field.displayField &&
9725 // combos' with local stores can
9726 // be queried via setValue()
9727 // to set their value..
9728 (field.store && !field.store.isLocal)
9732 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9733 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9734 field.setFromData(sd);
9736 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9738 field.setFromData(values);
9741 field.setValue(values[id]);
9745 if(this.trackResetOnLoad){
9746 field.originalValue = field.getValue();
9752 //Roo.each(this.childForms || [], function (f) {
9753 // f.setValues(values);
9760 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9761 * they are returned as an array.
9762 * @param {Boolean} asString
9765 getValues : function(asString){
9766 //if (this.childForms) {
9767 // copy values from the child forms
9768 // Roo.each(this.childForms, function (f) {
9769 // this.setValues(f.getValues());
9775 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9776 if(asString === true){
9779 return Roo.urlDecode(fs);
9783 * Returns the fields in this form as an object with key/value pairs.
9784 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9787 getFieldValues : function(with_hidden)
9789 var items = this.getItems();
9791 items.each(function(f){
9797 var v = f.getValue();
9799 if (f.inputType =='radio') {
9800 if (typeof(ret[f.getName()]) == 'undefined') {
9801 ret[f.getName()] = ''; // empty..
9804 if (!f.el.dom.checked) {
9812 if(f.xtype == 'MoneyField'){
9813 ret[f.currencyName] = f.getCurrency();
9816 // not sure if this supported any more..
9817 if ((typeof(v) == 'object') && f.getRawValue) {
9818 v = f.getRawValue() ; // dates..
9820 // combo boxes where name != hiddenName...
9821 if (f.name !== false && f.name != '' && f.name != f.getName()) {
9822 ret[f.name] = f.getRawValue();
9824 ret[f.getName()] = v;
9831 * Clears all invalid messages in this form.
9832 * @return {BasicForm} this
9834 clearInvalid : function(){
9835 var items = this.getItems();
9837 items.each(function(f){
9846 * @return {BasicForm} this
9849 var items = this.getItems();
9850 items.each(function(f){
9854 Roo.each(this.childForms || [], function (f) {
9862 getItems : function()
9864 var r=new Roo.util.MixedCollection(false, function(o){
9865 return o.id || (o.id = Roo.id());
9867 var iter = function(el) {
9874 Roo.each(el.items,function(e) {
9883 hideFields : function(items)
9885 Roo.each(items, function(i){
9887 var f = this.findField(i);
9898 showFields : function(items)
9900 Roo.each(items, function(i){
9902 var f = this.findField(i);
9915 Roo.apply(Roo.bootstrap.Form, {
9942 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9943 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9944 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9945 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9948 this.maskEl.top.enableDisplayMode("block");
9949 this.maskEl.left.enableDisplayMode("block");
9950 this.maskEl.bottom.enableDisplayMode("block");
9951 this.maskEl.right.enableDisplayMode("block");
9953 this.toolTip = new Roo.bootstrap.Tooltip({
9954 cls : 'roo-form-error-popover',
9956 'left' : ['r-l', [-2,0], 'right'],
9957 'right' : ['l-r', [2,0], 'left'],
9958 'bottom' : ['tl-bl', [0,2], 'top'],
9959 'top' : [ 'bl-tl', [0,-2], 'bottom']
9963 this.toolTip.render(Roo.get(document.body));
9965 this.toolTip.el.enableDisplayMode("block");
9967 Roo.get(document.body).on('click', function(){
9971 Roo.get(document.body).on('touchstart', function(){
9975 this.isApplied = true
9978 mask : function(form, target)
9982 this.target = target;
9984 if(!this.form.errorMask || !target.el){
9988 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9990 Roo.log(scrollable);
9992 var ot = this.target.el.calcOffsetsTo(scrollable);
9994 var scrollTo = ot[1] - this.form.maskOffset;
9996 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9998 scrollable.scrollTo('top', scrollTo);
10000 var box = this.target.el.getBox();
10002 var zIndex = Roo.bootstrap.Modal.zIndex++;
10005 this.maskEl.top.setStyle('position', 'absolute');
10006 this.maskEl.top.setStyle('z-index', zIndex);
10007 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10008 this.maskEl.top.setLeft(0);
10009 this.maskEl.top.setTop(0);
10010 this.maskEl.top.show();
10012 this.maskEl.left.setStyle('position', 'absolute');
10013 this.maskEl.left.setStyle('z-index', zIndex);
10014 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10015 this.maskEl.left.setLeft(0);
10016 this.maskEl.left.setTop(box.y - this.padding);
10017 this.maskEl.left.show();
10019 this.maskEl.bottom.setStyle('position', 'absolute');
10020 this.maskEl.bottom.setStyle('z-index', zIndex);
10021 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10022 this.maskEl.bottom.setLeft(0);
10023 this.maskEl.bottom.setTop(box.bottom + this.padding);
10024 this.maskEl.bottom.show();
10026 this.maskEl.right.setStyle('position', 'absolute');
10027 this.maskEl.right.setStyle('z-index', zIndex);
10028 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10029 this.maskEl.right.setLeft(box.right + this.padding);
10030 this.maskEl.right.setTop(box.y - this.padding);
10031 this.maskEl.right.show();
10033 this.toolTip.bindEl = this.target.el;
10035 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10037 var tip = this.target.blankText;
10039 if(this.target.getValue() !== '' ) {
10041 if (this.target.invalidText.length) {
10042 tip = this.target.invalidText;
10043 } else if (this.target.regexText.length){
10044 tip = this.target.regexText;
10048 this.toolTip.show(tip);
10050 this.intervalID = window.setInterval(function() {
10051 Roo.bootstrap.Form.popover.unmask();
10054 window.onwheel = function(){ return false;};
10056 (function(){ this.isMasked = true; }).defer(500, this);
10060 unmask : function()
10062 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10066 this.maskEl.top.setStyle('position', 'absolute');
10067 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10068 this.maskEl.top.hide();
10070 this.maskEl.left.setStyle('position', 'absolute');
10071 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10072 this.maskEl.left.hide();
10074 this.maskEl.bottom.setStyle('position', 'absolute');
10075 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10076 this.maskEl.bottom.hide();
10078 this.maskEl.right.setStyle('position', 'absolute');
10079 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10080 this.maskEl.right.hide();
10082 this.toolTip.hide();
10084 this.toolTip.el.hide();
10086 window.onwheel = function(){ return true;};
10088 if(this.intervalID){
10089 window.clearInterval(this.intervalID);
10090 this.intervalID = false;
10093 this.isMasked = false;
10103 * Ext JS Library 1.1.1
10104 * Copyright(c) 2006-2007, Ext JS, LLC.
10106 * Originally Released Under LGPL - original licence link has changed is not relivant.
10109 * <script type="text/javascript">
10112 * @class Roo.form.VTypes
10113 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10116 Roo.form.VTypes = function(){
10117 // closure these in so they are only created once.
10118 var alpha = /^[a-zA-Z_]+$/;
10119 var alphanum = /^[a-zA-Z0-9_]+$/;
10120 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10121 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10123 // All these messages and functions are configurable
10126 * The function used to validate email addresses
10127 * @param {String} value The email address
10129 'email' : function(v){
10130 return email.test(v);
10133 * The error text to display when the email validation function returns false
10136 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10138 * The keystroke filter mask to be applied on email input
10141 'emailMask' : /[a-z0-9_\.\-@]/i,
10144 * The function used to validate URLs
10145 * @param {String} value The URL
10147 'url' : function(v){
10148 return url.test(v);
10151 * The error text to display when the url validation function returns false
10154 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10157 * The function used to validate alpha values
10158 * @param {String} value The value
10160 'alpha' : function(v){
10161 return alpha.test(v);
10164 * The error text to display when the alpha validation function returns false
10167 'alphaText' : 'This field should only contain letters and _',
10169 * The keystroke filter mask to be applied on alpha input
10172 'alphaMask' : /[a-z_]/i,
10175 * The function used to validate alphanumeric values
10176 * @param {String} value The value
10178 'alphanum' : function(v){
10179 return alphanum.test(v);
10182 * The error text to display when the alphanumeric validation function returns false
10185 'alphanumText' : 'This field should only contain letters, numbers and _',
10187 * The keystroke filter mask to be applied on alphanumeric input
10190 'alphanumMask' : /[a-z0-9_]/i
10200 * @class Roo.bootstrap.Input
10201 * @extends Roo.bootstrap.Component
10202 * Bootstrap Input class
10203 * @cfg {Boolean} disabled is it disabled
10204 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10205 * @cfg {String} name name of the input
10206 * @cfg {string} fieldLabel - the label associated
10207 * @cfg {string} placeholder - placeholder to put in text.
10208 * @cfg {string} before - input group add on before
10209 * @cfg {string} after - input group add on after
10210 * @cfg {string} size - (lg|sm) or leave empty..
10211 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10212 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10213 * @cfg {Number} md colspan out of 12 for computer-sized screens
10214 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10215 * @cfg {string} value default value of the input
10216 * @cfg {Number} labelWidth set the width of label
10217 * @cfg {Number} labellg set the width of label (1-12)
10218 * @cfg {Number} labelmd set the width of label (1-12)
10219 * @cfg {Number} labelsm set the width of label (1-12)
10220 * @cfg {Number} labelxs set the width of label (1-12)
10221 * @cfg {String} labelAlign (top|left)
10222 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10223 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10224 * @cfg {String} indicatorpos (left|right) default left
10225 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10226 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10227 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10229 * @cfg {String} align (left|center|right) Default left
10230 * @cfg {Boolean} forceFeedback (true|false) Default false
10233 * Create a new Input
10234 * @param {Object} config The config object
10237 Roo.bootstrap.Input = function(config){
10239 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10244 * Fires when this field receives input focus.
10245 * @param {Roo.form.Field} this
10250 * Fires when this field loses input focus.
10251 * @param {Roo.form.Field} this
10255 * @event specialkey
10256 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10257 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10258 * @param {Roo.form.Field} this
10259 * @param {Roo.EventObject} e The event object
10264 * Fires just before the field blurs if the field value has changed.
10265 * @param {Roo.form.Field} this
10266 * @param {Mixed} newValue The new value
10267 * @param {Mixed} oldValue The original value
10272 * Fires after the field has been marked as invalid.
10273 * @param {Roo.form.Field} this
10274 * @param {String} msg The validation message
10279 * Fires after the field has been validated with no errors.
10280 * @param {Roo.form.Field} this
10285 * Fires after the key up
10286 * @param {Roo.form.Field} this
10287 * @param {Roo.EventObject} e The event Object
10293 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10295 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10296 automatic validation (defaults to "keyup").
10298 validationEvent : "keyup",
10300 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10302 validateOnBlur : true,
10304 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10306 validationDelay : 250,
10308 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10310 focusClass : "x-form-focus", // not needed???
10314 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10316 invalidClass : "has-warning",
10319 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10321 validClass : "has-success",
10324 * @cfg {Boolean} hasFeedback (true|false) default true
10326 hasFeedback : true,
10329 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10331 invalidFeedbackClass : "glyphicon-warning-sign",
10334 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10336 validFeedbackClass : "glyphicon-ok",
10339 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10341 selectOnFocus : false,
10344 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10348 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10353 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10355 disableKeyFilter : false,
10358 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10362 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10366 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10368 blankText : "Please complete this mandatory field",
10371 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10375 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10377 maxLength : Number.MAX_VALUE,
10379 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10381 minLengthText : "The minimum length for this field is {0}",
10383 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10385 maxLengthText : "The maximum length for this field is {0}",
10389 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10390 * If available, this function will be called only after the basic validators all return true, and will be passed the
10391 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10395 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10396 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10397 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10401 * @cfg {String} regexText -- Depricated - use Invalid Text
10406 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10412 autocomplete: false,
10416 inputType : 'text',
10419 placeholder: false,
10424 preventMark: false,
10425 isFormField : true,
10428 labelAlign : false,
10431 formatedValue : false,
10432 forceFeedback : false,
10434 indicatorpos : 'left',
10444 parentLabelAlign : function()
10447 while (parent.parent()) {
10448 parent = parent.parent();
10449 if (typeof(parent.labelAlign) !='undefined') {
10450 return parent.labelAlign;
10457 getAutoCreate : function()
10459 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10465 if(this.inputType != 'hidden'){
10466 cfg.cls = 'form-group' //input-group
10472 type : this.inputType,
10473 value : this.value,
10474 cls : 'form-control',
10475 placeholder : this.placeholder || '',
10476 autocomplete : this.autocomplete || 'new-password'
10479 if(this.capture.length){
10480 input.capture = this.capture;
10483 if(this.accept.length){
10484 input.accept = this.accept + "/*";
10488 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10491 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10492 input.maxLength = this.maxLength;
10495 if (this.disabled) {
10496 input.disabled=true;
10499 if (this.readOnly) {
10500 input.readonly=true;
10504 input.name = this.name;
10508 input.cls += ' input-' + this.size;
10512 ['xs','sm','md','lg'].map(function(size){
10513 if (settings[size]) {
10514 cfg.cls += ' col-' + size + '-' + settings[size];
10518 var inputblock = input;
10522 cls: 'glyphicon form-control-feedback'
10525 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10528 cls : 'has-feedback',
10536 if (this.before || this.after) {
10539 cls : 'input-group',
10543 if (this.before && typeof(this.before) == 'string') {
10545 inputblock.cn.push({
10547 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10551 if (this.before && typeof(this.before) == 'object') {
10552 this.before = Roo.factory(this.before);
10554 inputblock.cn.push({
10556 cls : 'roo-input-before input-group-prepend input-group-' +
10557 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10561 inputblock.cn.push(input);
10563 if (this.after && typeof(this.after) == 'string') {
10564 inputblock.cn.push({
10566 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10570 if (this.after && typeof(this.after) == 'object') {
10571 this.after = Roo.factory(this.after);
10573 inputblock.cn.push({
10575 cls : 'roo-input-after input-group-append input-group-' +
10576 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10580 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10581 inputblock.cls += ' has-feedback';
10582 inputblock.cn.push(feedback);
10587 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10588 tooltip : 'This field is required'
10590 if (Roo.bootstrap.version == 4) {
10593 style : 'display-none'
10596 if (align ==='left' && this.fieldLabel.length) {
10598 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10605 cls : 'control-label col-form-label',
10606 html : this.fieldLabel
10617 var labelCfg = cfg.cn[1];
10618 var contentCfg = cfg.cn[2];
10620 if(this.indicatorpos == 'right'){
10625 cls : 'control-label col-form-label',
10629 html : this.fieldLabel
10643 labelCfg = cfg.cn[0];
10644 contentCfg = cfg.cn[1];
10648 if(this.labelWidth > 12){
10649 labelCfg.style = "width: " + this.labelWidth + 'px';
10652 if(this.labelWidth < 13 && this.labelmd == 0){
10653 this.labelmd = this.labelWidth;
10656 if(this.labellg > 0){
10657 labelCfg.cls += ' col-lg-' + this.labellg;
10658 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10661 if(this.labelmd > 0){
10662 labelCfg.cls += ' col-md-' + this.labelmd;
10663 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10666 if(this.labelsm > 0){
10667 labelCfg.cls += ' col-sm-' + this.labelsm;
10668 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10671 if(this.labelxs > 0){
10672 labelCfg.cls += ' col-xs-' + this.labelxs;
10673 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10677 } else if ( this.fieldLabel.length) {
10682 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10683 tooltip : 'This field is required'
10687 //cls : 'input-group-addon',
10688 html : this.fieldLabel
10696 if(this.indicatorpos == 'right'){
10701 //cls : 'input-group-addon',
10702 html : this.fieldLabel
10707 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10708 tooltip : 'This field is required'
10728 if (this.parentType === 'Navbar' && this.parent().bar) {
10729 cfg.cls += ' navbar-form';
10732 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10733 // on BS4 we do this only if not form
10734 cfg.cls += ' navbar-form';
10742 * return the real input element.
10744 inputEl: function ()
10746 return this.el.select('input.form-control',true).first();
10749 tooltipEl : function()
10751 return this.inputEl();
10754 indicatorEl : function()
10756 if (Roo.bootstrap.version == 4) {
10757 return false; // not enabled in v4 yet.
10760 var indicator = this.el.select('i.roo-required-indicator',true).first();
10770 setDisabled : function(v)
10772 var i = this.inputEl().dom;
10774 i.removeAttribute('disabled');
10778 i.setAttribute('disabled','true');
10780 initEvents : function()
10783 this.inputEl().on("keydown" , this.fireKey, this);
10784 this.inputEl().on("focus", this.onFocus, this);
10785 this.inputEl().on("blur", this.onBlur, this);
10787 this.inputEl().relayEvent('keyup', this);
10789 this.indicator = this.indicatorEl();
10791 if(this.indicator){
10792 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10795 // reference to original value for reset
10796 this.originalValue = this.getValue();
10797 //Roo.form.TextField.superclass.initEvents.call(this);
10798 if(this.validationEvent == 'keyup'){
10799 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10800 this.inputEl().on('keyup', this.filterValidation, this);
10802 else if(this.validationEvent !== false){
10803 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10806 if(this.selectOnFocus){
10807 this.on("focus", this.preFocus, this);
10810 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10811 this.inputEl().on("keypress", this.filterKeys, this);
10813 this.inputEl().relayEvent('keypress', this);
10816 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
10817 this.el.on("click", this.autoSize, this);
10820 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10821 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10824 if (typeof(this.before) == 'object') {
10825 this.before.render(this.el.select('.roo-input-before',true).first());
10827 if (typeof(this.after) == 'object') {
10828 this.after.render(this.el.select('.roo-input-after',true).first());
10831 this.inputEl().on('change', this.onChange, this);
10834 filterValidation : function(e){
10835 if(!e.isNavKeyPress()){
10836 this.validationTask.delay(this.validationDelay);
10840 * Validates the field value
10841 * @return {Boolean} True if the value is valid, else false
10843 validate : function(){
10844 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10845 if(this.disabled || this.validateValue(this.getRawValue())){
10850 this.markInvalid();
10856 * Validates a value according to the field's validation rules and marks the field as invalid
10857 * if the validation fails
10858 * @param {Mixed} value The value to validate
10859 * @return {Boolean} True if the value is valid, else false
10861 validateValue : function(value)
10863 if(this.getVisibilityEl().hasClass('hidden')){
10867 if(value.length < 1) { // if it's blank
10868 if(this.allowBlank){
10874 if(value.length < this.minLength){
10877 if(value.length > this.maxLength){
10881 var vt = Roo.form.VTypes;
10882 if(!vt[this.vtype](value, this)){
10886 if(typeof this.validator == "function"){
10887 var msg = this.validator(value);
10891 if (typeof(msg) == 'string') {
10892 this.invalidText = msg;
10896 if(this.regex && !this.regex.test(value)){
10904 fireKey : function(e){
10905 //Roo.log('field ' + e.getKey());
10906 if(e.isNavKeyPress()){
10907 this.fireEvent("specialkey", this, e);
10910 focus : function (selectText){
10912 this.inputEl().focus();
10913 if(selectText === true){
10914 this.inputEl().dom.select();
10920 onFocus : function(){
10921 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10922 // this.el.addClass(this.focusClass);
10924 if(!this.hasFocus){
10925 this.hasFocus = true;
10926 this.startValue = this.getValue();
10927 this.fireEvent("focus", this);
10931 beforeBlur : Roo.emptyFn,
10935 onBlur : function(){
10937 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10938 //this.el.removeClass(this.focusClass);
10940 this.hasFocus = false;
10941 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10944 var v = this.getValue();
10945 if(String(v) !== String(this.startValue)){
10946 this.fireEvent('change', this, v, this.startValue);
10948 this.fireEvent("blur", this);
10951 onChange : function(e)
10953 var v = this.getValue();
10954 if(String(v) !== String(this.startValue)){
10955 this.fireEvent('change', this, v, this.startValue);
10961 * Resets the current field value to the originally loaded value and clears any validation messages
10963 reset : function(){
10964 this.setValue(this.originalValue);
10968 * Returns the name of the field
10969 * @return {Mixed} name The name field
10971 getName: function(){
10975 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10976 * @return {Mixed} value The field value
10978 getValue : function(){
10980 var v = this.inputEl().getValue();
10985 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10986 * @return {Mixed} value The field value
10988 getRawValue : function(){
10989 var v = this.inputEl().getValue();
10995 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10996 * @param {Mixed} value The value to set
10998 setRawValue : function(v){
10999 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11002 selectText : function(start, end){
11003 var v = this.getRawValue();
11005 start = start === undefined ? 0 : start;
11006 end = end === undefined ? v.length : end;
11007 var d = this.inputEl().dom;
11008 if(d.setSelectionRange){
11009 d.setSelectionRange(start, end);
11010 }else if(d.createTextRange){
11011 var range = d.createTextRange();
11012 range.moveStart("character", start);
11013 range.moveEnd("character", v.length-end);
11020 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11021 * @param {Mixed} value The value to set
11023 setValue : function(v){
11026 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11032 processValue : function(value){
11033 if(this.stripCharsRe){
11034 var newValue = value.replace(this.stripCharsRe, '');
11035 if(newValue !== value){
11036 this.setRawValue(newValue);
11043 preFocus : function(){
11045 if(this.selectOnFocus){
11046 this.inputEl().dom.select();
11049 filterKeys : function(e){
11050 var k = e.getKey();
11051 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11054 var c = e.getCharCode(), cc = String.fromCharCode(c);
11055 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11058 if(!this.maskRe.test(cc)){
11063 * Clear any invalid styles/messages for this field
11065 clearInvalid : function(){
11067 if(!this.el || this.preventMark){ // not rendered
11072 this.el.removeClass([this.invalidClass, 'is-invalid']);
11074 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11076 var feedback = this.el.select('.form-control-feedback', true).first();
11079 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11084 if(this.indicator){
11085 this.indicator.removeClass('visible');
11086 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11089 this.fireEvent('valid', this);
11093 * Mark this field as valid
11095 markValid : function()
11097 if(!this.el || this.preventMark){ // not rendered...
11101 this.el.removeClass([this.invalidClass, this.validClass]);
11102 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11104 var feedback = this.el.select('.form-control-feedback', true).first();
11107 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11110 if(this.indicator){
11111 this.indicator.removeClass('visible');
11112 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11120 if(this.allowBlank && !this.getRawValue().length){
11123 if (Roo.bootstrap.version == 3) {
11124 this.el.addClass(this.validClass);
11126 this.inputEl().addClass('is-valid');
11129 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11131 var feedback = this.el.select('.form-control-feedback', true).first();
11134 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11135 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11140 this.fireEvent('valid', this);
11144 * Mark this field as invalid
11145 * @param {String} msg The validation message
11147 markInvalid : function(msg)
11149 if(!this.el || this.preventMark){ // not rendered
11153 this.el.removeClass([this.invalidClass, this.validClass]);
11154 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11156 var feedback = this.el.select('.form-control-feedback', true).first();
11159 this.el.select('.form-control-feedback', true).first().removeClass(
11160 [this.invalidFeedbackClass, this.validFeedbackClass]);
11167 if(this.allowBlank && !this.getRawValue().length){
11171 if(this.indicator){
11172 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11173 this.indicator.addClass('visible');
11175 if (Roo.bootstrap.version == 3) {
11176 this.el.addClass(this.invalidClass);
11178 this.inputEl().addClass('is-invalid');
11183 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11185 var feedback = this.el.select('.form-control-feedback', true).first();
11188 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11190 if(this.getValue().length || this.forceFeedback){
11191 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11198 this.fireEvent('invalid', this, msg);
11201 SafariOnKeyDown : function(event)
11203 // this is a workaround for a password hang bug on chrome/ webkit.
11204 if (this.inputEl().dom.type != 'password') {
11208 var isSelectAll = false;
11210 if(this.inputEl().dom.selectionEnd > 0){
11211 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11213 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11214 event.preventDefault();
11219 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11221 event.preventDefault();
11222 // this is very hacky as keydown always get's upper case.
11224 var cc = String.fromCharCode(event.getCharCode());
11225 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11229 adjustWidth : function(tag, w){
11230 tag = tag.toLowerCase();
11231 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11232 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11233 if(tag == 'input'){
11236 if(tag == 'textarea'){
11239 }else if(Roo.isOpera){
11240 if(tag == 'input'){
11243 if(tag == 'textarea'){
11251 setFieldLabel : function(v)
11253 if(!this.rendered){
11257 if(this.indicatorEl()){
11258 var ar = this.el.select('label > span',true);
11260 if (ar.elements.length) {
11261 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11262 this.fieldLabel = v;
11266 var br = this.el.select('label',true);
11268 if(br.elements.length) {
11269 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11270 this.fieldLabel = v;
11274 Roo.log('Cannot Found any of label > span || label in input');
11278 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11279 this.fieldLabel = v;
11294 * @class Roo.bootstrap.TextArea
11295 * @extends Roo.bootstrap.Input
11296 * Bootstrap TextArea class
11297 * @cfg {Number} cols Specifies the visible width of a text area
11298 * @cfg {Number} rows Specifies the visible number of lines in a text area
11299 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11300 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11301 * @cfg {string} html text
11304 * Create a new TextArea
11305 * @param {Object} config The config object
11308 Roo.bootstrap.TextArea = function(config){
11309 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11313 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11323 getAutoCreate : function(){
11325 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11331 if(this.inputType != 'hidden'){
11332 cfg.cls = 'form-group' //input-group
11340 value : this.value || '',
11341 html: this.html || '',
11342 cls : 'form-control',
11343 placeholder : this.placeholder || ''
11347 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11348 input.maxLength = this.maxLength;
11352 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11356 input.cols = this.cols;
11359 if (this.readOnly) {
11360 input.readonly = true;
11364 input.name = this.name;
11368 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11372 ['xs','sm','md','lg'].map(function(size){
11373 if (settings[size]) {
11374 cfg.cls += ' col-' + size + '-' + settings[size];
11378 var inputblock = input;
11380 if(this.hasFeedback && !this.allowBlank){
11384 cls: 'glyphicon form-control-feedback'
11388 cls : 'has-feedback',
11397 if (this.before || this.after) {
11400 cls : 'input-group',
11404 inputblock.cn.push({
11406 cls : 'input-group-addon',
11411 inputblock.cn.push(input);
11413 if(this.hasFeedback && !this.allowBlank){
11414 inputblock.cls += ' has-feedback';
11415 inputblock.cn.push(feedback);
11419 inputblock.cn.push({
11421 cls : 'input-group-addon',
11428 if (align ==='left' && this.fieldLabel.length) {
11433 cls : 'control-label',
11434 html : this.fieldLabel
11445 if(this.labelWidth > 12){
11446 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11449 if(this.labelWidth < 13 && this.labelmd == 0){
11450 this.labelmd = this.labelWidth;
11453 if(this.labellg > 0){
11454 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11455 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11458 if(this.labelmd > 0){
11459 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11460 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11463 if(this.labelsm > 0){
11464 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11465 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11468 if(this.labelxs > 0){
11469 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11470 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11473 } else if ( this.fieldLabel.length) {
11478 //cls : 'input-group-addon',
11479 html : this.fieldLabel
11497 if (this.disabled) {
11498 input.disabled=true;
11505 * return the real textarea element.
11507 inputEl: function ()
11509 return this.el.select('textarea.form-control',true).first();
11513 * Clear any invalid styles/messages for this field
11515 clearInvalid : function()
11518 if(!this.el || this.preventMark){ // not rendered
11522 var label = this.el.select('label', true).first();
11523 var icon = this.el.select('i.fa-star', true).first();
11528 this.el.removeClass( this.validClass);
11529 this.inputEl().removeClass('is-invalid');
11531 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11533 var feedback = this.el.select('.form-control-feedback', true).first();
11536 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11541 this.fireEvent('valid', this);
11545 * Mark this field as valid
11547 markValid : function()
11549 if(!this.el || this.preventMark){ // not rendered
11553 this.el.removeClass([this.invalidClass, this.validClass]);
11554 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11556 var feedback = this.el.select('.form-control-feedback', true).first();
11559 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11562 if(this.disabled || this.allowBlank){
11566 var label = this.el.select('label', true).first();
11567 var icon = this.el.select('i.fa-star', true).first();
11572 if (Roo.bootstrap.version == 3) {
11573 this.el.addClass(this.validClass);
11575 this.inputEl().addClass('is-valid');
11579 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11581 var feedback = this.el.select('.form-control-feedback', true).first();
11584 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11585 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11590 this.fireEvent('valid', this);
11594 * Mark this field as invalid
11595 * @param {String} msg The validation message
11597 markInvalid : function(msg)
11599 if(!this.el || this.preventMark){ // not rendered
11603 this.el.removeClass([this.invalidClass, this.validClass]);
11604 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11606 var feedback = this.el.select('.form-control-feedback', true).first();
11609 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11612 if(this.disabled || this.allowBlank){
11616 var label = this.el.select('label', true).first();
11617 var icon = this.el.select('i.fa-star', true).first();
11619 if(!this.getValue().length && label && !icon){
11620 this.el.createChild({
11622 cls : 'text-danger fa fa-lg fa-star',
11623 tooltip : 'This field is required',
11624 style : 'margin-right:5px;'
11628 if (Roo.bootstrap.version == 3) {
11629 this.el.addClass(this.invalidClass);
11631 this.inputEl().addClass('is-invalid');
11634 // fixme ... this may be depricated need to test..
11635 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11637 var feedback = this.el.select('.form-control-feedback', true).first();
11640 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11642 if(this.getValue().length || this.forceFeedback){
11643 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11650 this.fireEvent('invalid', this, msg);
11658 * trigger field - base class for combo..
11663 * @class Roo.bootstrap.TriggerField
11664 * @extends Roo.bootstrap.Input
11665 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11666 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11667 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11668 * for which you can provide a custom implementation. For example:
11670 var trigger = new Roo.bootstrap.TriggerField();
11671 trigger.onTriggerClick = myTriggerFn;
11672 trigger.applyTo('my-field');
11675 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11676 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11677 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11678 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11679 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11682 * Create a new TriggerField.
11683 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11684 * to the base TextField)
11686 Roo.bootstrap.TriggerField = function(config){
11687 this.mimicing = false;
11688 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11691 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11693 * @cfg {String} triggerClass A CSS class to apply to the trigger
11696 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11701 * @cfg {Boolean} removable (true|false) special filter default false
11705 /** @cfg {Boolean} grow @hide */
11706 /** @cfg {Number} growMin @hide */
11707 /** @cfg {Number} growMax @hide */
11713 autoSize: Roo.emptyFn,
11717 deferHeight : true,
11720 actionMode : 'wrap',
11725 getAutoCreate : function(){
11727 var align = this.labelAlign || this.parentLabelAlign();
11732 cls: 'form-group' //input-group
11739 type : this.inputType,
11740 cls : 'form-control',
11741 autocomplete: 'new-password',
11742 placeholder : this.placeholder || ''
11746 input.name = this.name;
11749 input.cls += ' input-' + this.size;
11752 if (this.disabled) {
11753 input.disabled=true;
11756 var inputblock = input;
11758 if(this.hasFeedback && !this.allowBlank){
11762 cls: 'glyphicon form-control-feedback'
11765 if(this.removable && !this.editable ){
11767 cls : 'has-feedback',
11773 cls : 'roo-combo-removable-btn close'
11780 cls : 'has-feedback',
11789 if(this.removable && !this.editable ){
11791 cls : 'roo-removable',
11797 cls : 'roo-combo-removable-btn close'
11804 if (this.before || this.after) {
11807 cls : 'input-group',
11811 inputblock.cn.push({
11813 cls : 'input-group-addon input-group-prepend input-group-text',
11818 inputblock.cn.push(input);
11820 if(this.hasFeedback && !this.allowBlank){
11821 inputblock.cls += ' has-feedback';
11822 inputblock.cn.push(feedback);
11826 inputblock.cn.push({
11828 cls : 'input-group-addon input-group-append input-group-text',
11837 var ibwrap = inputblock;
11842 cls: 'roo-select2-choices',
11846 cls: 'roo-select2-search-field',
11858 cls: 'roo-select2-container input-group',
11863 cls: 'form-hidden-field'
11869 if(!this.multiple && this.showToggleBtn){
11875 if (this.caret != false) {
11878 cls: 'fa fa-' + this.caret
11885 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11887 Roo.bootstrap.version == 3 ? caret : '',
11890 cls: 'combobox-clear',
11904 combobox.cls += ' roo-select2-container-multi';
11908 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11909 tooltip : 'This field is required'
11911 if (Roo.bootstrap.version == 4) {
11914 style : 'display:none'
11919 if (align ==='left' && this.fieldLabel.length) {
11921 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11928 cls : 'control-label',
11929 html : this.fieldLabel
11941 var labelCfg = cfg.cn[1];
11942 var contentCfg = cfg.cn[2];
11944 if(this.indicatorpos == 'right'){
11949 cls : 'control-label',
11953 html : this.fieldLabel
11967 labelCfg = cfg.cn[0];
11968 contentCfg = cfg.cn[1];
11971 if(this.labelWidth > 12){
11972 labelCfg.style = "width: " + this.labelWidth + 'px';
11975 if(this.labelWidth < 13 && this.labelmd == 0){
11976 this.labelmd = this.labelWidth;
11979 if(this.labellg > 0){
11980 labelCfg.cls += ' col-lg-' + this.labellg;
11981 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11984 if(this.labelmd > 0){
11985 labelCfg.cls += ' col-md-' + this.labelmd;
11986 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11989 if(this.labelsm > 0){
11990 labelCfg.cls += ' col-sm-' + this.labelsm;
11991 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11994 if(this.labelxs > 0){
11995 labelCfg.cls += ' col-xs-' + this.labelxs;
11996 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11999 } else if ( this.fieldLabel.length) {
12000 // Roo.log(" label");
12005 //cls : 'input-group-addon',
12006 html : this.fieldLabel
12014 if(this.indicatorpos == 'right'){
12022 html : this.fieldLabel
12036 // Roo.log(" no label && no align");
12043 ['xs','sm','md','lg'].map(function(size){
12044 if (settings[size]) {
12045 cfg.cls += ' col-' + size + '-' + settings[size];
12056 onResize : function(w, h){
12057 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12058 // if(typeof w == 'number'){
12059 // var x = w - this.trigger.getWidth();
12060 // this.inputEl().setWidth(this.adjustWidth('input', x));
12061 // this.trigger.setStyle('left', x+'px');
12066 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12069 getResizeEl : function(){
12070 return this.inputEl();
12074 getPositionEl : function(){
12075 return this.inputEl();
12079 alignErrorIcon : function(){
12080 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12084 initEvents : function(){
12088 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12089 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12090 if(!this.multiple && this.showToggleBtn){
12091 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12092 if(this.hideTrigger){
12093 this.trigger.setDisplayed(false);
12095 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12099 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12102 if(this.removable && !this.editable && !this.tickable){
12103 var close = this.closeTriggerEl();
12106 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12107 close.on('click', this.removeBtnClick, this, close);
12111 //this.trigger.addClassOnOver('x-form-trigger-over');
12112 //this.trigger.addClassOnClick('x-form-trigger-click');
12115 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12119 closeTriggerEl : function()
12121 var close = this.el.select('.roo-combo-removable-btn', true).first();
12122 return close ? close : false;
12125 removeBtnClick : function(e, h, el)
12127 e.preventDefault();
12129 if(this.fireEvent("remove", this) !== false){
12131 this.fireEvent("afterremove", this)
12135 createList : function()
12137 this.list = Roo.get(document.body).createChild({
12138 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12139 cls: 'typeahead typeahead-long dropdown-menu',
12140 style: 'display:none'
12143 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12148 initTrigger : function(){
12153 onDestroy : function(){
12155 this.trigger.removeAllListeners();
12156 // this.trigger.remove();
12159 // this.wrap.remove();
12161 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12165 onFocus : function(){
12166 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12168 if(!this.mimicing){
12169 this.wrap.addClass('x-trigger-wrap-focus');
12170 this.mimicing = true;
12171 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12172 if(this.monitorTab){
12173 this.el.on("keydown", this.checkTab, this);
12180 checkTab : function(e){
12181 if(e.getKey() == e.TAB){
12182 this.triggerBlur();
12187 onBlur : function(){
12192 mimicBlur : function(e, t){
12194 if(!this.wrap.contains(t) && this.validateBlur()){
12195 this.triggerBlur();
12201 triggerBlur : function(){
12202 this.mimicing = false;
12203 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12204 if(this.monitorTab){
12205 this.el.un("keydown", this.checkTab, this);
12207 //this.wrap.removeClass('x-trigger-wrap-focus');
12208 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12212 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12213 validateBlur : function(e, t){
12218 onDisable : function(){
12219 this.inputEl().dom.disabled = true;
12220 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12222 // this.wrap.addClass('x-item-disabled');
12227 onEnable : function(){
12228 this.inputEl().dom.disabled = false;
12229 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12231 // this.el.removeClass('x-item-disabled');
12236 onShow : function(){
12237 var ae = this.getActionEl();
12240 ae.dom.style.display = '';
12241 ae.dom.style.visibility = 'visible';
12247 onHide : function(){
12248 var ae = this.getActionEl();
12249 ae.dom.style.display = 'none';
12253 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12254 * by an implementing function.
12256 * @param {EventObject} e
12258 onTriggerClick : Roo.emptyFn
12266 * @class Roo.bootstrap.CardUploader
12267 * @extends Roo.bootstrap.Button
12268 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12269 * @cfg {Number} errorTimeout default 3000
12270 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12271 * @cfg {Array} html The button text.
12275 * Create a new CardUploader
12276 * @param {Object} config The config object
12279 Roo.bootstrap.CardUploader = function(config){
12283 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12286 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12293 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12296 errorTimeout : 3000,
12300 fileCollection : false,
12303 getAutoCreate : function()
12307 cls :'form-group' ,
12312 //cls : 'input-group-addon',
12313 html : this.fieldLabel
12320 value : this.value,
12321 cls : 'd-none form-control'
12326 multiple : 'multiple',
12328 cls : 'd-none roo-card-upload-selector'
12332 cls : 'roo-card-uploader-button-container w-100 mb-2'
12335 cls : 'card-columns roo-card-uploader-container'
12345 getChildContainer : function() /// what children are added to.
12347 return this.containerEl;
12350 getButtonContainer : function() /// what children are added to.
12352 return this.el.select(".roo-card-uploader-button-container").first();
12355 initEvents : function()
12358 Roo.bootstrap.Input.prototype.initEvents.call(this);
12362 xns: Roo.bootstrap,
12365 container_method : 'getButtonContainer' ,
12366 html : this.html, // fix changable?
12369 'click' : function(btn, e) {
12378 this.urlAPI = (window.createObjectURL && window) ||
12379 (window.URL && URL.revokeObjectURL && URL) ||
12380 (window.webkitURL && webkitURL);
12385 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12387 this.selectorEl.on('change', this.onFileSelected, this);
12390 this.images.forEach(function(img) {
12393 this.images = false;
12395 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12401 onClick : function(e)
12403 e.preventDefault();
12405 this.selectorEl.dom.click();
12409 onFileSelected : function(e)
12411 e.preventDefault();
12413 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12417 Roo.each(this.selectorEl.dom.files, function(file){
12418 this.addFile(file);
12427 addFile : function(file)
12430 if(typeof(file) === 'string'){
12431 throw "Add file by name?"; // should not happen
12435 if(!file || !this.urlAPI){
12445 var url = _this.urlAPI.createObjectURL( file);
12448 id : Roo.bootstrap.CardUploader.ID--,
12449 is_uploaded : false,
12452 mimetype : file.type,
12459 addCard : function (data)
12461 // hidden input element?
12462 // if the file is not an image...
12463 //then we need to use something other that and header_image
12468 xns : Roo.bootstrap,
12469 xtype : 'CardFooter',
12472 xns : Roo.bootstrap,
12478 xns : Roo.bootstrap,
12480 html : String.format("<small>{0}</small>", data.title),
12481 cls : 'col-11 text-left',
12486 click : function() {
12487 this.downloadCard(data.id)
12493 xns : Roo.bootstrap,
12501 click : function() {
12502 t.removeCard(data.id)
12514 var cn = this.addxtype(
12517 xns : Roo.bootstrap,
12520 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12521 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12522 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12527 initEvents : function() {
12528 Roo.bootstrap.Card.prototype.initEvents.call(this);
12529 this.imgEl = this.el.select('.card-img-top').first();
12531 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12532 this.imgEl.set({ 'pointer' : 'cursor' });
12541 // dont' really need ot update items.
12542 // this.items.push(cn);
12543 this.fileCollection.add(cn);
12544 this.updateInput();
12547 removeCard : function(id)
12550 var card = this.fileCollection.get(id);
12551 card.data.is_deleted = 1;
12552 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12553 this.fileCollection.remove(card);
12554 //this.items = this.items.filter(function(e) { return e != card });
12555 // dont' really need ot update items.
12556 card.el.dom.parentNode.removeChild(card.el.dom);
12561 this.fileCollection.each(function(card) {
12562 card.el.dom.parentNode.removeChild(card.el.dom);
12564 this.fileCollection.clear();
12565 this.updateInput();
12568 updateInput : function()
12571 this.fileCollection.each(function(e) {
12575 this.inputEl().dom.value = JSON.stringify(data);
12582 Roo.bootstrap.CardUploader.ID = -1;/*
12584 * Ext JS Library 1.1.1
12585 * Copyright(c) 2006-2007, Ext JS, LLC.
12587 * Originally Released Under LGPL - original licence link has changed is not relivant.
12590 * <script type="text/javascript">
12595 * @class Roo.data.SortTypes
12597 * Defines the default sorting (casting?) comparison functions used when sorting data.
12599 Roo.data.SortTypes = {
12601 * Default sort that does nothing
12602 * @param {Mixed} s The value being converted
12603 * @return {Mixed} The comparison value
12605 none : function(s){
12610 * The regular expression used to strip tags
12614 stripTagsRE : /<\/?[^>]+>/gi,
12617 * Strips all HTML tags to sort on text only
12618 * @param {Mixed} s The value being converted
12619 * @return {String} The comparison value
12621 asText : function(s){
12622 return String(s).replace(this.stripTagsRE, "");
12626 * Strips all HTML tags to sort on text only - Case insensitive
12627 * @param {Mixed} s The value being converted
12628 * @return {String} The comparison value
12630 asUCText : function(s){
12631 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12635 * Case insensitive string
12636 * @param {Mixed} s The value being converted
12637 * @return {String} The comparison value
12639 asUCString : function(s) {
12640 return String(s).toUpperCase();
12645 * @param {Mixed} s The value being converted
12646 * @return {Number} The comparison value
12648 asDate : function(s) {
12652 if(s instanceof Date){
12653 return s.getTime();
12655 return Date.parse(String(s));
12660 * @param {Mixed} s The value being converted
12661 * @return {Float} The comparison value
12663 asFloat : function(s) {
12664 var val = parseFloat(String(s).replace(/,/g, ""));
12673 * @param {Mixed} s The value being converted
12674 * @return {Number} The comparison value
12676 asInt : function(s) {
12677 var val = parseInt(String(s).replace(/,/g, ""));
12685 * Ext JS Library 1.1.1
12686 * Copyright(c) 2006-2007, Ext JS, LLC.
12688 * Originally Released Under LGPL - original licence link has changed is not relivant.
12691 * <script type="text/javascript">
12695 * @class Roo.data.Record
12696 * Instances of this class encapsulate both record <em>definition</em> information, and record
12697 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12698 * to access Records cached in an {@link Roo.data.Store} object.<br>
12700 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12701 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12704 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12706 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12707 * {@link #create}. The parameters are the same.
12708 * @param {Array} data An associative Array of data values keyed by the field name.
12709 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12710 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12711 * not specified an integer id is generated.
12713 Roo.data.Record = function(data, id){
12714 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12719 * Generate a constructor for a specific record layout.
12720 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12721 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12722 * Each field definition object may contain the following properties: <ul>
12723 * <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,
12724 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12725 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12726 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12727 * is being used, then this is a string containing the javascript expression to reference the data relative to
12728 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12729 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12730 * this may be omitted.</p></li>
12731 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12732 * <ul><li>auto (Default, implies no conversion)</li>
12737 * <li>date</li></ul></p></li>
12738 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12739 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12740 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12741 * by the Reader into an object that will be stored in the Record. It is passed the
12742 * following parameters:<ul>
12743 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12745 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12747 * <br>usage:<br><pre><code>
12748 var TopicRecord = Roo.data.Record.create(
12749 {name: 'title', mapping: 'topic_title'},
12750 {name: 'author', mapping: 'username'},
12751 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12752 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12753 {name: 'lastPoster', mapping: 'user2'},
12754 {name: 'excerpt', mapping: 'post_text'}
12757 var myNewRecord = new TopicRecord({
12758 title: 'Do my job please',
12761 lastPost: new Date(),
12762 lastPoster: 'Animal',
12763 excerpt: 'No way dude!'
12765 myStore.add(myNewRecord);
12770 Roo.data.Record.create = function(o){
12771 var f = function(){
12772 f.superclass.constructor.apply(this, arguments);
12774 Roo.extend(f, Roo.data.Record);
12775 var p = f.prototype;
12776 p.fields = new Roo.util.MixedCollection(false, function(field){
12779 for(var i = 0, len = o.length; i < len; i++){
12780 p.fields.add(new Roo.data.Field(o[i]));
12782 f.getField = function(name){
12783 return p.fields.get(name);
12788 Roo.data.Record.AUTO_ID = 1000;
12789 Roo.data.Record.EDIT = 'edit';
12790 Roo.data.Record.REJECT = 'reject';
12791 Roo.data.Record.COMMIT = 'commit';
12793 Roo.data.Record.prototype = {
12795 * Readonly flag - true if this record has been modified.
12804 join : function(store){
12805 this.store = store;
12809 * Set the named field to the specified value.
12810 * @param {String} name The name of the field to set.
12811 * @param {Object} value The value to set the field to.
12813 set : function(name, value){
12814 if(this.data[name] == value){
12818 if(!this.modified){
12819 this.modified = {};
12821 if(typeof this.modified[name] == 'undefined'){
12822 this.modified[name] = this.data[name];
12824 this.data[name] = value;
12825 if(!this.editing && this.store){
12826 this.store.afterEdit(this);
12831 * Get the value of the named field.
12832 * @param {String} name The name of the field to get the value of.
12833 * @return {Object} The value of the field.
12835 get : function(name){
12836 return this.data[name];
12840 beginEdit : function(){
12841 this.editing = true;
12842 this.modified = {};
12846 cancelEdit : function(){
12847 this.editing = false;
12848 delete this.modified;
12852 endEdit : function(){
12853 this.editing = false;
12854 if(this.dirty && this.store){
12855 this.store.afterEdit(this);
12860 * Usually called by the {@link Roo.data.Store} which owns the Record.
12861 * Rejects all changes made to the Record since either creation, or the last commit operation.
12862 * Modified fields are reverted to their original values.
12864 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12865 * of reject operations.
12867 reject : function(){
12868 var m = this.modified;
12870 if(typeof m[n] != "function"){
12871 this.data[n] = m[n];
12874 this.dirty = false;
12875 delete this.modified;
12876 this.editing = false;
12878 this.store.afterReject(this);
12883 * Usually called by the {@link Roo.data.Store} which owns the Record.
12884 * Commits all changes made to the Record since either creation, or the last commit operation.
12886 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12887 * of commit operations.
12889 commit : function(){
12890 this.dirty = false;
12891 delete this.modified;
12892 this.editing = false;
12894 this.store.afterCommit(this);
12899 hasError : function(){
12900 return this.error != null;
12904 clearError : function(){
12909 * Creates a copy of this record.
12910 * @param {String} id (optional) A new record id if you don't want to use this record's id
12913 copy : function(newId) {
12914 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12918 * Ext JS Library 1.1.1
12919 * Copyright(c) 2006-2007, Ext JS, LLC.
12921 * Originally Released Under LGPL - original licence link has changed is not relivant.
12924 * <script type="text/javascript">
12930 * @class Roo.data.Store
12931 * @extends Roo.util.Observable
12932 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12933 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12935 * 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
12936 * has no knowledge of the format of the data returned by the Proxy.<br>
12938 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12939 * instances from the data object. These records are cached and made available through accessor functions.
12941 * Creates a new Store.
12942 * @param {Object} config A config object containing the objects needed for the Store to access data,
12943 * and read the data into Records.
12945 Roo.data.Store = function(config){
12946 this.data = new Roo.util.MixedCollection(false);
12947 this.data.getKey = function(o){
12950 this.baseParams = {};
12952 this.paramNames = {
12957 "multisort" : "_multisort"
12960 if(config && config.data){
12961 this.inlineData = config.data;
12962 delete config.data;
12965 Roo.apply(this, config);
12967 if(this.reader){ // reader passed
12968 this.reader = Roo.factory(this.reader, Roo.data);
12969 this.reader.xmodule = this.xmodule || false;
12970 if(!this.recordType){
12971 this.recordType = this.reader.recordType;
12973 if(this.reader.onMetaChange){
12974 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12978 if(this.recordType){
12979 this.fields = this.recordType.prototype.fields;
12981 this.modified = [];
12985 * @event datachanged
12986 * Fires when the data cache has changed, and a widget which is using this Store
12987 * as a Record cache should refresh its view.
12988 * @param {Store} this
12990 datachanged : true,
12992 * @event metachange
12993 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12994 * @param {Store} this
12995 * @param {Object} meta The JSON metadata
13000 * Fires when Records have been added to the Store
13001 * @param {Store} this
13002 * @param {Roo.data.Record[]} records The array of Records added
13003 * @param {Number} index The index at which the record(s) were added
13008 * Fires when a Record has been removed from the Store
13009 * @param {Store} this
13010 * @param {Roo.data.Record} record The Record that was removed
13011 * @param {Number} index The index at which the record was removed
13016 * Fires when a Record has been updated
13017 * @param {Store} this
13018 * @param {Roo.data.Record} record The Record that was updated
13019 * @param {String} operation The update operation being performed. Value may be one of:
13021 Roo.data.Record.EDIT
13022 Roo.data.Record.REJECT
13023 Roo.data.Record.COMMIT
13029 * Fires when the data cache has been cleared.
13030 * @param {Store} this
13034 * @event beforeload
13035 * Fires before a request is made for a new data object. If the beforeload handler returns false
13036 * the load action will be canceled.
13037 * @param {Store} this
13038 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13042 * @event beforeloadadd
13043 * Fires after a new set of Records has been loaded.
13044 * @param {Store} this
13045 * @param {Roo.data.Record[]} records The Records that were loaded
13046 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13048 beforeloadadd : true,
13051 * Fires after a new set of Records has been loaded, before they are added to the store.
13052 * @param {Store} this
13053 * @param {Roo.data.Record[]} records The Records that were loaded
13054 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13055 * @params {Object} return from reader
13059 * @event loadexception
13060 * Fires if an exception occurs in the Proxy during loading.
13061 * Called with the signature of the Proxy's "loadexception" event.
13062 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13065 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13066 * @param {Object} load options
13067 * @param {Object} jsonData from your request (normally this contains the Exception)
13069 loadexception : true
13073 this.proxy = Roo.factory(this.proxy, Roo.data);
13074 this.proxy.xmodule = this.xmodule || false;
13075 this.relayEvents(this.proxy, ["loadexception"]);
13077 this.sortToggle = {};
13078 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13080 Roo.data.Store.superclass.constructor.call(this);
13082 if(this.inlineData){
13083 this.loadData(this.inlineData);
13084 delete this.inlineData;
13088 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13090 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13091 * without a remote query - used by combo/forms at present.
13095 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13098 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13101 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13102 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13105 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13106 * on any HTTP request
13109 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13112 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13116 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13117 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13119 remoteSort : false,
13122 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13123 * loaded or when a record is removed. (defaults to false).
13125 pruneModifiedRecords : false,
13128 lastOptions : null,
13131 * Add Records to the Store and fires the add event.
13132 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13134 add : function(records){
13135 records = [].concat(records);
13136 for(var i = 0, len = records.length; i < len; i++){
13137 records[i].join(this);
13139 var index = this.data.length;
13140 this.data.addAll(records);
13141 this.fireEvent("add", this, records, index);
13145 * Remove a Record from the Store and fires the remove event.
13146 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13148 remove : function(record){
13149 var index = this.data.indexOf(record);
13150 this.data.removeAt(index);
13152 if(this.pruneModifiedRecords){
13153 this.modified.remove(record);
13155 this.fireEvent("remove", this, record, index);
13159 * Remove all Records from the Store and fires the clear event.
13161 removeAll : function(){
13163 if(this.pruneModifiedRecords){
13164 this.modified = [];
13166 this.fireEvent("clear", this);
13170 * Inserts Records to the Store at the given index and fires the add event.
13171 * @param {Number} index The start index at which to insert the passed Records.
13172 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13174 insert : function(index, records){
13175 records = [].concat(records);
13176 for(var i = 0, len = records.length; i < len; i++){
13177 this.data.insert(index, records[i]);
13178 records[i].join(this);
13180 this.fireEvent("add", this, records, index);
13184 * Get the index within the cache of the passed Record.
13185 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13186 * @return {Number} The index of the passed Record. Returns -1 if not found.
13188 indexOf : function(record){
13189 return this.data.indexOf(record);
13193 * Get the index within the cache of the Record with the passed id.
13194 * @param {String} id The id of the Record to find.
13195 * @return {Number} The index of the Record. Returns -1 if not found.
13197 indexOfId : function(id){
13198 return this.data.indexOfKey(id);
13202 * Get the Record with the specified id.
13203 * @param {String} id The id of the Record to find.
13204 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13206 getById : function(id){
13207 return this.data.key(id);
13211 * Get the Record at the specified index.
13212 * @param {Number} index The index of the Record to find.
13213 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13215 getAt : function(index){
13216 return this.data.itemAt(index);
13220 * Returns a range of Records between specified indices.
13221 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13222 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13223 * @return {Roo.data.Record[]} An array of Records
13225 getRange : function(start, end){
13226 return this.data.getRange(start, end);
13230 storeOptions : function(o){
13231 o = Roo.apply({}, o);
13234 this.lastOptions = o;
13238 * Loads the Record cache from the configured Proxy using the configured Reader.
13240 * If using remote paging, then the first load call must specify the <em>start</em>
13241 * and <em>limit</em> properties in the options.params property to establish the initial
13242 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13244 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13245 * and this call will return before the new data has been loaded. Perform any post-processing
13246 * in a callback function, or in a "load" event handler.</strong>
13248 * @param {Object} options An object containing properties which control loading options:<ul>
13249 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13250 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13251 * passed the following arguments:<ul>
13252 * <li>r : Roo.data.Record[]</li>
13253 * <li>options: Options object from the load call</li>
13254 * <li>success: Boolean success indicator</li></ul></li>
13255 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13256 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13259 load : function(options){
13260 options = options || {};
13261 if(this.fireEvent("beforeload", this, options) !== false){
13262 this.storeOptions(options);
13263 var p = Roo.apply(options.params || {}, this.baseParams);
13264 // if meta was not loaded from remote source.. try requesting it.
13265 if (!this.reader.metaFromRemote) {
13266 p._requestMeta = 1;
13268 if(this.sortInfo && this.remoteSort){
13269 var pn = this.paramNames;
13270 p[pn["sort"]] = this.sortInfo.field;
13271 p[pn["dir"]] = this.sortInfo.direction;
13273 if (this.multiSort) {
13274 var pn = this.paramNames;
13275 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13278 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13283 * Reloads the Record cache from the configured Proxy using the configured Reader and
13284 * the options from the last load operation performed.
13285 * @param {Object} options (optional) An object containing properties which may override the options
13286 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13287 * the most recently used options are reused).
13289 reload : function(options){
13290 this.load(Roo.applyIf(options||{}, this.lastOptions));
13294 // Called as a callback by the Reader during a load operation.
13295 loadRecords : function(o, options, success){
13296 if(!o || success === false){
13297 if(success !== false){
13298 this.fireEvent("load", this, [], options, o);
13300 if(options.callback){
13301 options.callback.call(options.scope || this, [], options, false);
13305 // if data returned failure - throw an exception.
13306 if (o.success === false) {
13307 // show a message if no listener is registered.
13308 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13309 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13311 // loadmask wil be hooked into this..
13312 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13315 var r = o.records, t = o.totalRecords || r.length;
13317 this.fireEvent("beforeloadadd", this, r, options, o);
13319 if(!options || options.add !== true){
13320 if(this.pruneModifiedRecords){
13321 this.modified = [];
13323 for(var i = 0, len = r.length; i < len; i++){
13327 this.data = this.snapshot;
13328 delete this.snapshot;
13331 this.data.addAll(r);
13332 this.totalLength = t;
13334 this.fireEvent("datachanged", this);
13336 this.totalLength = Math.max(t, this.data.length+r.length);
13340 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13342 var e = new Roo.data.Record({});
13344 e.set(this.parent.displayField, this.parent.emptyTitle);
13345 e.set(this.parent.valueField, '');
13350 this.fireEvent("load", this, r, options, o);
13351 if(options.callback){
13352 options.callback.call(options.scope || this, r, options, true);
13358 * Loads data from a passed data block. A Reader which understands the format of the data
13359 * must have been configured in the constructor.
13360 * @param {Object} data The data block from which to read the Records. The format of the data expected
13361 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13362 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13364 loadData : function(o, append){
13365 var r = this.reader.readRecords(o);
13366 this.loadRecords(r, {add: append}, true);
13370 * using 'cn' the nested child reader read the child array into it's child stores.
13371 * @param {Object} rec The record with a 'children array
13373 loadDataFromChildren : function(rec)
13375 this.loadData(this.reader.toLoadData(rec));
13380 * Gets the number of cached records.
13382 * <em>If using paging, this may not be the total size of the dataset. If the data object
13383 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13384 * the data set size</em>
13386 getCount : function(){
13387 return this.data.length || 0;
13391 * Gets the total number of records in the dataset as returned by the server.
13393 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13394 * the dataset size</em>
13396 getTotalCount : function(){
13397 return this.totalLength || 0;
13401 * Returns the sort state of the Store as an object with two properties:
13403 field {String} The name of the field by which the Records are sorted
13404 direction {String} The sort order, "ASC" or "DESC"
13407 getSortState : function(){
13408 return this.sortInfo;
13412 applySort : function(){
13413 if(this.sortInfo && !this.remoteSort){
13414 var s = this.sortInfo, f = s.field;
13415 var st = this.fields.get(f).sortType;
13416 var fn = function(r1, r2){
13417 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13418 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13420 this.data.sort(s.direction, fn);
13421 if(this.snapshot && this.snapshot != this.data){
13422 this.snapshot.sort(s.direction, fn);
13428 * Sets the default sort column and order to be used by the next load operation.
13429 * @param {String} fieldName The name of the field to sort by.
13430 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13432 setDefaultSort : function(field, dir){
13433 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13437 * Sort the Records.
13438 * If remote sorting is used, the sort is performed on the server, and the cache is
13439 * reloaded. If local sorting is used, the cache is sorted internally.
13440 * @param {String} fieldName The name of the field to sort by.
13441 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13443 sort : function(fieldName, dir){
13444 var f = this.fields.get(fieldName);
13446 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13448 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13449 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13454 this.sortToggle[f.name] = dir;
13455 this.sortInfo = {field: f.name, direction: dir};
13456 if(!this.remoteSort){
13458 this.fireEvent("datachanged", this);
13460 this.load(this.lastOptions);
13465 * Calls the specified function for each of the Records in the cache.
13466 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13467 * Returning <em>false</em> aborts and exits the iteration.
13468 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13470 each : function(fn, scope){
13471 this.data.each(fn, scope);
13475 * Gets all records modified since the last commit. Modified records are persisted across load operations
13476 * (e.g., during paging).
13477 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13479 getModifiedRecords : function(){
13480 return this.modified;
13484 createFilterFn : function(property, value, anyMatch){
13485 if(!value.exec){ // not a regex
13486 value = String(value);
13487 if(value.length == 0){
13490 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13492 return function(r){
13493 return value.test(r.data[property]);
13498 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13499 * @param {String} property A field on your records
13500 * @param {Number} start The record index to start at (defaults to 0)
13501 * @param {Number} end The last record index to include (defaults to length - 1)
13502 * @return {Number} The sum
13504 sum : function(property, start, end){
13505 var rs = this.data.items, v = 0;
13506 start = start || 0;
13507 end = (end || end === 0) ? end : rs.length-1;
13509 for(var i = start; i <= end; i++){
13510 v += (rs[i].data[property] || 0);
13516 * Filter the records by a specified property.
13517 * @param {String} field A field on your records
13518 * @param {String/RegExp} value Either a string that the field
13519 * should start with or a RegExp to test against the field
13520 * @param {Boolean} anyMatch True to match any part not just the beginning
13522 filter : function(property, value, anyMatch){
13523 var fn = this.createFilterFn(property, value, anyMatch);
13524 return fn ? this.filterBy(fn) : this.clearFilter();
13528 * Filter by a function. The specified function will be called with each
13529 * record in this data source. If the function returns true the record is included,
13530 * otherwise it is filtered.
13531 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13532 * @param {Object} scope (optional) The scope of the function (defaults to this)
13534 filterBy : function(fn, scope){
13535 this.snapshot = this.snapshot || this.data;
13536 this.data = this.queryBy(fn, scope||this);
13537 this.fireEvent("datachanged", this);
13541 * Query the records by a specified property.
13542 * @param {String} field A field on your records
13543 * @param {String/RegExp} value Either a string that the field
13544 * should start with or a RegExp to test against the field
13545 * @param {Boolean} anyMatch True to match any part not just the beginning
13546 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13548 query : function(property, value, anyMatch){
13549 var fn = this.createFilterFn(property, value, anyMatch);
13550 return fn ? this.queryBy(fn) : this.data.clone();
13554 * Query by a function. The specified function will be called with each
13555 * record in this data source. If the function returns true the record is included
13557 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13558 * @param {Object} scope (optional) The scope of the function (defaults to this)
13559 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13561 queryBy : function(fn, scope){
13562 var data = this.snapshot || this.data;
13563 return data.filterBy(fn, scope||this);
13567 * Collects unique values for a particular dataIndex from this store.
13568 * @param {String} dataIndex The property to collect
13569 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13570 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13571 * @return {Array} An array of the unique values
13573 collect : function(dataIndex, allowNull, bypassFilter){
13574 var d = (bypassFilter === true && this.snapshot) ?
13575 this.snapshot.items : this.data.items;
13576 var v, sv, r = [], l = {};
13577 for(var i = 0, len = d.length; i < len; i++){
13578 v = d[i].data[dataIndex];
13580 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13589 * Revert to a view of the Record cache with no filtering applied.
13590 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13592 clearFilter : function(suppressEvent){
13593 if(this.snapshot && this.snapshot != this.data){
13594 this.data = this.snapshot;
13595 delete this.snapshot;
13596 if(suppressEvent !== true){
13597 this.fireEvent("datachanged", this);
13603 afterEdit : function(record){
13604 if(this.modified.indexOf(record) == -1){
13605 this.modified.push(record);
13607 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13611 afterReject : function(record){
13612 this.modified.remove(record);
13613 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13617 afterCommit : function(record){
13618 this.modified.remove(record);
13619 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13623 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13624 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13626 commitChanges : function(){
13627 var m = this.modified.slice(0);
13628 this.modified = [];
13629 for(var i = 0, len = m.length; i < len; i++){
13635 * Cancel outstanding changes on all changed records.
13637 rejectChanges : function(){
13638 var m = this.modified.slice(0);
13639 this.modified = [];
13640 for(var i = 0, len = m.length; i < len; i++){
13645 onMetaChange : function(meta, rtype, o){
13646 this.recordType = rtype;
13647 this.fields = rtype.prototype.fields;
13648 delete this.snapshot;
13649 this.sortInfo = meta.sortInfo || this.sortInfo;
13650 this.modified = [];
13651 this.fireEvent('metachange', this, this.reader.meta);
13654 moveIndex : function(data, type)
13656 var index = this.indexOf(data);
13658 var newIndex = index + type;
13662 this.insert(newIndex, data);
13667 * Ext JS Library 1.1.1
13668 * Copyright(c) 2006-2007, Ext JS, LLC.
13670 * Originally Released Under LGPL - original licence link has changed is not relivant.
13673 * <script type="text/javascript">
13677 * @class Roo.data.SimpleStore
13678 * @extends Roo.data.Store
13679 * Small helper class to make creating Stores from Array data easier.
13680 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13681 * @cfg {Array} fields An array of field definition objects, or field name strings.
13682 * @cfg {Object} an existing reader (eg. copied from another store)
13683 * @cfg {Array} data The multi-dimensional array of data
13685 * @param {Object} config
13687 Roo.data.SimpleStore = function(config)
13689 Roo.data.SimpleStore.superclass.constructor.call(this, {
13691 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13694 Roo.data.Record.create(config.fields)
13696 proxy : new Roo.data.MemoryProxy(config.data)
13700 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13702 * Ext JS Library 1.1.1
13703 * Copyright(c) 2006-2007, Ext JS, LLC.
13705 * Originally Released Under LGPL - original licence link has changed is not relivant.
13708 * <script type="text/javascript">
13713 * @extends Roo.data.Store
13714 * @class Roo.data.JsonStore
13715 * Small helper class to make creating Stores for JSON data easier. <br/>
13717 var store = new Roo.data.JsonStore({
13718 url: 'get-images.php',
13720 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13723 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13724 * JsonReader and HttpProxy (unless inline data is provided).</b>
13725 * @cfg {Array} fields An array of field definition objects, or field name strings.
13727 * @param {Object} config
13729 Roo.data.JsonStore = function(c){
13730 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13731 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13732 reader: new Roo.data.JsonReader(c, c.fields)
13735 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13737 * Ext JS Library 1.1.1
13738 * Copyright(c) 2006-2007, Ext JS, LLC.
13740 * Originally Released Under LGPL - original licence link has changed is not relivant.
13743 * <script type="text/javascript">
13747 Roo.data.Field = function(config){
13748 if(typeof config == "string"){
13749 config = {name: config};
13751 Roo.apply(this, config);
13754 this.type = "auto";
13757 var st = Roo.data.SortTypes;
13758 // named sortTypes are supported, here we look them up
13759 if(typeof this.sortType == "string"){
13760 this.sortType = st[this.sortType];
13763 // set default sortType for strings and dates
13764 if(!this.sortType){
13767 this.sortType = st.asUCString;
13770 this.sortType = st.asDate;
13773 this.sortType = st.none;
13778 var stripRe = /[\$,%]/g;
13780 // prebuilt conversion function for this field, instead of
13781 // switching every time we're reading a value
13783 var cv, dateFormat = this.dateFormat;
13788 cv = function(v){ return v; };
13791 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13795 return v !== undefined && v !== null && v !== '' ?
13796 parseInt(String(v).replace(stripRe, ""), 10) : '';
13801 return v !== undefined && v !== null && v !== '' ?
13802 parseFloat(String(v).replace(stripRe, ""), 10) : '';
13807 cv = function(v){ return v === true || v === "true" || v == 1; };
13814 if(v instanceof Date){
13818 if(dateFormat == "timestamp"){
13819 return new Date(v*1000);
13821 return Date.parseDate(v, dateFormat);
13823 var parsed = Date.parse(v);
13824 return parsed ? new Date(parsed) : null;
13833 Roo.data.Field.prototype = {
13841 * Ext JS Library 1.1.1
13842 * Copyright(c) 2006-2007, Ext JS, LLC.
13844 * Originally Released Under LGPL - original licence link has changed is not relivant.
13847 * <script type="text/javascript">
13850 // Base class for reading structured data from a data source. This class is intended to be
13851 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13854 * @class Roo.data.DataReader
13855 * Base class for reading structured data from a data source. This class is intended to be
13856 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13859 Roo.data.DataReader = function(meta, recordType){
13863 this.recordType = recordType instanceof Array ?
13864 Roo.data.Record.create(recordType) : recordType;
13867 Roo.data.DataReader.prototype = {
13870 readerType : 'Data',
13872 * Create an empty record
13873 * @param {Object} data (optional) - overlay some values
13874 * @return {Roo.data.Record} record created.
13876 newRow : function(d) {
13878 this.recordType.prototype.fields.each(function(c) {
13880 case 'int' : da[c.name] = 0; break;
13881 case 'date' : da[c.name] = new Date(); break;
13882 case 'float' : da[c.name] = 0.0; break;
13883 case 'boolean' : da[c.name] = false; break;
13884 default : da[c.name] = ""; break;
13888 return new this.recordType(Roo.apply(da, d));
13894 * Ext JS Library 1.1.1
13895 * Copyright(c) 2006-2007, Ext JS, LLC.
13897 * Originally Released Under LGPL - original licence link has changed is not relivant.
13900 * <script type="text/javascript">
13904 * @class Roo.data.DataProxy
13905 * @extends Roo.data.Observable
13906 * This class is an abstract base class for implementations which provide retrieval of
13907 * unformatted data objects.<br>
13909 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13910 * (of the appropriate type which knows how to parse the data object) to provide a block of
13911 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13913 * Custom implementations must implement the load method as described in
13914 * {@link Roo.data.HttpProxy#load}.
13916 Roo.data.DataProxy = function(){
13919 * @event beforeload
13920 * Fires before a network request is made to retrieve a data object.
13921 * @param {Object} This DataProxy object.
13922 * @param {Object} params The params parameter to the load function.
13927 * Fires before the load method's callback is called.
13928 * @param {Object} This DataProxy object.
13929 * @param {Object} o The data object.
13930 * @param {Object} arg The callback argument object passed to the load function.
13934 * @event loadexception
13935 * Fires if an Exception occurs during data retrieval.
13936 * @param {Object} This DataProxy object.
13937 * @param {Object} o The data object.
13938 * @param {Object} arg The callback argument object passed to the load function.
13939 * @param {Object} e The Exception.
13941 loadexception : true
13943 Roo.data.DataProxy.superclass.constructor.call(this);
13946 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13949 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13953 * Ext JS Library 1.1.1
13954 * Copyright(c) 2006-2007, Ext JS, LLC.
13956 * Originally Released Under LGPL - original licence link has changed is not relivant.
13959 * <script type="text/javascript">
13962 * @class Roo.data.MemoryProxy
13963 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13964 * to the Reader when its load method is called.
13966 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13968 Roo.data.MemoryProxy = function(data){
13972 Roo.data.MemoryProxy.superclass.constructor.call(this);
13976 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13979 * Load data from the requested source (in this case an in-memory
13980 * data object passed to the constructor), read the data object into
13981 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13982 * process that block using the passed callback.
13983 * @param {Object} params This parameter is not used by the MemoryProxy class.
13984 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13985 * object into a block of Roo.data.Records.
13986 * @param {Function} callback The function into which to pass the block of Roo.data.records.
13987 * The function must be passed <ul>
13988 * <li>The Record block object</li>
13989 * <li>The "arg" argument from the load function</li>
13990 * <li>A boolean success indicator</li>
13992 * @param {Object} scope The scope in which to call the callback
13993 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13995 load : function(params, reader, callback, scope, arg){
13996 params = params || {};
13999 result = reader.readRecords(params.data ? params.data :this.data);
14001 this.fireEvent("loadexception", this, arg, null, e);
14002 callback.call(scope, null, arg, false);
14005 callback.call(scope, result, arg, true);
14009 update : function(params, records){
14014 * Ext JS Library 1.1.1
14015 * Copyright(c) 2006-2007, Ext JS, LLC.
14017 * Originally Released Under LGPL - original licence link has changed is not relivant.
14020 * <script type="text/javascript">
14023 * @class Roo.data.HttpProxy
14024 * @extends Roo.data.DataProxy
14025 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14026 * configured to reference a certain URL.<br><br>
14028 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14029 * from which the running page was served.<br><br>
14031 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14033 * Be aware that to enable the browser to parse an XML document, the server must set
14034 * the Content-Type header in the HTTP response to "text/xml".
14036 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14037 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14038 * will be used to make the request.
14040 Roo.data.HttpProxy = function(conn){
14041 Roo.data.HttpProxy.superclass.constructor.call(this);
14042 // is conn a conn config or a real conn?
14044 this.useAjax = !conn || !conn.events;
14048 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14049 // thse are take from connection...
14052 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14055 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14056 * extra parameters to each request made by this object. (defaults to undefined)
14059 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14060 * to each request made by this object. (defaults to undefined)
14063 * @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)
14066 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14069 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14075 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14079 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14080 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14081 * a finer-grained basis than the DataProxy events.
14083 getConnection : function(){
14084 return this.useAjax ? Roo.Ajax : this.conn;
14088 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14089 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14090 * process that block using the passed callback.
14091 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14092 * for the request to the remote server.
14093 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14094 * object into a block of Roo.data.Records.
14095 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14096 * The function must be passed <ul>
14097 * <li>The Record block object</li>
14098 * <li>The "arg" argument from the load function</li>
14099 * <li>A boolean success indicator</li>
14101 * @param {Object} scope The scope in which to call the callback
14102 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14104 load : function(params, reader, callback, scope, arg){
14105 if(this.fireEvent("beforeload", this, params) !== false){
14107 params : params || {},
14109 callback : callback,
14114 callback : this.loadResponse,
14118 Roo.applyIf(o, this.conn);
14119 if(this.activeRequest){
14120 Roo.Ajax.abort(this.activeRequest);
14122 this.activeRequest = Roo.Ajax.request(o);
14124 this.conn.request(o);
14127 callback.call(scope||this, null, arg, false);
14132 loadResponse : function(o, success, response){
14133 delete this.activeRequest;
14135 this.fireEvent("loadexception", this, o, response);
14136 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14141 result = o.reader.read(response);
14143 this.fireEvent("loadexception", this, o, response, e);
14144 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14148 this.fireEvent("load", this, o, o.request.arg);
14149 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14153 update : function(dataSet){
14158 updateResponse : function(dataSet){
14163 * Ext JS Library 1.1.1
14164 * Copyright(c) 2006-2007, Ext JS, LLC.
14166 * Originally Released Under LGPL - original licence link has changed is not relivant.
14169 * <script type="text/javascript">
14173 * @class Roo.data.ScriptTagProxy
14174 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14175 * other than the originating domain of the running page.<br><br>
14177 * <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
14178 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14180 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14181 * source code that is used as the source inside a <script> tag.<br><br>
14183 * In order for the browser to process the returned data, the server must wrap the data object
14184 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14185 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14186 * depending on whether the callback name was passed:
14189 boolean scriptTag = false;
14190 String cb = request.getParameter("callback");
14193 response.setContentType("text/javascript");
14195 response.setContentType("application/x-json");
14197 Writer out = response.getWriter();
14199 out.write(cb + "(");
14201 out.print(dataBlock.toJsonString());
14208 * @param {Object} config A configuration object.
14210 Roo.data.ScriptTagProxy = function(config){
14211 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14212 Roo.apply(this, config);
14213 this.head = document.getElementsByTagName("head")[0];
14216 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14218 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14220 * @cfg {String} url The URL from which to request the data object.
14223 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14227 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14228 * the server the name of the callback function set up by the load call to process the returned data object.
14229 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14230 * javascript output which calls this named function passing the data object as its only parameter.
14232 callbackParam : "callback",
14234 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14235 * name to the request.
14240 * Load data from the configured URL, read the data object into
14241 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14242 * process that block using the passed callback.
14243 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14244 * for the request to the remote server.
14245 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14246 * object into a block of Roo.data.Records.
14247 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14248 * The function must be passed <ul>
14249 * <li>The Record block object</li>
14250 * <li>The "arg" argument from the load function</li>
14251 * <li>A boolean success indicator</li>
14253 * @param {Object} scope The scope in which to call the callback
14254 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14256 load : function(params, reader, callback, scope, arg){
14257 if(this.fireEvent("beforeload", this, params) !== false){
14259 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14261 var url = this.url;
14262 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14264 url += "&_dc=" + (new Date().getTime());
14266 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14269 cb : "stcCallback"+transId,
14270 scriptId : "stcScript"+transId,
14274 callback : callback,
14280 window[trans.cb] = function(o){
14281 conn.handleResponse(o, trans);
14284 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14286 if(this.autoAbort !== false){
14290 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14292 var script = document.createElement("script");
14293 script.setAttribute("src", url);
14294 script.setAttribute("type", "text/javascript");
14295 script.setAttribute("id", trans.scriptId);
14296 this.head.appendChild(script);
14298 this.trans = trans;
14300 callback.call(scope||this, null, arg, false);
14305 isLoading : function(){
14306 return this.trans ? true : false;
14310 * Abort the current server request.
14312 abort : function(){
14313 if(this.isLoading()){
14314 this.destroyTrans(this.trans);
14319 destroyTrans : function(trans, isLoaded){
14320 this.head.removeChild(document.getElementById(trans.scriptId));
14321 clearTimeout(trans.timeoutId);
14323 window[trans.cb] = undefined;
14325 delete window[trans.cb];
14328 // if hasn't been loaded, wait for load to remove it to prevent script error
14329 window[trans.cb] = function(){
14330 window[trans.cb] = undefined;
14332 delete window[trans.cb];
14339 handleResponse : function(o, trans){
14340 this.trans = false;
14341 this.destroyTrans(trans, true);
14344 result = trans.reader.readRecords(o);
14346 this.fireEvent("loadexception", this, o, trans.arg, e);
14347 trans.callback.call(trans.scope||window, null, trans.arg, false);
14350 this.fireEvent("load", this, o, trans.arg);
14351 trans.callback.call(trans.scope||window, result, trans.arg, true);
14355 handleFailure : function(trans){
14356 this.trans = false;
14357 this.destroyTrans(trans, false);
14358 this.fireEvent("loadexception", this, null, trans.arg);
14359 trans.callback.call(trans.scope||window, null, trans.arg, false);
14363 * Ext JS Library 1.1.1
14364 * Copyright(c) 2006-2007, Ext JS, LLC.
14366 * Originally Released Under LGPL - original licence link has changed is not relivant.
14369 * <script type="text/javascript">
14373 * @class Roo.data.JsonReader
14374 * @extends Roo.data.DataReader
14375 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14376 * based on mappings in a provided Roo.data.Record constructor.
14378 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14379 * in the reply previously.
14384 var RecordDef = Roo.data.Record.create([
14385 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14386 {name: 'occupation'} // This field will use "occupation" as the mapping.
14388 var myReader = new Roo.data.JsonReader({
14389 totalProperty: "results", // The property which contains the total dataset size (optional)
14390 root: "rows", // The property which contains an Array of row objects
14391 id: "id" // The property within each row object that provides an ID for the record (optional)
14395 * This would consume a JSON file like this:
14397 { 'results': 2, 'rows': [
14398 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14399 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14402 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14403 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14404 * paged from the remote server.
14405 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14406 * @cfg {String} root name of the property which contains the Array of row objects.
14407 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14408 * @cfg {Array} fields Array of field definition objects
14410 * Create a new JsonReader
14411 * @param {Object} meta Metadata configuration options
14412 * @param {Object} recordType Either an Array of field definition objects,
14413 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14415 Roo.data.JsonReader = function(meta, recordType){
14418 // set some defaults:
14419 Roo.applyIf(meta, {
14420 totalProperty: 'total',
14421 successProperty : 'success',
14426 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14428 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14430 readerType : 'Json',
14433 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14434 * Used by Store query builder to append _requestMeta to params.
14437 metaFromRemote : false,
14439 * This method is only used by a DataProxy which has retrieved data from a remote server.
14440 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14441 * @return {Object} data A data block which is used by an Roo.data.Store object as
14442 * a cache of Roo.data.Records.
14444 read : function(response){
14445 var json = response.responseText;
14447 var o = /* eval:var:o */ eval("("+json+")");
14449 throw {message: "JsonReader.read: Json object not found"};
14455 this.metaFromRemote = true;
14456 this.meta = o.metaData;
14457 this.recordType = Roo.data.Record.create(o.metaData.fields);
14458 this.onMetaChange(this.meta, this.recordType, o);
14460 return this.readRecords(o);
14463 // private function a store will implement
14464 onMetaChange : function(meta, recordType, o){
14471 simpleAccess: function(obj, subsc) {
14478 getJsonAccessor: function(){
14480 return function(expr) {
14482 return(re.test(expr))
14483 ? new Function("obj", "return obj." + expr)
14488 return Roo.emptyFn;
14493 * Create a data block containing Roo.data.Records from an XML document.
14494 * @param {Object} o An object which contains an Array of row objects in the property specified
14495 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14496 * which contains the total size of the dataset.
14497 * @return {Object} data A data block which is used by an Roo.data.Store object as
14498 * a cache of Roo.data.Records.
14500 readRecords : function(o){
14502 * After any data loads, the raw JSON data is available for further custom processing.
14506 var s = this.meta, Record = this.recordType,
14507 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14509 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14511 if(s.totalProperty) {
14512 this.getTotal = this.getJsonAccessor(s.totalProperty);
14514 if(s.successProperty) {
14515 this.getSuccess = this.getJsonAccessor(s.successProperty);
14517 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14519 var g = this.getJsonAccessor(s.id);
14520 this.getId = function(rec) {
14522 return (r === undefined || r === "") ? null : r;
14525 this.getId = function(){return null;};
14528 for(var jj = 0; jj < fl; jj++){
14530 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14531 this.ef[jj] = this.getJsonAccessor(map);
14535 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14536 if(s.totalProperty){
14537 var vt = parseInt(this.getTotal(o), 10);
14542 if(s.successProperty){
14543 var vs = this.getSuccess(o);
14544 if(vs === false || vs === 'false'){
14549 for(var i = 0; i < c; i++){
14552 var id = this.getId(n);
14553 for(var j = 0; j < fl; j++){
14555 var v = this.ef[j](n);
14557 Roo.log('missing convert for ' + f.name);
14561 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14563 var record = new Record(values, id);
14565 records[i] = record;
14571 totalRecords : totalRecords
14574 // used when loading children.. @see loadDataFromChildren
14575 toLoadData: function(rec)
14577 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14578 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14579 return { data : data, total : data.length };
14584 * Ext JS Library 1.1.1
14585 * Copyright(c) 2006-2007, Ext JS, LLC.
14587 * Originally Released Under LGPL - original licence link has changed is not relivant.
14590 * <script type="text/javascript">
14594 * @class Roo.data.ArrayReader
14595 * @extends Roo.data.DataReader
14596 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14597 * Each element of that Array represents a row of data fields. The
14598 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14599 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14603 var RecordDef = Roo.data.Record.create([
14604 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14605 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14607 var myReader = new Roo.data.ArrayReader({
14608 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14612 * This would consume an Array like this:
14614 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14618 * Create a new JsonReader
14619 * @param {Object} meta Metadata configuration options.
14620 * @param {Object|Array} recordType Either an Array of field definition objects
14622 * @cfg {Array} fields Array of field definition objects
14623 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14624 * as specified to {@link Roo.data.Record#create},
14625 * or an {@link Roo.data.Record} object
14628 * created using {@link Roo.data.Record#create}.
14630 Roo.data.ArrayReader = function(meta, recordType)
14632 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14635 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14638 * Create a data block containing Roo.data.Records from an XML document.
14639 * @param {Object} o An Array of row objects which represents the dataset.
14640 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14641 * a cache of Roo.data.Records.
14643 readRecords : function(o)
14645 var sid = this.meta ? this.meta.id : null;
14646 var recordType = this.recordType, fields = recordType.prototype.fields;
14649 for(var i = 0; i < root.length; i++){
14652 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14653 for(var j = 0, jlen = fields.length; j < jlen; j++){
14654 var f = fields.items[j];
14655 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14656 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14658 values[f.name] = v;
14660 var record = new recordType(values, id);
14662 records[records.length] = record;
14666 totalRecords : records.length
14669 // used when loading children.. @see loadDataFromChildren
14670 toLoadData: function(rec)
14672 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14673 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14684 * @class Roo.bootstrap.ComboBox
14685 * @extends Roo.bootstrap.TriggerField
14686 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14687 * @cfg {Boolean} append (true|false) default false
14688 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14689 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14690 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14691 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14692 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14693 * @cfg {Boolean} animate default true
14694 * @cfg {Boolean} emptyResultText only for touch device
14695 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14696 * @cfg {String} emptyTitle default ''
14698 * Create a new ComboBox.
14699 * @param {Object} config Configuration options
14701 Roo.bootstrap.ComboBox = function(config){
14702 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14706 * Fires when the dropdown list is expanded
14707 * @param {Roo.bootstrap.ComboBox} combo This combo box
14712 * Fires when the dropdown list is collapsed
14713 * @param {Roo.bootstrap.ComboBox} combo This combo box
14717 * @event beforeselect
14718 * Fires before a list item is selected. Return false to cancel the selection.
14719 * @param {Roo.bootstrap.ComboBox} combo This combo box
14720 * @param {Roo.data.Record} record The data record returned from the underlying store
14721 * @param {Number} index The index of the selected item in the dropdown list
14723 'beforeselect' : true,
14726 * Fires when a list item is selected
14727 * @param {Roo.bootstrap.ComboBox} combo This combo box
14728 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14729 * @param {Number} index The index of the selected item in the dropdown list
14733 * @event beforequery
14734 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14735 * The event object passed has these properties:
14736 * @param {Roo.bootstrap.ComboBox} combo This combo box
14737 * @param {String} query The query
14738 * @param {Boolean} forceAll true to force "all" query
14739 * @param {Boolean} cancel true to cancel the query
14740 * @param {Object} e The query event object
14742 'beforequery': true,
14745 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14746 * @param {Roo.bootstrap.ComboBox} combo This combo box
14751 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14752 * @param {Roo.bootstrap.ComboBox} combo This combo box
14753 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14758 * Fires when the remove value from the combobox array
14759 * @param {Roo.bootstrap.ComboBox} combo This combo box
14763 * @event afterremove
14764 * Fires when the remove value from the combobox array
14765 * @param {Roo.bootstrap.ComboBox} combo This combo box
14767 'afterremove' : true,
14769 * @event specialfilter
14770 * Fires when specialfilter
14771 * @param {Roo.bootstrap.ComboBox} combo This combo box
14773 'specialfilter' : true,
14776 * Fires when tick the element
14777 * @param {Roo.bootstrap.ComboBox} combo This combo box
14781 * @event touchviewdisplay
14782 * Fires when touch view require special display (default is using displayField)
14783 * @param {Roo.bootstrap.ComboBox} combo This combo box
14784 * @param {Object} cfg set html .
14786 'touchviewdisplay' : true
14791 this.tickItems = [];
14793 this.selectedIndex = -1;
14794 if(this.mode == 'local'){
14795 if(config.queryDelay === undefined){
14796 this.queryDelay = 10;
14798 if(config.minChars === undefined){
14804 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14807 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14808 * rendering into an Roo.Editor, defaults to false)
14811 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14812 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14815 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14818 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14819 * the dropdown list (defaults to undefined, with no header element)
14823 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
14827 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14829 listWidth: undefined,
14831 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14832 * mode = 'remote' or 'text' if mode = 'local')
14834 displayField: undefined,
14837 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14838 * mode = 'remote' or 'value' if mode = 'local').
14839 * Note: use of a valueField requires the user make a selection
14840 * in order for a value to be mapped.
14842 valueField: undefined,
14844 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14849 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14850 * field's data value (defaults to the underlying DOM element's name)
14852 hiddenName: undefined,
14854 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14858 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14860 selectedClass: 'active',
14863 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14867 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14868 * anchor positions (defaults to 'tl-bl')
14870 listAlign: 'tl-bl?',
14872 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14876 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
14877 * query specified by the allQuery config option (defaults to 'query')
14879 triggerAction: 'query',
14881 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14882 * (defaults to 4, does not apply if editable = false)
14886 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14887 * delay (typeAheadDelay) if it matches a known value (defaults to false)
14891 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14892 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14896 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14897 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
14901 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
14902 * when editable = true (defaults to false)
14904 selectOnFocus:false,
14906 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14908 queryParam: 'query',
14910 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
14911 * when mode = 'remote' (defaults to 'Loading...')
14913 loadingText: 'Loading...',
14915 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14919 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14923 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14924 * traditional select (defaults to true)
14928 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14932 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14936 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14937 * listWidth has a higher value)
14941 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14942 * allow the user to set arbitrary text into the field (defaults to false)
14944 forceSelection:false,
14946 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14947 * if typeAhead = true (defaults to 250)
14949 typeAheadDelay : 250,
14951 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14952 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14954 valueNotFoundText : undefined,
14956 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14958 blockFocus : false,
14961 * @cfg {Boolean} disableClear Disable showing of clear button.
14963 disableClear : false,
14965 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
14967 alwaysQuery : false,
14970 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
14975 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14977 invalidClass : "has-warning",
14980 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14982 validClass : "has-success",
14985 * @cfg {Boolean} specialFilter (true|false) special filter default false
14987 specialFilter : false,
14990 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14992 mobileTouchView : true,
14995 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14997 useNativeIOS : false,
15000 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15002 mobile_restrict_height : false,
15004 ios_options : false,
15016 btnPosition : 'right',
15017 triggerList : true,
15018 showToggleBtn : true,
15020 emptyResultText: 'Empty',
15021 triggerText : 'Select',
15024 // element that contains real text value.. (when hidden is used..)
15026 getAutoCreate : function()
15031 * Render classic select for iso
15034 if(Roo.isIOS && this.useNativeIOS){
15035 cfg = this.getAutoCreateNativeIOS();
15043 if(Roo.isTouch && this.mobileTouchView){
15044 cfg = this.getAutoCreateTouchView();
15051 if(!this.tickable){
15052 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15057 * ComboBox with tickable selections
15060 var align = this.labelAlign || this.parentLabelAlign();
15063 cls : 'form-group roo-combobox-tickable' //input-group
15066 var btn_text_select = '';
15067 var btn_text_done = '';
15068 var btn_text_cancel = '';
15070 if (this.btn_text_show) {
15071 btn_text_select = 'Select';
15072 btn_text_done = 'Done';
15073 btn_text_cancel = 'Cancel';
15078 cls : 'tickable-buttons',
15083 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15084 //html : this.triggerText
15085 html: btn_text_select
15091 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15093 html: btn_text_done
15099 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15101 html: btn_text_cancel
15107 buttons.cn.unshift({
15109 cls: 'roo-select2-search-field-input'
15115 Roo.each(buttons.cn, function(c){
15117 c.cls += ' btn-' + _this.size;
15120 if (_this.disabled) {
15127 style : 'display: contents',
15132 cls: 'form-hidden-field'
15136 cls: 'roo-select2-choices',
15140 cls: 'roo-select2-search-field',
15151 cls: 'roo-select2-container input-group roo-select2-container-multi',
15157 // cls: 'typeahead typeahead-long dropdown-menu',
15158 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15163 if(this.hasFeedback && !this.allowBlank){
15167 cls: 'glyphicon form-control-feedback'
15170 combobox.cn.push(feedback);
15177 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15178 tooltip : 'This field is required'
15180 if (Roo.bootstrap.version == 4) {
15183 style : 'display:none'
15186 if (align ==='left' && this.fieldLabel.length) {
15188 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15195 cls : 'control-label col-form-label',
15196 html : this.fieldLabel
15208 var labelCfg = cfg.cn[1];
15209 var contentCfg = cfg.cn[2];
15212 if(this.indicatorpos == 'right'){
15218 cls : 'control-label col-form-label',
15222 html : this.fieldLabel
15238 labelCfg = cfg.cn[0];
15239 contentCfg = cfg.cn[1];
15243 if(this.labelWidth > 12){
15244 labelCfg.style = "width: " + this.labelWidth + 'px';
15247 if(this.labelWidth < 13 && this.labelmd == 0){
15248 this.labelmd = this.labelWidth;
15251 if(this.labellg > 0){
15252 labelCfg.cls += ' col-lg-' + this.labellg;
15253 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15256 if(this.labelmd > 0){
15257 labelCfg.cls += ' col-md-' + this.labelmd;
15258 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15261 if(this.labelsm > 0){
15262 labelCfg.cls += ' col-sm-' + this.labelsm;
15263 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15266 if(this.labelxs > 0){
15267 labelCfg.cls += ' col-xs-' + this.labelxs;
15268 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15272 } else if ( this.fieldLabel.length) {
15273 // Roo.log(" label");
15278 //cls : 'input-group-addon',
15279 html : this.fieldLabel
15284 if(this.indicatorpos == 'right'){
15288 //cls : 'input-group-addon',
15289 html : this.fieldLabel
15299 // Roo.log(" no label && no align");
15306 ['xs','sm','md','lg'].map(function(size){
15307 if (settings[size]) {
15308 cfg.cls += ' col-' + size + '-' + settings[size];
15316 _initEventsCalled : false,
15319 initEvents: function()
15321 if (this._initEventsCalled) { // as we call render... prevent looping...
15324 this._initEventsCalled = true;
15327 throw "can not find store for combo";
15330 this.indicator = this.indicatorEl();
15332 this.store = Roo.factory(this.store, Roo.data);
15333 this.store.parent = this;
15335 // if we are building from html. then this element is so complex, that we can not really
15336 // use the rendered HTML.
15337 // so we have to trash and replace the previous code.
15338 if (Roo.XComponent.build_from_html) {
15339 // remove this element....
15340 var e = this.el.dom, k=0;
15341 while (e ) { e = e.previousSibling; ++k;}
15346 this.rendered = false;
15348 this.render(this.parent().getChildContainer(true), k);
15351 if(Roo.isIOS && this.useNativeIOS){
15352 this.initIOSView();
15360 if(Roo.isTouch && this.mobileTouchView){
15361 this.initTouchView();
15366 this.initTickableEvents();
15370 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15372 if(this.hiddenName){
15374 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15376 this.hiddenField.dom.value =
15377 this.hiddenValue !== undefined ? this.hiddenValue :
15378 this.value !== undefined ? this.value : '';
15380 // prevent input submission
15381 this.el.dom.removeAttribute('name');
15382 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15387 // this.el.dom.setAttribute('autocomplete', 'off');
15390 var cls = 'x-combo-list';
15392 //this.list = new Roo.Layer({
15393 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15399 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15400 _this.list.setWidth(lw);
15403 this.list.on('mouseover', this.onViewOver, this);
15404 this.list.on('mousemove', this.onViewMove, this);
15405 this.list.on('scroll', this.onViewScroll, this);
15408 this.list.swallowEvent('mousewheel');
15409 this.assetHeight = 0;
15412 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15413 this.assetHeight += this.header.getHeight();
15416 this.innerList = this.list.createChild({cls:cls+'-inner'});
15417 this.innerList.on('mouseover', this.onViewOver, this);
15418 this.innerList.on('mousemove', this.onViewMove, this);
15419 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15421 if(this.allowBlank && !this.pageSize && !this.disableClear){
15422 this.footer = this.list.createChild({cls:cls+'-ft'});
15423 this.pageTb = new Roo.Toolbar(this.footer);
15427 this.footer = this.list.createChild({cls:cls+'-ft'});
15428 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15429 {pageSize: this.pageSize});
15433 if (this.pageTb && this.allowBlank && !this.disableClear) {
15435 this.pageTb.add(new Roo.Toolbar.Fill(), {
15436 cls: 'x-btn-icon x-btn-clear',
15438 handler: function()
15441 _this.clearValue();
15442 _this.onSelect(false, -1);
15447 this.assetHeight += this.footer.getHeight();
15452 this.tpl = Roo.bootstrap.version == 4 ?
15453 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15454 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15457 this.view = new Roo.View(this.list, this.tpl, {
15458 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15460 //this.view.wrapEl.setDisplayed(false);
15461 this.view.on('click', this.onViewClick, this);
15464 this.store.on('beforeload', this.onBeforeLoad, this);
15465 this.store.on('load', this.onLoad, this);
15466 this.store.on('loadexception', this.onLoadException, this);
15468 if(this.resizable){
15469 this.resizer = new Roo.Resizable(this.list, {
15470 pinned:true, handles:'se'
15472 this.resizer.on('resize', function(r, w, h){
15473 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15474 this.listWidth = w;
15475 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15476 this.restrictHeight();
15478 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15481 if(!this.editable){
15482 this.editable = true;
15483 this.setEditable(false);
15488 if (typeof(this.events.add.listeners) != 'undefined') {
15490 this.addicon = this.wrap.createChild(
15491 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15493 this.addicon.on('click', function(e) {
15494 this.fireEvent('add', this);
15497 if (typeof(this.events.edit.listeners) != 'undefined') {
15499 this.editicon = this.wrap.createChild(
15500 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15501 if (this.addicon) {
15502 this.editicon.setStyle('margin-left', '40px');
15504 this.editicon.on('click', function(e) {
15506 // we fire even if inothing is selected..
15507 this.fireEvent('edit', this, this.lastData );
15513 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15514 "up" : function(e){
15515 this.inKeyMode = true;
15519 "down" : function(e){
15520 if(!this.isExpanded()){
15521 this.onTriggerClick();
15523 this.inKeyMode = true;
15528 "enter" : function(e){
15529 // this.onViewClick();
15533 if(this.fireEvent("specialkey", this, e)){
15534 this.onViewClick(false);
15540 "esc" : function(e){
15544 "tab" : function(e){
15547 if(this.fireEvent("specialkey", this, e)){
15548 this.onViewClick(false);
15556 doRelay : function(foo, bar, hname){
15557 if(hname == 'down' || this.scope.isExpanded()){
15558 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15567 this.queryDelay = Math.max(this.queryDelay || 10,
15568 this.mode == 'local' ? 10 : 250);
15571 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15573 if(this.typeAhead){
15574 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15576 if(this.editable !== false){
15577 this.inputEl().on("keyup", this.onKeyUp, this);
15579 if(this.forceSelection){
15580 this.inputEl().on('blur', this.doForce, this);
15584 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15585 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15589 initTickableEvents: function()
15593 if(this.hiddenName){
15595 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15597 this.hiddenField.dom.value =
15598 this.hiddenValue !== undefined ? this.hiddenValue :
15599 this.value !== undefined ? this.value : '';
15601 // prevent input submission
15602 this.el.dom.removeAttribute('name');
15603 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15608 // this.list = this.el.select('ul.dropdown-menu',true).first();
15610 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15611 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15612 if(this.triggerList){
15613 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15616 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15617 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15619 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15620 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15622 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15623 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15625 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15626 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15627 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15630 this.cancelBtn.hide();
15635 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15636 _this.list.setWidth(lw);
15639 this.list.on('mouseover', this.onViewOver, this);
15640 this.list.on('mousemove', this.onViewMove, this);
15642 this.list.on('scroll', this.onViewScroll, this);
15645 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15646 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15649 this.view = new Roo.View(this.list, this.tpl, {
15654 selectedClass: this.selectedClass
15657 //this.view.wrapEl.setDisplayed(false);
15658 this.view.on('click', this.onViewClick, this);
15662 this.store.on('beforeload', this.onBeforeLoad, this);
15663 this.store.on('load', this.onLoad, this);
15664 this.store.on('loadexception', this.onLoadException, this);
15667 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15668 "up" : function(e){
15669 this.inKeyMode = true;
15673 "down" : function(e){
15674 this.inKeyMode = true;
15678 "enter" : function(e){
15679 if(this.fireEvent("specialkey", this, e)){
15680 this.onViewClick(false);
15686 "esc" : function(e){
15687 this.onTickableFooterButtonClick(e, false, false);
15690 "tab" : function(e){
15691 this.fireEvent("specialkey", this, e);
15693 this.onTickableFooterButtonClick(e, false, false);
15700 doRelay : function(e, fn, key){
15701 if(this.scope.isExpanded()){
15702 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15711 this.queryDelay = Math.max(this.queryDelay || 10,
15712 this.mode == 'local' ? 10 : 250);
15715 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15717 if(this.typeAhead){
15718 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15721 if(this.editable !== false){
15722 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15725 this.indicator = this.indicatorEl();
15727 if(this.indicator){
15728 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15729 this.indicator.hide();
15734 onDestroy : function(){
15736 this.view.setStore(null);
15737 this.view.el.removeAllListeners();
15738 this.view.el.remove();
15739 this.view.purgeListeners();
15742 this.list.dom.innerHTML = '';
15746 this.store.un('beforeload', this.onBeforeLoad, this);
15747 this.store.un('load', this.onLoad, this);
15748 this.store.un('loadexception', this.onLoadException, this);
15750 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15754 fireKey : function(e){
15755 if(e.isNavKeyPress() && !this.list.isVisible()){
15756 this.fireEvent("specialkey", this, e);
15761 onResize: function(w, h){
15762 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15764 // if(typeof w != 'number'){
15765 // // we do not handle it!?!?
15768 // var tw = this.trigger.getWidth();
15769 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15770 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15772 // this.inputEl().setWidth( this.adjustWidth('input', x));
15774 // //this.trigger.setStyle('left', x+'px');
15776 // if(this.list && this.listWidth === undefined){
15777 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15778 // this.list.setWidth(lw);
15779 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15787 * Allow or prevent the user from directly editing the field text. If false is passed,
15788 * the user will only be able to select from the items defined in the dropdown list. This method
15789 * is the runtime equivalent of setting the 'editable' config option at config time.
15790 * @param {Boolean} value True to allow the user to directly edit the field text
15792 setEditable : function(value){
15793 if(value == this.editable){
15796 this.editable = value;
15798 this.inputEl().dom.setAttribute('readOnly', true);
15799 this.inputEl().on('mousedown', this.onTriggerClick, this);
15800 this.inputEl().addClass('x-combo-noedit');
15802 this.inputEl().dom.setAttribute('readOnly', false);
15803 this.inputEl().un('mousedown', this.onTriggerClick, this);
15804 this.inputEl().removeClass('x-combo-noedit');
15810 onBeforeLoad : function(combo,opts){
15811 if(!this.hasFocus){
15815 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15817 this.restrictHeight();
15818 this.selectedIndex = -1;
15822 onLoad : function(){
15824 this.hasQuery = false;
15826 if(!this.hasFocus){
15830 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15831 this.loading.hide();
15834 if(this.store.getCount() > 0){
15837 this.restrictHeight();
15838 if(this.lastQuery == this.allQuery){
15839 if(this.editable && !this.tickable){
15840 this.inputEl().dom.select();
15844 !this.selectByValue(this.value, true) &&
15847 !this.store.lastOptions ||
15848 typeof(this.store.lastOptions.add) == 'undefined' ||
15849 this.store.lastOptions.add != true
15852 this.select(0, true);
15855 if(this.autoFocus){
15858 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15859 this.taTask.delay(this.typeAheadDelay);
15863 this.onEmptyResults();
15869 onLoadException : function()
15871 this.hasQuery = false;
15873 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15874 this.loading.hide();
15877 if(this.tickable && this.editable){
15882 // only causes errors at present
15883 //Roo.log(this.store.reader.jsonData);
15884 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15886 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15892 onTypeAhead : function(){
15893 if(this.store.getCount() > 0){
15894 var r = this.store.getAt(0);
15895 var newValue = r.data[this.displayField];
15896 var len = newValue.length;
15897 var selStart = this.getRawValue().length;
15899 if(selStart != len){
15900 this.setRawValue(newValue);
15901 this.selectText(selStart, newValue.length);
15907 onSelect : function(record, index){
15909 if(this.fireEvent('beforeselect', this, record, index) !== false){
15911 this.setFromData(index > -1 ? record.data : false);
15914 this.fireEvent('select', this, record, index);
15919 * Returns the currently selected field value or empty string if no value is set.
15920 * @return {String} value The selected value
15922 getValue : function()
15924 if(Roo.isIOS && this.useNativeIOS){
15925 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15929 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15932 if(this.valueField){
15933 return typeof this.value != 'undefined' ? this.value : '';
15935 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15939 getRawValue : function()
15941 if(Roo.isIOS && this.useNativeIOS){
15942 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15945 var v = this.inputEl().getValue();
15951 * Clears any text/value currently set in the field
15953 clearValue : function(){
15955 if(this.hiddenField){
15956 this.hiddenField.dom.value = '';
15959 this.setRawValue('');
15960 this.lastSelectionText = '';
15961 this.lastData = false;
15963 var close = this.closeTriggerEl();
15974 * Sets the specified value into the field. If the value finds a match, the corresponding record text
15975 * will be displayed in the field. If the value does not match the data value of an existing item,
15976 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15977 * Otherwise the field will be blank (although the value will still be set).
15978 * @param {String} value The value to match
15980 setValue : function(v)
15982 if(Roo.isIOS && this.useNativeIOS){
15983 this.setIOSValue(v);
15993 if(this.valueField){
15994 var r = this.findRecord(this.valueField, v);
15996 text = r.data[this.displayField];
15997 }else if(this.valueNotFoundText !== undefined){
15998 text = this.valueNotFoundText;
16001 this.lastSelectionText = text;
16002 if(this.hiddenField){
16003 this.hiddenField.dom.value = v;
16005 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16008 var close = this.closeTriggerEl();
16011 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16017 * @property {Object} the last set data for the element
16022 * Sets the value of the field based on a object which is related to the record format for the store.
16023 * @param {Object} value the value to set as. or false on reset?
16025 setFromData : function(o){
16032 var dv = ''; // display value
16033 var vv = ''; // value value..
16035 if (this.displayField) {
16036 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16038 // this is an error condition!!!
16039 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16042 if(this.valueField){
16043 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16046 var close = this.closeTriggerEl();
16049 if(dv.length || vv * 1 > 0){
16051 this.blockFocus=true;
16057 if(this.hiddenField){
16058 this.hiddenField.dom.value = vv;
16060 this.lastSelectionText = dv;
16061 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16065 // no hidden field.. - we store the value in 'value', but still display
16066 // display field!!!!
16067 this.lastSelectionText = dv;
16068 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16075 reset : function(){
16076 // overridden so that last data is reset..
16083 this.setValue(this.originalValue);
16084 //this.clearInvalid();
16085 this.lastData = false;
16087 this.view.clearSelections();
16093 findRecord : function(prop, value){
16095 if(this.store.getCount() > 0){
16096 this.store.each(function(r){
16097 if(r.data[prop] == value){
16107 getName: function()
16109 // returns hidden if it's set..
16110 if (!this.rendered) {return ''};
16111 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16115 onViewMove : function(e, t){
16116 this.inKeyMode = false;
16120 onViewOver : function(e, t){
16121 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16124 var item = this.view.findItemFromChild(t);
16127 var index = this.view.indexOf(item);
16128 this.select(index, false);
16133 onViewClick : function(view, doFocus, el, e)
16135 var index = this.view.getSelectedIndexes()[0];
16137 var r = this.store.getAt(index);
16141 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16148 Roo.each(this.tickItems, function(v,k){
16150 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16152 _this.tickItems.splice(k, 1);
16154 if(typeof(e) == 'undefined' && view == false){
16155 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16167 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16168 this.tickItems.push(r.data);
16171 if(typeof(e) == 'undefined' && view == false){
16172 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16179 this.onSelect(r, index);
16181 if(doFocus !== false && !this.blockFocus){
16182 this.inputEl().focus();
16187 restrictHeight : function(){
16188 //this.innerList.dom.style.height = '';
16189 //var inner = this.innerList.dom;
16190 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16191 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16192 //this.list.beginUpdate();
16193 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16194 this.list.alignTo(this.inputEl(), this.listAlign);
16195 this.list.alignTo(this.inputEl(), this.listAlign);
16196 //this.list.endUpdate();
16200 onEmptyResults : function(){
16202 if(this.tickable && this.editable){
16203 this.hasFocus = false;
16204 this.restrictHeight();
16212 * Returns true if the dropdown list is expanded, else false.
16214 isExpanded : function(){
16215 return this.list.isVisible();
16219 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16220 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16221 * @param {String} value The data value of the item to select
16222 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16223 * selected item if it is not currently in view (defaults to true)
16224 * @return {Boolean} True if the value matched an item in the list, else false
16226 selectByValue : function(v, scrollIntoView){
16227 if(v !== undefined && v !== null){
16228 var r = this.findRecord(this.valueField || this.displayField, v);
16230 this.select(this.store.indexOf(r), scrollIntoView);
16238 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16239 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16240 * @param {Number} index The zero-based index of the list item to select
16241 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16242 * selected item if it is not currently in view (defaults to true)
16244 select : function(index, scrollIntoView){
16245 this.selectedIndex = index;
16246 this.view.select(index);
16247 if(scrollIntoView !== false){
16248 var el = this.view.getNode(index);
16250 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16253 this.list.scrollChildIntoView(el, false);
16259 selectNext : function(){
16260 var ct = this.store.getCount();
16262 if(this.selectedIndex == -1){
16264 }else if(this.selectedIndex < ct-1){
16265 this.select(this.selectedIndex+1);
16271 selectPrev : function(){
16272 var ct = this.store.getCount();
16274 if(this.selectedIndex == -1){
16276 }else if(this.selectedIndex != 0){
16277 this.select(this.selectedIndex-1);
16283 onKeyUp : function(e){
16284 if(this.editable !== false && !e.isSpecialKey()){
16285 this.lastKey = e.getKey();
16286 this.dqTask.delay(this.queryDelay);
16291 validateBlur : function(){
16292 return !this.list || !this.list.isVisible();
16296 initQuery : function(){
16298 var v = this.getRawValue();
16300 if(this.tickable && this.editable){
16301 v = this.tickableInputEl().getValue();
16308 doForce : function(){
16309 if(this.inputEl().dom.value.length > 0){
16310 this.inputEl().dom.value =
16311 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16317 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16318 * query allowing the query action to be canceled if needed.
16319 * @param {String} query The SQL query to execute
16320 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16321 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16322 * saved in the current store (defaults to false)
16324 doQuery : function(q, forceAll){
16326 if(q === undefined || q === null){
16331 forceAll: forceAll,
16335 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16340 forceAll = qe.forceAll;
16341 if(forceAll === true || (q.length >= this.minChars)){
16343 this.hasQuery = true;
16345 if(this.lastQuery != q || this.alwaysQuery){
16346 this.lastQuery = q;
16347 if(this.mode == 'local'){
16348 this.selectedIndex = -1;
16350 this.store.clearFilter();
16353 if(this.specialFilter){
16354 this.fireEvent('specialfilter', this);
16359 this.store.filter(this.displayField, q);
16362 this.store.fireEvent("datachanged", this.store);
16369 this.store.baseParams[this.queryParam] = q;
16371 var options = {params : this.getParams(q)};
16374 options.add = true;
16375 options.params.start = this.page * this.pageSize;
16378 this.store.load(options);
16381 * this code will make the page width larger, at the beginning, the list not align correctly,
16382 * we should expand the list on onLoad
16383 * so command out it
16388 this.selectedIndex = -1;
16393 this.loadNext = false;
16397 getParams : function(q){
16399 //p[this.queryParam] = q;
16403 p.limit = this.pageSize;
16409 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16411 collapse : function(){
16412 if(!this.isExpanded()){
16418 this.hasFocus = false;
16422 this.cancelBtn.hide();
16423 this.trigger.show();
16426 this.tickableInputEl().dom.value = '';
16427 this.tickableInputEl().blur();
16432 Roo.get(document).un('mousedown', this.collapseIf, this);
16433 Roo.get(document).un('mousewheel', this.collapseIf, this);
16434 if (!this.editable) {
16435 Roo.get(document).un('keydown', this.listKeyPress, this);
16437 this.fireEvent('collapse', this);
16443 collapseIf : function(e){
16444 var in_combo = e.within(this.el);
16445 var in_list = e.within(this.list);
16446 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16448 if (in_combo || in_list || is_list) {
16449 //e.stopPropagation();
16454 this.onTickableFooterButtonClick(e, false, false);
16462 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16464 expand : function(){
16466 if(this.isExpanded() || !this.hasFocus){
16470 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16471 this.list.setWidth(lw);
16477 this.restrictHeight();
16481 this.tickItems = Roo.apply([], this.item);
16484 this.cancelBtn.show();
16485 this.trigger.hide();
16488 this.tickableInputEl().focus();
16493 Roo.get(document).on('mousedown', this.collapseIf, this);
16494 Roo.get(document).on('mousewheel', this.collapseIf, this);
16495 if (!this.editable) {
16496 Roo.get(document).on('keydown', this.listKeyPress, this);
16499 this.fireEvent('expand', this);
16503 // Implements the default empty TriggerField.onTriggerClick function
16504 onTriggerClick : function(e)
16506 Roo.log('trigger click');
16508 if(this.disabled || !this.triggerList){
16513 this.loadNext = false;
16515 if(this.isExpanded()){
16517 if (!this.blockFocus) {
16518 this.inputEl().focus();
16522 this.hasFocus = true;
16523 if(this.triggerAction == 'all') {
16524 this.doQuery(this.allQuery, true);
16526 this.doQuery(this.getRawValue());
16528 if (!this.blockFocus) {
16529 this.inputEl().focus();
16534 onTickableTriggerClick : function(e)
16541 this.loadNext = false;
16542 this.hasFocus = true;
16544 if(this.triggerAction == 'all') {
16545 this.doQuery(this.allQuery, true);
16547 this.doQuery(this.getRawValue());
16551 onSearchFieldClick : function(e)
16553 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16554 this.onTickableFooterButtonClick(e, false, false);
16558 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16563 this.loadNext = false;
16564 this.hasFocus = true;
16566 if(this.triggerAction == 'all') {
16567 this.doQuery(this.allQuery, true);
16569 this.doQuery(this.getRawValue());
16573 listKeyPress : function(e)
16575 //Roo.log('listkeypress');
16576 // scroll to first matching element based on key pres..
16577 if (e.isSpecialKey()) {
16580 var k = String.fromCharCode(e.getKey()).toUpperCase();
16583 var csel = this.view.getSelectedNodes();
16584 var cselitem = false;
16586 var ix = this.view.indexOf(csel[0]);
16587 cselitem = this.store.getAt(ix);
16588 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16594 this.store.each(function(v) {
16596 // start at existing selection.
16597 if (cselitem.id == v.id) {
16603 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16604 match = this.store.indexOf(v);
16610 if (match === false) {
16611 return true; // no more action?
16614 this.view.select(match);
16615 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16616 sn.scrollIntoView(sn.dom.parentNode, false);
16619 onViewScroll : function(e, t){
16621 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){
16625 this.hasQuery = true;
16627 this.loading = this.list.select('.loading', true).first();
16629 if(this.loading === null){
16630 this.list.createChild({
16632 cls: 'loading roo-select2-more-results roo-select2-active',
16633 html: 'Loading more results...'
16636 this.loading = this.list.select('.loading', true).first();
16638 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16640 this.loading.hide();
16643 this.loading.show();
16648 this.loadNext = true;
16650 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16655 addItem : function(o)
16657 var dv = ''; // display value
16659 if (this.displayField) {
16660 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16662 // this is an error condition!!!
16663 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16670 var choice = this.choices.createChild({
16672 cls: 'roo-select2-search-choice',
16681 cls: 'roo-select2-search-choice-close fa fa-times',
16686 }, this.searchField);
16688 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16690 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16698 this.inputEl().dom.value = '';
16703 onRemoveItem : function(e, _self, o)
16705 e.preventDefault();
16707 this.lastItem = Roo.apply([], this.item);
16709 var index = this.item.indexOf(o.data) * 1;
16712 Roo.log('not this item?!');
16716 this.item.splice(index, 1);
16721 this.fireEvent('remove', this, e);
16727 syncValue : function()
16729 if(!this.item.length){
16736 Roo.each(this.item, function(i){
16737 if(_this.valueField){
16738 value.push(i[_this.valueField]);
16745 this.value = value.join(',');
16747 if(this.hiddenField){
16748 this.hiddenField.dom.value = this.value;
16751 this.store.fireEvent("datachanged", this.store);
16756 clearItem : function()
16758 if(!this.multiple){
16764 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16772 if(this.tickable && !Roo.isTouch){
16773 this.view.refresh();
16777 inputEl: function ()
16779 if(Roo.isIOS && this.useNativeIOS){
16780 return this.el.select('select.roo-ios-select', true).first();
16783 if(Roo.isTouch && this.mobileTouchView){
16784 return this.el.select('input.form-control',true).first();
16788 return this.searchField;
16791 return this.el.select('input.form-control',true).first();
16794 onTickableFooterButtonClick : function(e, btn, el)
16796 e.preventDefault();
16798 this.lastItem = Roo.apply([], this.item);
16800 if(btn && btn.name == 'cancel'){
16801 this.tickItems = Roo.apply([], this.item);
16810 Roo.each(this.tickItems, function(o){
16818 validate : function()
16820 if(this.getVisibilityEl().hasClass('hidden')){
16824 var v = this.getRawValue();
16827 v = this.getValue();
16830 if(this.disabled || this.allowBlank || v.length){
16835 this.markInvalid();
16839 tickableInputEl : function()
16841 if(!this.tickable || !this.editable){
16842 return this.inputEl();
16845 return this.inputEl().select('.roo-select2-search-field-input', true).first();
16849 getAutoCreateTouchView : function()
16854 cls: 'form-group' //input-group
16860 type : this.inputType,
16861 cls : 'form-control x-combo-noedit',
16862 autocomplete: 'new-password',
16863 placeholder : this.placeholder || '',
16868 input.name = this.name;
16872 input.cls += ' input-' + this.size;
16875 if (this.disabled) {
16876 input.disabled = true;
16887 inputblock.cls += ' input-group';
16889 inputblock.cn.unshift({
16891 cls : 'input-group-addon input-group-prepend input-group-text',
16896 if(this.removable && !this.multiple){
16897 inputblock.cls += ' roo-removable';
16899 inputblock.cn.push({
16902 cls : 'roo-combo-removable-btn close'
16906 if(this.hasFeedback && !this.allowBlank){
16908 inputblock.cls += ' has-feedback';
16910 inputblock.cn.push({
16912 cls: 'glyphicon form-control-feedback'
16919 inputblock.cls += (this.before) ? '' : ' input-group';
16921 inputblock.cn.push({
16923 cls : 'input-group-addon input-group-append input-group-text',
16929 var ibwrap = inputblock;
16934 cls: 'roo-select2-choices',
16938 cls: 'roo-select2-search-field',
16951 cls: 'roo-select2-container input-group roo-touchview-combobox ',
16956 cls: 'form-hidden-field'
16962 if(!this.multiple && this.showToggleBtn){
16968 if (this.caret != false) {
16971 cls: 'fa fa-' + this.caret
16978 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16980 Roo.bootstrap.version == 3 ? caret : '',
16983 cls: 'combobox-clear',
16997 combobox.cls += ' roo-select2-container-multi';
17000 var align = this.labelAlign || this.parentLabelAlign();
17002 if (align ==='left' && this.fieldLabel.length) {
17007 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17008 tooltip : 'This field is required'
17012 cls : 'control-label col-form-label',
17013 html : this.fieldLabel
17024 var labelCfg = cfg.cn[1];
17025 var contentCfg = cfg.cn[2];
17028 if(this.indicatorpos == 'right'){
17033 cls : 'control-label col-form-label',
17037 html : this.fieldLabel
17041 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17042 tooltip : 'This field is required'
17055 labelCfg = cfg.cn[0];
17056 contentCfg = cfg.cn[1];
17061 if(this.labelWidth > 12){
17062 labelCfg.style = "width: " + this.labelWidth + 'px';
17065 if(this.labelWidth < 13 && this.labelmd == 0){
17066 this.labelmd = this.labelWidth;
17069 if(this.labellg > 0){
17070 labelCfg.cls += ' col-lg-' + this.labellg;
17071 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17074 if(this.labelmd > 0){
17075 labelCfg.cls += ' col-md-' + this.labelmd;
17076 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17079 if(this.labelsm > 0){
17080 labelCfg.cls += ' col-sm-' + this.labelsm;
17081 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17084 if(this.labelxs > 0){
17085 labelCfg.cls += ' col-xs-' + this.labelxs;
17086 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17090 } else if ( this.fieldLabel.length) {
17094 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17095 tooltip : 'This field is required'
17099 cls : 'control-label',
17100 html : this.fieldLabel
17111 if(this.indicatorpos == 'right'){
17115 cls : 'control-label',
17116 html : this.fieldLabel,
17120 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17121 tooltip : 'This field is required'
17138 var settings = this;
17140 ['xs','sm','md','lg'].map(function(size){
17141 if (settings[size]) {
17142 cfg.cls += ' col-' + size + '-' + settings[size];
17149 initTouchView : function()
17151 this.renderTouchView();
17153 this.touchViewEl.on('scroll', function(){
17154 this.el.dom.scrollTop = 0;
17157 this.originalValue = this.getValue();
17159 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17161 this.inputEl().on("click", this.showTouchView, this);
17162 if (this.triggerEl) {
17163 this.triggerEl.on("click", this.showTouchView, this);
17167 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17168 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17170 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17172 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17173 this.store.on('load', this.onTouchViewLoad, this);
17174 this.store.on('loadexception', this.onTouchViewLoadException, this);
17176 if(this.hiddenName){
17178 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17180 this.hiddenField.dom.value =
17181 this.hiddenValue !== undefined ? this.hiddenValue :
17182 this.value !== undefined ? this.value : '';
17184 this.el.dom.removeAttribute('name');
17185 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17189 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17190 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17193 if(this.removable && !this.multiple){
17194 var close = this.closeTriggerEl();
17196 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17197 close.on('click', this.removeBtnClick, this, close);
17201 * fix the bug in Safari iOS8
17203 this.inputEl().on("focus", function(e){
17204 document.activeElement.blur();
17207 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17214 renderTouchView : function()
17216 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17217 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17219 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17220 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17222 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17223 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17224 this.touchViewBodyEl.setStyle('overflow', 'auto');
17226 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17227 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17229 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17230 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17234 showTouchView : function()
17240 this.touchViewHeaderEl.hide();
17242 if(this.modalTitle.length){
17243 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17244 this.touchViewHeaderEl.show();
17247 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17248 this.touchViewEl.show();
17250 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17252 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17253 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17255 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17257 if(this.modalTitle.length){
17258 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17261 this.touchViewBodyEl.setHeight(bodyHeight);
17265 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17267 this.touchViewEl.addClass('in');
17270 if(this._touchViewMask){
17271 Roo.get(document.body).addClass("x-body-masked");
17272 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17273 this._touchViewMask.setStyle('z-index', 10000);
17274 this._touchViewMask.addClass('show');
17277 this.doTouchViewQuery();
17281 hideTouchView : function()
17283 this.touchViewEl.removeClass('in');
17287 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17289 this.touchViewEl.setStyle('display', 'none');
17292 if(this._touchViewMask){
17293 this._touchViewMask.removeClass('show');
17294 Roo.get(document.body).removeClass("x-body-masked");
17298 setTouchViewValue : function()
17305 Roo.each(this.tickItems, function(o){
17310 this.hideTouchView();
17313 doTouchViewQuery : function()
17322 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17326 if(!this.alwaysQuery || this.mode == 'local'){
17327 this.onTouchViewLoad();
17334 onTouchViewBeforeLoad : function(combo,opts)
17340 onTouchViewLoad : function()
17342 if(this.store.getCount() < 1){
17343 this.onTouchViewEmptyResults();
17347 this.clearTouchView();
17349 var rawValue = this.getRawValue();
17351 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17353 this.tickItems = [];
17355 this.store.data.each(function(d, rowIndex){
17356 var row = this.touchViewListGroup.createChild(template);
17358 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17359 row.addClass(d.data.cls);
17362 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17365 html : d.data[this.displayField]
17368 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17369 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17372 row.removeClass('selected');
17373 if(!this.multiple && this.valueField &&
17374 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17377 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17378 row.addClass('selected');
17381 if(this.multiple && this.valueField &&
17382 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17386 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17387 this.tickItems.push(d.data);
17390 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17394 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17396 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17398 if(this.modalTitle.length){
17399 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17402 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17404 if(this.mobile_restrict_height && listHeight < bodyHeight){
17405 this.touchViewBodyEl.setHeight(listHeight);
17410 if(firstChecked && listHeight > bodyHeight){
17411 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17416 onTouchViewLoadException : function()
17418 this.hideTouchView();
17421 onTouchViewEmptyResults : function()
17423 this.clearTouchView();
17425 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17427 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17431 clearTouchView : function()
17433 this.touchViewListGroup.dom.innerHTML = '';
17436 onTouchViewClick : function(e, el, o)
17438 e.preventDefault();
17441 var rowIndex = o.rowIndex;
17443 var r = this.store.getAt(rowIndex);
17445 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17447 if(!this.multiple){
17448 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17449 c.dom.removeAttribute('checked');
17452 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17454 this.setFromData(r.data);
17456 var close = this.closeTriggerEl();
17462 this.hideTouchView();
17464 this.fireEvent('select', this, r, rowIndex);
17469 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17470 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17471 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17475 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17476 this.addItem(r.data);
17477 this.tickItems.push(r.data);
17481 getAutoCreateNativeIOS : function()
17484 cls: 'form-group' //input-group,
17489 cls : 'roo-ios-select'
17493 combobox.name = this.name;
17496 if (this.disabled) {
17497 combobox.disabled = true;
17500 var settings = this;
17502 ['xs','sm','md','lg'].map(function(size){
17503 if (settings[size]) {
17504 cfg.cls += ' col-' + size + '-' + settings[size];
17514 initIOSView : function()
17516 this.store.on('load', this.onIOSViewLoad, this);
17521 onIOSViewLoad : function()
17523 if(this.store.getCount() < 1){
17527 this.clearIOSView();
17529 if(this.allowBlank) {
17531 var default_text = '-- SELECT --';
17533 if(this.placeholder.length){
17534 default_text = this.placeholder;
17537 if(this.emptyTitle.length){
17538 default_text += ' - ' + this.emptyTitle + ' -';
17541 var opt = this.inputEl().createChild({
17544 html : default_text
17548 o[this.valueField] = 0;
17549 o[this.displayField] = default_text;
17551 this.ios_options.push({
17558 this.store.data.each(function(d, rowIndex){
17562 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17563 html = d.data[this.displayField];
17568 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17569 value = d.data[this.valueField];
17578 if(this.value == d.data[this.valueField]){
17579 option['selected'] = true;
17582 var opt = this.inputEl().createChild(option);
17584 this.ios_options.push({
17591 this.inputEl().on('change', function(){
17592 this.fireEvent('select', this);
17597 clearIOSView: function()
17599 this.inputEl().dom.innerHTML = '';
17601 this.ios_options = [];
17604 setIOSValue: function(v)
17608 if(!this.ios_options){
17612 Roo.each(this.ios_options, function(opts){
17614 opts.el.dom.removeAttribute('selected');
17616 if(opts.data[this.valueField] != v){
17620 opts.el.dom.setAttribute('selected', true);
17626 * @cfg {Boolean} grow
17630 * @cfg {Number} growMin
17634 * @cfg {Number} growMax
17643 Roo.apply(Roo.bootstrap.ComboBox, {
17647 cls: 'modal-header',
17669 cls: 'list-group-item',
17673 cls: 'roo-combobox-list-group-item-value'
17677 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17691 listItemCheckbox : {
17693 cls: 'list-group-item',
17697 cls: 'roo-combobox-list-group-item-value'
17701 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17717 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17722 cls: 'modal-footer',
17730 cls: 'col-xs-6 text-left',
17733 cls: 'btn btn-danger roo-touch-view-cancel',
17739 cls: 'col-xs-6 text-right',
17742 cls: 'btn btn-success roo-touch-view-ok',
17753 Roo.apply(Roo.bootstrap.ComboBox, {
17755 touchViewTemplate : {
17757 cls: 'modal fade roo-combobox-touch-view',
17761 cls: 'modal-dialog',
17762 style : 'position:fixed', // we have to fix position....
17766 cls: 'modal-content',
17768 Roo.bootstrap.ComboBox.header,
17769 Roo.bootstrap.ComboBox.body,
17770 Roo.bootstrap.ComboBox.footer
17779 * Ext JS Library 1.1.1
17780 * Copyright(c) 2006-2007, Ext JS, LLC.
17782 * Originally Released Under LGPL - original licence link has changed is not relivant.
17785 * <script type="text/javascript">
17790 * @extends Roo.util.Observable
17791 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17792 * This class also supports single and multi selection modes. <br>
17793 * Create a data model bound view:
17795 var store = new Roo.data.Store(...);
17797 var view = new Roo.View({
17799 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
17801 singleSelect: true,
17802 selectedClass: "ydataview-selected",
17806 // listen for node click?
17807 view.on("click", function(vw, index, node, e){
17808 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17812 dataModel.load("foobar.xml");
17814 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17816 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17817 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17819 * Note: old style constructor is still suported (container, template, config)
17822 * Create a new View
17823 * @param {Object} config The config object
17826 Roo.View = function(config, depreciated_tpl, depreciated_config){
17828 this.parent = false;
17830 if (typeof(depreciated_tpl) == 'undefined') {
17831 // new way.. - universal constructor.
17832 Roo.apply(this, config);
17833 this.el = Roo.get(this.el);
17836 this.el = Roo.get(config);
17837 this.tpl = depreciated_tpl;
17838 Roo.apply(this, depreciated_config);
17840 this.wrapEl = this.el.wrap().wrap();
17841 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17844 if(typeof(this.tpl) == "string"){
17845 this.tpl = new Roo.Template(this.tpl);
17847 // support xtype ctors..
17848 this.tpl = new Roo.factory(this.tpl, Roo);
17852 this.tpl.compile();
17857 * @event beforeclick
17858 * Fires before a click is processed. Returns false to cancel the default action.
17859 * @param {Roo.View} this
17860 * @param {Number} index The index of the target node
17861 * @param {HTMLElement} node The target node
17862 * @param {Roo.EventObject} e The raw event object
17864 "beforeclick" : true,
17867 * Fires when a template node is clicked.
17868 * @param {Roo.View} this
17869 * @param {Number} index The index of the target node
17870 * @param {HTMLElement} node The target node
17871 * @param {Roo.EventObject} e The raw event object
17876 * Fires when a template node is double clicked.
17877 * @param {Roo.View} this
17878 * @param {Number} index The index of the target node
17879 * @param {HTMLElement} node The target node
17880 * @param {Roo.EventObject} e The raw event object
17884 * @event contextmenu
17885 * Fires when a template node is right clicked.
17886 * @param {Roo.View} this
17887 * @param {Number} index The index of the target node
17888 * @param {HTMLElement} node The target node
17889 * @param {Roo.EventObject} e The raw event object
17891 "contextmenu" : true,
17893 * @event selectionchange
17894 * Fires when the selected nodes change.
17895 * @param {Roo.View} this
17896 * @param {Array} selections Array of the selected nodes
17898 "selectionchange" : true,
17901 * @event beforeselect
17902 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17903 * @param {Roo.View} this
17904 * @param {HTMLElement} node The node to be selected
17905 * @param {Array} selections Array of currently selected nodes
17907 "beforeselect" : true,
17909 * @event preparedata
17910 * Fires on every row to render, to allow you to change the data.
17911 * @param {Roo.View} this
17912 * @param {Object} data to be rendered (change this)
17914 "preparedata" : true
17922 "click": this.onClick,
17923 "dblclick": this.onDblClick,
17924 "contextmenu": this.onContextMenu,
17928 this.selections = [];
17930 this.cmp = new Roo.CompositeElementLite([]);
17932 this.store = Roo.factory(this.store, Roo.data);
17933 this.setStore(this.store, true);
17936 if ( this.footer && this.footer.xtype) {
17938 var fctr = this.wrapEl.appendChild(document.createElement("div"));
17940 this.footer.dataSource = this.store;
17941 this.footer.container = fctr;
17942 this.footer = Roo.factory(this.footer, Roo);
17943 fctr.insertFirst(this.el);
17945 // this is a bit insane - as the paging toolbar seems to detach the el..
17946 // dom.parentNode.parentNode.parentNode
17947 // they get detached?
17951 Roo.View.superclass.constructor.call(this);
17956 Roo.extend(Roo.View, Roo.util.Observable, {
17959 * @cfg {Roo.data.Store} store Data store to load data from.
17964 * @cfg {String|Roo.Element} el The container element.
17969 * @cfg {String|Roo.Template} tpl The template used by this View
17973 * @cfg {String} dataName the named area of the template to use as the data area
17974 * Works with domtemplates roo-name="name"
17978 * @cfg {String} selectedClass The css class to add to selected nodes
17980 selectedClass : "x-view-selected",
17982 * @cfg {String} emptyText The empty text to show when nothing is loaded.
17987 * @cfg {String} text to display on mask (default Loading)
17991 * @cfg {Boolean} multiSelect Allow multiple selection
17993 multiSelect : false,
17995 * @cfg {Boolean} singleSelect Allow single selection
17997 singleSelect: false,
18000 * @cfg {Boolean} toggleSelect - selecting
18002 toggleSelect : false,
18005 * @cfg {Boolean} tickable - selecting
18010 * Returns the element this view is bound to.
18011 * @return {Roo.Element}
18013 getEl : function(){
18014 return this.wrapEl;
18020 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18022 refresh : function(){
18023 //Roo.log('refresh');
18026 // if we are using something like 'domtemplate', then
18027 // the what gets used is:
18028 // t.applySubtemplate(NAME, data, wrapping data..)
18029 // the outer template then get' applied with
18030 // the store 'extra data'
18031 // and the body get's added to the
18032 // roo-name="data" node?
18033 // <span class='roo-tpl-{name}'></span> ?????
18037 this.clearSelections();
18038 this.el.update("");
18040 var records = this.store.getRange();
18041 if(records.length < 1) {
18043 // is this valid?? = should it render a template??
18045 this.el.update(this.emptyText);
18049 if (this.dataName) {
18050 this.el.update(t.apply(this.store.meta)); //????
18051 el = this.el.child('.roo-tpl-' + this.dataName);
18054 for(var i = 0, len = records.length; i < len; i++){
18055 var data = this.prepareData(records[i].data, i, records[i]);
18056 this.fireEvent("preparedata", this, data, i, records[i]);
18058 var d = Roo.apply({}, data);
18061 Roo.apply(d, {'roo-id' : Roo.id()});
18065 Roo.each(this.parent.item, function(item){
18066 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18069 Roo.apply(d, {'roo-data-checked' : 'checked'});
18073 html[html.length] = Roo.util.Format.trim(
18075 t.applySubtemplate(this.dataName, d, this.store.meta) :
18082 el.update(html.join(""));
18083 this.nodes = el.dom.childNodes;
18084 this.updateIndexes(0);
18089 * Function to override to reformat the data that is sent to
18090 * the template for each node.
18091 * DEPRICATED - use the preparedata event handler.
18092 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18093 * a JSON object for an UpdateManager bound view).
18095 prepareData : function(data, index, record)
18097 this.fireEvent("preparedata", this, data, index, record);
18101 onUpdate : function(ds, record){
18102 // Roo.log('on update');
18103 this.clearSelections();
18104 var index = this.store.indexOf(record);
18105 var n = this.nodes[index];
18106 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18107 n.parentNode.removeChild(n);
18108 this.updateIndexes(index, index);
18114 onAdd : function(ds, records, index)
18116 //Roo.log(['on Add', ds, records, index] );
18117 this.clearSelections();
18118 if(this.nodes.length == 0){
18122 var n = this.nodes[index];
18123 for(var i = 0, len = records.length; i < len; i++){
18124 var d = this.prepareData(records[i].data, i, records[i]);
18126 this.tpl.insertBefore(n, d);
18129 this.tpl.append(this.el, d);
18132 this.updateIndexes(index);
18135 onRemove : function(ds, record, index){
18136 // Roo.log('onRemove');
18137 this.clearSelections();
18138 var el = this.dataName ?
18139 this.el.child('.roo-tpl-' + this.dataName) :
18142 el.dom.removeChild(this.nodes[index]);
18143 this.updateIndexes(index);
18147 * Refresh an individual node.
18148 * @param {Number} index
18150 refreshNode : function(index){
18151 this.onUpdate(this.store, this.store.getAt(index));
18154 updateIndexes : function(startIndex, endIndex){
18155 var ns = this.nodes;
18156 startIndex = startIndex || 0;
18157 endIndex = endIndex || ns.length - 1;
18158 for(var i = startIndex; i <= endIndex; i++){
18159 ns[i].nodeIndex = i;
18164 * Changes the data store this view uses and refresh the view.
18165 * @param {Store} store
18167 setStore : function(store, initial){
18168 if(!initial && this.store){
18169 this.store.un("datachanged", this.refresh);
18170 this.store.un("add", this.onAdd);
18171 this.store.un("remove", this.onRemove);
18172 this.store.un("update", this.onUpdate);
18173 this.store.un("clear", this.refresh);
18174 this.store.un("beforeload", this.onBeforeLoad);
18175 this.store.un("load", this.onLoad);
18176 this.store.un("loadexception", this.onLoad);
18180 store.on("datachanged", this.refresh, this);
18181 store.on("add", this.onAdd, this);
18182 store.on("remove", this.onRemove, this);
18183 store.on("update", this.onUpdate, this);
18184 store.on("clear", this.refresh, this);
18185 store.on("beforeload", this.onBeforeLoad, this);
18186 store.on("load", this.onLoad, this);
18187 store.on("loadexception", this.onLoad, this);
18195 * onbeforeLoad - masks the loading area.
18198 onBeforeLoad : function(store,opts)
18200 //Roo.log('onBeforeLoad');
18202 this.el.update("");
18204 this.el.mask(this.mask ? this.mask : "Loading" );
18206 onLoad : function ()
18213 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18214 * @param {HTMLElement} node
18215 * @return {HTMLElement} The template node
18217 findItemFromChild : function(node){
18218 var el = this.dataName ?
18219 this.el.child('.roo-tpl-' + this.dataName,true) :
18222 if(!node || node.parentNode == el){
18225 var p = node.parentNode;
18226 while(p && p != el){
18227 if(p.parentNode == el){
18236 onClick : function(e){
18237 var item = this.findItemFromChild(e.getTarget());
18239 var index = this.indexOf(item);
18240 if(this.onItemClick(item, index, e) !== false){
18241 this.fireEvent("click", this, index, item, e);
18244 this.clearSelections();
18249 onContextMenu : function(e){
18250 var item = this.findItemFromChild(e.getTarget());
18252 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18257 onDblClick : function(e){
18258 var item = this.findItemFromChild(e.getTarget());
18260 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18264 onItemClick : function(item, index, e)
18266 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18269 if (this.toggleSelect) {
18270 var m = this.isSelected(item) ? 'unselect' : 'select';
18273 _t[m](item, true, false);
18276 if(this.multiSelect || this.singleSelect){
18277 if(this.multiSelect && e.shiftKey && this.lastSelection){
18278 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18280 this.select(item, this.multiSelect && e.ctrlKey);
18281 this.lastSelection = item;
18284 if(!this.tickable){
18285 e.preventDefault();
18293 * Get the number of selected nodes.
18296 getSelectionCount : function(){
18297 return this.selections.length;
18301 * Get the currently selected nodes.
18302 * @return {Array} An array of HTMLElements
18304 getSelectedNodes : function(){
18305 return this.selections;
18309 * Get the indexes of the selected nodes.
18312 getSelectedIndexes : function(){
18313 var indexes = [], s = this.selections;
18314 for(var i = 0, len = s.length; i < len; i++){
18315 indexes.push(s[i].nodeIndex);
18321 * Clear all selections
18322 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18324 clearSelections : function(suppressEvent){
18325 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18326 this.cmp.elements = this.selections;
18327 this.cmp.removeClass(this.selectedClass);
18328 this.selections = [];
18329 if(!suppressEvent){
18330 this.fireEvent("selectionchange", this, this.selections);
18336 * Returns true if the passed node is selected
18337 * @param {HTMLElement/Number} node The node or node index
18338 * @return {Boolean}
18340 isSelected : function(node){
18341 var s = this.selections;
18345 node = this.getNode(node);
18346 return s.indexOf(node) !== -1;
18351 * @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
18352 * @param {Boolean} keepExisting (optional) true to keep existing selections
18353 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18355 select : function(nodeInfo, keepExisting, suppressEvent){
18356 if(nodeInfo instanceof Array){
18358 this.clearSelections(true);
18360 for(var i = 0, len = nodeInfo.length; i < len; i++){
18361 this.select(nodeInfo[i], true, true);
18365 var node = this.getNode(nodeInfo);
18366 if(!node || this.isSelected(node)){
18367 return; // already selected.
18370 this.clearSelections(true);
18373 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18374 Roo.fly(node).addClass(this.selectedClass);
18375 this.selections.push(node);
18376 if(!suppressEvent){
18377 this.fireEvent("selectionchange", this, this.selections);
18385 * @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
18386 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18387 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18389 unselect : function(nodeInfo, keepExisting, suppressEvent)
18391 if(nodeInfo instanceof Array){
18392 Roo.each(this.selections, function(s) {
18393 this.unselect(s, nodeInfo);
18397 var node = this.getNode(nodeInfo);
18398 if(!node || !this.isSelected(node)){
18399 //Roo.log("not selected");
18400 return; // not selected.
18404 Roo.each(this.selections, function(s) {
18406 Roo.fly(node).removeClass(this.selectedClass);
18413 this.selections= ns;
18414 this.fireEvent("selectionchange", this, this.selections);
18418 * Gets a template node.
18419 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18420 * @return {HTMLElement} The node or null if it wasn't found
18422 getNode : function(nodeInfo){
18423 if(typeof nodeInfo == "string"){
18424 return document.getElementById(nodeInfo);
18425 }else if(typeof nodeInfo == "number"){
18426 return this.nodes[nodeInfo];
18432 * Gets a range template nodes.
18433 * @param {Number} startIndex
18434 * @param {Number} endIndex
18435 * @return {Array} An array of nodes
18437 getNodes : function(start, end){
18438 var ns = this.nodes;
18439 start = start || 0;
18440 end = typeof end == "undefined" ? ns.length - 1 : end;
18443 for(var i = start; i <= end; i++){
18447 for(var i = start; i >= end; i--){
18455 * Finds the index of the passed node
18456 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18457 * @return {Number} The index of the node or -1
18459 indexOf : function(node){
18460 node = this.getNode(node);
18461 if(typeof node.nodeIndex == "number"){
18462 return node.nodeIndex;
18464 var ns = this.nodes;
18465 for(var i = 0, len = ns.length; i < len; i++){
18476 * based on jquery fullcalendar
18480 Roo.bootstrap = Roo.bootstrap || {};
18482 * @class Roo.bootstrap.Calendar
18483 * @extends Roo.bootstrap.Component
18484 * Bootstrap Calendar class
18485 * @cfg {Boolean} loadMask (true|false) default false
18486 * @cfg {Object} header generate the user specific header of the calendar, default false
18489 * Create a new Container
18490 * @param {Object} config The config object
18495 Roo.bootstrap.Calendar = function(config){
18496 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18500 * Fires when a date is selected
18501 * @param {DatePicker} this
18502 * @param {Date} date The selected date
18506 * @event monthchange
18507 * Fires when the displayed month changes
18508 * @param {DatePicker} this
18509 * @param {Date} date The selected month
18511 'monthchange': true,
18513 * @event evententer
18514 * Fires when mouse over an event
18515 * @param {Calendar} this
18516 * @param {event} Event
18518 'evententer': true,
18520 * @event eventleave
18521 * Fires when the mouse leaves an
18522 * @param {Calendar} this
18525 'eventleave': true,
18527 * @event eventclick
18528 * Fires when the mouse click an
18529 * @param {Calendar} this
18538 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18541 * @cfg {Number} startDay
18542 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18550 getAutoCreate : function(){
18553 var fc_button = function(name, corner, style, content ) {
18554 return Roo.apply({},{
18556 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18558 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18561 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18572 style : 'width:100%',
18579 cls : 'fc-header-left',
18581 fc_button('prev', 'left', 'arrow', '‹' ),
18582 fc_button('next', 'right', 'arrow', '›' ),
18583 { tag: 'span', cls: 'fc-header-space' },
18584 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18592 cls : 'fc-header-center',
18596 cls: 'fc-header-title',
18599 html : 'month / year'
18607 cls : 'fc-header-right',
18609 /* fc_button('month', 'left', '', 'month' ),
18610 fc_button('week', '', '', 'week' ),
18611 fc_button('day', 'right', '', 'day' )
18623 header = this.header;
18626 var cal_heads = function() {
18628 // fixme - handle this.
18630 for (var i =0; i < Date.dayNames.length; i++) {
18631 var d = Date.dayNames[i];
18634 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18635 html : d.substring(0,3)
18639 ret[0].cls += ' fc-first';
18640 ret[6].cls += ' fc-last';
18643 var cal_cell = function(n) {
18646 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18651 cls: 'fc-day-number',
18655 cls: 'fc-day-content',
18659 style: 'position: relative;' // height: 17px;
18671 var cal_rows = function() {
18674 for (var r = 0; r < 6; r++) {
18681 for (var i =0; i < Date.dayNames.length; i++) {
18682 var d = Date.dayNames[i];
18683 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18686 row.cn[0].cls+=' fc-first';
18687 row.cn[0].cn[0].style = 'min-height:90px';
18688 row.cn[6].cls+=' fc-last';
18692 ret[0].cls += ' fc-first';
18693 ret[4].cls += ' fc-prev-last';
18694 ret[5].cls += ' fc-last';
18701 cls: 'fc-border-separate',
18702 style : 'width:100%',
18710 cls : 'fc-first fc-last',
18728 cls : 'fc-content',
18729 style : "position: relative;",
18732 cls : 'fc-view fc-view-month fc-grid',
18733 style : 'position: relative',
18734 unselectable : 'on',
18737 cls : 'fc-event-container',
18738 style : 'position:absolute;z-index:8;top:0;left:0;'
18756 initEvents : function()
18759 throw "can not find store for calendar";
18765 style: "text-align:center",
18769 style: "background-color:white;width:50%;margin:250 auto",
18773 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18784 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18786 var size = this.el.select('.fc-content', true).first().getSize();
18787 this.maskEl.setSize(size.width, size.height);
18788 this.maskEl.enableDisplayMode("block");
18789 if(!this.loadMask){
18790 this.maskEl.hide();
18793 this.store = Roo.factory(this.store, Roo.data);
18794 this.store.on('load', this.onLoad, this);
18795 this.store.on('beforeload', this.onBeforeLoad, this);
18799 this.cells = this.el.select('.fc-day',true);
18800 //Roo.log(this.cells);
18801 this.textNodes = this.el.query('.fc-day-number');
18802 this.cells.addClassOnOver('fc-state-hover');
18804 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18805 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18806 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18807 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18809 this.on('monthchange', this.onMonthChange, this);
18811 this.update(new Date().clearTime());
18814 resize : function() {
18815 var sz = this.el.getSize();
18817 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18818 this.el.select('.fc-day-content div',true).setHeight(34);
18823 showPrevMonth : function(e){
18824 this.update(this.activeDate.add("mo", -1));
18826 showToday : function(e){
18827 this.update(new Date().clearTime());
18830 showNextMonth : function(e){
18831 this.update(this.activeDate.add("mo", 1));
18835 showPrevYear : function(){
18836 this.update(this.activeDate.add("y", -1));
18840 showNextYear : function(){
18841 this.update(this.activeDate.add("y", 1));
18846 update : function(date)
18848 var vd = this.activeDate;
18849 this.activeDate = date;
18850 // if(vd && this.el){
18851 // var t = date.getTime();
18852 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18853 // Roo.log('using add remove');
18855 // this.fireEvent('monthchange', this, date);
18857 // this.cells.removeClass("fc-state-highlight");
18858 // this.cells.each(function(c){
18859 // if(c.dateValue == t){
18860 // c.addClass("fc-state-highlight");
18861 // setTimeout(function(){
18862 // try{c.dom.firstChild.focus();}catch(e){}
18872 var days = date.getDaysInMonth();
18874 var firstOfMonth = date.getFirstDateOfMonth();
18875 var startingPos = firstOfMonth.getDay()-this.startDay;
18877 if(startingPos < this.startDay){
18881 var pm = date.add(Date.MONTH, -1);
18882 var prevStart = pm.getDaysInMonth()-startingPos;
18884 this.cells = this.el.select('.fc-day',true);
18885 this.textNodes = this.el.query('.fc-day-number');
18886 this.cells.addClassOnOver('fc-state-hover');
18888 var cells = this.cells.elements;
18889 var textEls = this.textNodes;
18891 Roo.each(cells, function(cell){
18892 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18895 days += startingPos;
18897 // convert everything to numbers so it's fast
18898 var day = 86400000;
18899 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18902 //Roo.log(prevStart);
18904 var today = new Date().clearTime().getTime();
18905 var sel = date.clearTime().getTime();
18906 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18907 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18908 var ddMatch = this.disabledDatesRE;
18909 var ddText = this.disabledDatesText;
18910 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18911 var ddaysText = this.disabledDaysText;
18912 var format = this.format;
18914 var setCellClass = function(cal, cell){
18918 //Roo.log('set Cell Class');
18920 var t = d.getTime();
18924 cell.dateValue = t;
18926 cell.className += " fc-today";
18927 cell.className += " fc-state-highlight";
18928 cell.title = cal.todayText;
18931 // disable highlight in other month..
18932 //cell.className += " fc-state-highlight";
18937 cell.className = " fc-state-disabled";
18938 cell.title = cal.minText;
18942 cell.className = " fc-state-disabled";
18943 cell.title = cal.maxText;
18947 if(ddays.indexOf(d.getDay()) != -1){
18948 cell.title = ddaysText;
18949 cell.className = " fc-state-disabled";
18952 if(ddMatch && format){
18953 var fvalue = d.dateFormat(format);
18954 if(ddMatch.test(fvalue)){
18955 cell.title = ddText.replace("%0", fvalue);
18956 cell.className = " fc-state-disabled";
18960 if (!cell.initialClassName) {
18961 cell.initialClassName = cell.dom.className;
18964 cell.dom.className = cell.initialClassName + ' ' + cell.className;
18969 for(; i < startingPos; i++) {
18970 textEls[i].innerHTML = (++prevStart);
18971 d.setDate(d.getDate()+1);
18973 cells[i].className = "fc-past fc-other-month";
18974 setCellClass(this, cells[i]);
18979 for(; i < days; i++){
18980 intDay = i - startingPos + 1;
18981 textEls[i].innerHTML = (intDay);
18982 d.setDate(d.getDate()+1);
18984 cells[i].className = ''; // "x-date-active";
18985 setCellClass(this, cells[i]);
18989 for(; i < 42; i++) {
18990 textEls[i].innerHTML = (++extraDays);
18991 d.setDate(d.getDate()+1);
18993 cells[i].className = "fc-future fc-other-month";
18994 setCellClass(this, cells[i]);
18997 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18999 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19001 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19002 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19004 if(totalRows != 6){
19005 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19006 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19009 this.fireEvent('monthchange', this, date);
19013 if(!this.internalRender){
19014 var main = this.el.dom.firstChild;
19015 var w = main.offsetWidth;
19016 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19017 Roo.fly(main).setWidth(w);
19018 this.internalRender = true;
19019 // opera does not respect the auto grow header center column
19020 // then, after it gets a width opera refuses to recalculate
19021 // without a second pass
19022 if(Roo.isOpera && !this.secondPass){
19023 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19024 this.secondPass = true;
19025 this.update.defer(10, this, [date]);
19032 findCell : function(dt) {
19033 dt = dt.clearTime().getTime();
19035 this.cells.each(function(c){
19036 //Roo.log("check " +c.dateValue + '?=' + dt);
19037 if(c.dateValue == dt){
19047 findCells : function(ev) {
19048 var s = ev.start.clone().clearTime().getTime();
19050 var e= ev.end.clone().clearTime().getTime();
19053 this.cells.each(function(c){
19054 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19056 if(c.dateValue > e){
19059 if(c.dateValue < s){
19068 // findBestRow: function(cells)
19072 // for (var i =0 ; i < cells.length;i++) {
19073 // ret = Math.max(cells[i].rows || 0,ret);
19080 addItem : function(ev)
19082 // look for vertical location slot in
19083 var cells = this.findCells(ev);
19085 // ev.row = this.findBestRow(cells);
19087 // work out the location.
19091 for(var i =0; i < cells.length; i++) {
19093 cells[i].row = cells[0].row;
19096 cells[i].row = cells[i].row + 1;
19106 if (crow.start.getY() == cells[i].getY()) {
19108 crow.end = cells[i];
19125 cells[0].events.push(ev);
19127 this.calevents.push(ev);
19130 clearEvents: function() {
19132 if(!this.calevents){
19136 Roo.each(this.cells.elements, function(c){
19142 Roo.each(this.calevents, function(e) {
19143 Roo.each(e.els, function(el) {
19144 el.un('mouseenter' ,this.onEventEnter, this);
19145 el.un('mouseleave' ,this.onEventLeave, this);
19150 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19156 renderEvents: function()
19160 this.cells.each(function(c) {
19169 if(c.row != c.events.length){
19170 r = 4 - (4 - (c.row - c.events.length));
19173 c.events = ev.slice(0, r);
19174 c.more = ev.slice(r);
19176 if(c.more.length && c.more.length == 1){
19177 c.events.push(c.more.pop());
19180 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19184 this.cells.each(function(c) {
19186 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19189 for (var e = 0; e < c.events.length; e++){
19190 var ev = c.events[e];
19191 var rows = ev.rows;
19193 for(var i = 0; i < rows.length; i++) {
19195 // how many rows should it span..
19198 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19199 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19201 unselectable : "on",
19204 cls: 'fc-event-inner',
19208 // cls: 'fc-event-time',
19209 // html : cells.length > 1 ? '' : ev.time
19213 cls: 'fc-event-title',
19214 html : String.format('{0}', ev.title)
19221 cls: 'ui-resizable-handle ui-resizable-e',
19222 html : '  '
19229 cfg.cls += ' fc-event-start';
19231 if ((i+1) == rows.length) {
19232 cfg.cls += ' fc-event-end';
19235 var ctr = _this.el.select('.fc-event-container',true).first();
19236 var cg = ctr.createChild(cfg);
19238 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19239 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19241 var r = (c.more.length) ? 1 : 0;
19242 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19243 cg.setWidth(ebox.right - sbox.x -2);
19245 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19246 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19247 cg.on('click', _this.onEventClick, _this, ev);
19258 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19259 style : 'position: absolute',
19260 unselectable : "on",
19263 cls: 'fc-event-inner',
19267 cls: 'fc-event-title',
19275 cls: 'ui-resizable-handle ui-resizable-e',
19276 html : '  '
19282 var ctr = _this.el.select('.fc-event-container',true).first();
19283 var cg = ctr.createChild(cfg);
19285 var sbox = c.select('.fc-day-content',true).first().getBox();
19286 var ebox = c.select('.fc-day-content',true).first().getBox();
19288 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19289 cg.setWidth(ebox.right - sbox.x -2);
19291 cg.on('click', _this.onMoreEventClick, _this, c.more);
19301 onEventEnter: function (e, el,event,d) {
19302 this.fireEvent('evententer', this, el, event);
19305 onEventLeave: function (e, el,event,d) {
19306 this.fireEvent('eventleave', this, el, event);
19309 onEventClick: function (e, el,event,d) {
19310 this.fireEvent('eventclick', this, el, event);
19313 onMonthChange: function () {
19317 onMoreEventClick: function(e, el, more)
19321 this.calpopover.placement = 'right';
19322 this.calpopover.setTitle('More');
19324 this.calpopover.setContent('');
19326 var ctr = this.calpopover.el.select('.popover-content', true).first();
19328 Roo.each(more, function(m){
19330 cls : 'fc-event-hori fc-event-draggable',
19333 var cg = ctr.createChild(cfg);
19335 cg.on('click', _this.onEventClick, _this, m);
19338 this.calpopover.show(el);
19343 onLoad: function ()
19345 this.calevents = [];
19348 if(this.store.getCount() > 0){
19349 this.store.data.each(function(d){
19352 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19353 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19354 time : d.data.start_time,
19355 title : d.data.title,
19356 description : d.data.description,
19357 venue : d.data.venue
19362 this.renderEvents();
19364 if(this.calevents.length && this.loadMask){
19365 this.maskEl.hide();
19369 onBeforeLoad: function()
19371 this.clearEvents();
19373 this.maskEl.show();
19387 * @class Roo.bootstrap.Popover
19388 * @extends Roo.bootstrap.Component
19389 * Bootstrap Popover class
19390 * @cfg {String} html contents of the popover (or false to use children..)
19391 * @cfg {String} title of popover (or false to hide)
19392 * @cfg {String} placement how it is placed
19393 * @cfg {String} trigger click || hover (or false to trigger manually)
19394 * @cfg {String} over what (parent or false to trigger manually.)
19395 * @cfg {Number} delay - delay before showing
19398 * Create a new Popover
19399 * @param {Object} config The config object
19402 Roo.bootstrap.Popover = function(config){
19403 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19409 * After the popover show
19411 * @param {Roo.bootstrap.Popover} this
19416 * After the popover hide
19418 * @param {Roo.bootstrap.Popover} this
19424 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19426 title: 'Fill in a title',
19429 placement : 'right',
19430 trigger : 'hover', // hover
19436 can_build_overlaid : false,
19438 getChildContainer : function()
19440 return this.el.select('.popover-content',true).first();
19443 getAutoCreate : function(){
19446 cls : 'popover roo-dynamic',
19447 style: 'display:block',
19453 cls : 'popover-inner',
19457 cls: 'popover-title popover-header',
19461 cls : 'popover-content popover-body',
19472 setTitle: function(str)
19475 this.el.select('.popover-title',true).first().dom.innerHTML = str;
19477 setContent: function(str)
19480 this.el.select('.popover-content',true).first().dom.innerHTML = str;
19482 // as it get's added to the bottom of the page.
19483 onRender : function(ct, position)
19485 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19487 var cfg = Roo.apply({}, this.getAutoCreate());
19491 cfg.cls += ' ' + this.cls;
19494 cfg.style = this.style;
19496 //Roo.log("adding to ");
19497 this.el = Roo.get(document.body).createChild(cfg, position);
19498 // Roo.log(this.el);
19503 initEvents : function()
19505 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19506 this.el.enableDisplayMode('block');
19508 if (this.over === false) {
19511 if (this.triggers === false) {
19514 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19515 var triggers = this.trigger ? this.trigger.split(' ') : [];
19516 Roo.each(triggers, function(trigger) {
19518 if (trigger == 'click') {
19519 on_el.on('click', this.toggle, this);
19520 } else if (trigger != 'manual') {
19521 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19522 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19524 on_el.on(eventIn ,this.enter, this);
19525 on_el.on(eventOut, this.leave, this);
19536 toggle : function () {
19537 this.hoverState == 'in' ? this.leave() : this.enter();
19540 enter : function () {
19542 clearTimeout(this.timeout);
19544 this.hoverState = 'in';
19546 if (!this.delay || !this.delay.show) {
19551 this.timeout = setTimeout(function () {
19552 if (_t.hoverState == 'in') {
19555 }, this.delay.show)
19558 leave : function() {
19559 clearTimeout(this.timeout);
19561 this.hoverState = 'out';
19563 if (!this.delay || !this.delay.hide) {
19568 this.timeout = setTimeout(function () {
19569 if (_t.hoverState == 'out') {
19572 }, this.delay.hide)
19575 show : function (on_el)
19578 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19582 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19583 if (this.html !== false) {
19584 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19586 this.el.removeClass([
19587 'fade','top','bottom', 'left', 'right','in',
19588 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19590 if (!this.title.length) {
19591 this.el.select('.popover-title',true).hide();
19594 var placement = typeof this.placement == 'function' ?
19595 this.placement.call(this, this.el, on_el) :
19598 var autoToken = /\s?auto?\s?/i;
19599 var autoPlace = autoToken.test(placement);
19601 placement = placement.replace(autoToken, '') || 'top';
19605 //this.el.setXY([0,0]);
19607 this.el.dom.style.display='block';
19608 this.el.addClass(placement);
19610 //this.el.appendTo(on_el);
19612 var p = this.getPosition();
19613 var box = this.el.getBox();
19618 var align = Roo.bootstrap.Popover.alignment[placement];
19621 this.el.alignTo(on_el, align[0],align[1]);
19622 //var arrow = this.el.select('.arrow',true).first();
19623 //arrow.set(align[2],
19625 this.el.addClass('in');
19628 if (this.el.hasClass('fade')) {
19632 this.hoverState = 'in';
19634 this.fireEvent('show', this);
19639 this.el.setXY([0,0]);
19640 this.el.removeClass('in');
19642 this.hoverState = null;
19644 this.fireEvent('hide', this);
19649 Roo.bootstrap.Popover.alignment = {
19650 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19651 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19652 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19653 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19664 * @class Roo.bootstrap.Progress
19665 * @extends Roo.bootstrap.Component
19666 * Bootstrap Progress class
19667 * @cfg {Boolean} striped striped of the progress bar
19668 * @cfg {Boolean} active animated of the progress bar
19672 * Create a new Progress
19673 * @param {Object} config The config object
19676 Roo.bootstrap.Progress = function(config){
19677 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19680 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19685 getAutoCreate : function(){
19693 cfg.cls += ' progress-striped';
19697 cfg.cls += ' active';
19716 * @class Roo.bootstrap.ProgressBar
19717 * @extends Roo.bootstrap.Component
19718 * Bootstrap ProgressBar class
19719 * @cfg {Number} aria_valuenow aria-value now
19720 * @cfg {Number} aria_valuemin aria-value min
19721 * @cfg {Number} aria_valuemax aria-value max
19722 * @cfg {String} label label for the progress bar
19723 * @cfg {String} panel (success | info | warning | danger )
19724 * @cfg {String} role role of the progress bar
19725 * @cfg {String} sr_only text
19729 * Create a new ProgressBar
19730 * @param {Object} config The config object
19733 Roo.bootstrap.ProgressBar = function(config){
19734 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19737 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19741 aria_valuemax : 100,
19747 getAutoCreate : function()
19752 cls: 'progress-bar',
19753 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19765 cfg.role = this.role;
19768 if(this.aria_valuenow){
19769 cfg['aria-valuenow'] = this.aria_valuenow;
19772 if(this.aria_valuemin){
19773 cfg['aria-valuemin'] = this.aria_valuemin;
19776 if(this.aria_valuemax){
19777 cfg['aria-valuemax'] = this.aria_valuemax;
19780 if(this.label && !this.sr_only){
19781 cfg.html = this.label;
19785 cfg.cls += ' progress-bar-' + this.panel;
19791 update : function(aria_valuenow)
19793 this.aria_valuenow = aria_valuenow;
19795 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19810 * @class Roo.bootstrap.TabGroup
19811 * @extends Roo.bootstrap.Column
19812 * Bootstrap Column class
19813 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19814 * @cfg {Boolean} carousel true to make the group behave like a carousel
19815 * @cfg {Boolean} bullets show bullets for the panels
19816 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19817 * @cfg {Number} timer auto slide timer .. default 0 millisecond
19818 * @cfg {Boolean} showarrow (true|false) show arrow default true
19821 * Create a new TabGroup
19822 * @param {Object} config The config object
19825 Roo.bootstrap.TabGroup = function(config){
19826 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19828 this.navId = Roo.id();
19831 Roo.bootstrap.TabGroup.register(this);
19835 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
19838 transition : false,
19843 slideOnTouch : false,
19846 getAutoCreate : function()
19848 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19850 cfg.cls += ' tab-content';
19852 if (this.carousel) {
19853 cfg.cls += ' carousel slide';
19856 cls : 'carousel-inner',
19860 if(this.bullets && !Roo.isTouch){
19863 cls : 'carousel-bullets',
19867 if(this.bullets_cls){
19868 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19875 cfg.cn[0].cn.push(bullets);
19878 if(this.showarrow){
19879 cfg.cn[0].cn.push({
19881 class : 'carousel-arrow',
19885 class : 'carousel-prev',
19889 class : 'fa fa-chevron-left'
19895 class : 'carousel-next',
19899 class : 'fa fa-chevron-right'
19912 initEvents: function()
19914 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19915 // this.el.on("touchstart", this.onTouchStart, this);
19918 if(this.autoslide){
19921 this.slideFn = window.setInterval(function() {
19922 _this.showPanelNext();
19926 if(this.showarrow){
19927 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19928 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19934 // onTouchStart : function(e, el, o)
19936 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19940 // this.showPanelNext();
19944 getChildContainer : function()
19946 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19950 * register a Navigation item
19951 * @param {Roo.bootstrap.NavItem} the navitem to add
19953 register : function(item)
19955 this.tabs.push( item);
19956 item.navId = this.navId; // not really needed..
19961 getActivePanel : function()
19964 Roo.each(this.tabs, function(t) {
19974 getPanelByName : function(n)
19977 Roo.each(this.tabs, function(t) {
19978 if (t.tabId == n) {
19986 indexOfPanel : function(p)
19989 Roo.each(this.tabs, function(t,i) {
19990 if (t.tabId == p.tabId) {
19999 * show a specific panel
20000 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20001 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20003 showPanel : function (pan)
20005 if(this.transition || typeof(pan) == 'undefined'){
20006 Roo.log("waiting for the transitionend");
20010 if (typeof(pan) == 'number') {
20011 pan = this.tabs[pan];
20014 if (typeof(pan) == 'string') {
20015 pan = this.getPanelByName(pan);
20018 var cur = this.getActivePanel();
20021 Roo.log('pan or acitve pan is undefined');
20025 if (pan.tabId == this.getActivePanel().tabId) {
20029 if (false === cur.fireEvent('beforedeactivate')) {
20033 if(this.bullets > 0 && !Roo.isTouch){
20034 this.setActiveBullet(this.indexOfPanel(pan));
20037 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20039 //class="carousel-item carousel-item-next carousel-item-left"
20041 this.transition = true;
20042 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20043 var lr = dir == 'next' ? 'left' : 'right';
20044 pan.el.addClass(dir); // or prev
20045 pan.el.addClass('carousel-item-' + dir); // or prev
20046 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20047 cur.el.addClass(lr); // or right
20048 pan.el.addClass(lr);
20049 cur.el.addClass('carousel-item-' +lr); // or right
20050 pan.el.addClass('carousel-item-' +lr);
20054 cur.el.on('transitionend', function() {
20055 Roo.log("trans end?");
20057 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20058 pan.setActive(true);
20060 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20061 cur.setActive(false);
20063 _this.transition = false;
20065 }, this, { single: true } );
20070 cur.setActive(false);
20071 pan.setActive(true);
20076 showPanelNext : function()
20078 var i = this.indexOfPanel(this.getActivePanel());
20080 if (i >= this.tabs.length - 1 && !this.autoslide) {
20084 if (i >= this.tabs.length - 1 && this.autoslide) {
20088 this.showPanel(this.tabs[i+1]);
20091 showPanelPrev : function()
20093 var i = this.indexOfPanel(this.getActivePanel());
20095 if (i < 1 && !this.autoslide) {
20099 if (i < 1 && this.autoslide) {
20100 i = this.tabs.length;
20103 this.showPanel(this.tabs[i-1]);
20107 addBullet: function()
20109 if(!this.bullets || Roo.isTouch){
20112 var ctr = this.el.select('.carousel-bullets',true).first();
20113 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20114 var bullet = ctr.createChild({
20115 cls : 'bullet bullet-' + i
20116 },ctr.dom.lastChild);
20121 bullet.on('click', (function(e, el, o, ii, t){
20123 e.preventDefault();
20125 this.showPanel(ii);
20127 if(this.autoslide && this.slideFn){
20128 clearInterval(this.slideFn);
20129 this.slideFn = window.setInterval(function() {
20130 _this.showPanelNext();
20134 }).createDelegate(this, [i, bullet], true));
20139 setActiveBullet : function(i)
20145 Roo.each(this.el.select('.bullet', true).elements, function(el){
20146 el.removeClass('selected');
20149 var bullet = this.el.select('.bullet-' + i, true).first();
20155 bullet.addClass('selected');
20166 Roo.apply(Roo.bootstrap.TabGroup, {
20170 * register a Navigation Group
20171 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20173 register : function(navgrp)
20175 this.groups[navgrp.navId] = navgrp;
20179 * fetch a Navigation Group based on the navigation ID
20180 * if one does not exist , it will get created.
20181 * @param {string} the navgroup to add
20182 * @returns {Roo.bootstrap.NavGroup} the navgroup
20184 get: function(navId) {
20185 if (typeof(this.groups[navId]) == 'undefined') {
20186 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20188 return this.groups[navId] ;
20203 * @class Roo.bootstrap.TabPanel
20204 * @extends Roo.bootstrap.Component
20205 * Bootstrap TabPanel class
20206 * @cfg {Boolean} active panel active
20207 * @cfg {String} html panel content
20208 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20209 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20210 * @cfg {String} href click to link..
20214 * Create a new TabPanel
20215 * @param {Object} config The config object
20218 Roo.bootstrap.TabPanel = function(config){
20219 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20223 * Fires when the active status changes
20224 * @param {Roo.bootstrap.TabPanel} this
20225 * @param {Boolean} state the new state
20230 * @event beforedeactivate
20231 * Fires before a tab is de-activated - can be used to do validation on a form.
20232 * @param {Roo.bootstrap.TabPanel} this
20233 * @return {Boolean} false if there is an error
20236 'beforedeactivate': true
20239 this.tabId = this.tabId || Roo.id();
20243 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20251 getAutoCreate : function(){
20256 // item is needed for carousel - not sure if it has any effect otherwise
20257 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20258 html: this.html || ''
20262 cfg.cls += ' active';
20266 cfg.tabId = this.tabId;
20274 initEvents: function()
20276 var p = this.parent();
20278 this.navId = this.navId || p.navId;
20280 if (typeof(this.navId) != 'undefined') {
20281 // not really needed.. but just in case.. parent should be a NavGroup.
20282 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20286 var i = tg.tabs.length - 1;
20288 if(this.active && tg.bullets > 0 && i < tg.bullets){
20289 tg.setActiveBullet(i);
20293 this.el.on('click', this.onClick, this);
20296 this.el.on("touchstart", this.onTouchStart, this);
20297 this.el.on("touchmove", this.onTouchMove, this);
20298 this.el.on("touchend", this.onTouchEnd, this);
20303 onRender : function(ct, position)
20305 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20308 setActive : function(state)
20310 Roo.log("panel - set active " + this.tabId + "=" + state);
20312 this.active = state;
20314 this.el.removeClass('active');
20316 } else if (!this.el.hasClass('active')) {
20317 this.el.addClass('active');
20320 this.fireEvent('changed', this, state);
20323 onClick : function(e)
20325 e.preventDefault();
20327 if(!this.href.length){
20331 window.location.href = this.href;
20340 onTouchStart : function(e)
20342 this.swiping = false;
20344 this.startX = e.browserEvent.touches[0].clientX;
20345 this.startY = e.browserEvent.touches[0].clientY;
20348 onTouchMove : function(e)
20350 this.swiping = true;
20352 this.endX = e.browserEvent.touches[0].clientX;
20353 this.endY = e.browserEvent.touches[0].clientY;
20356 onTouchEnd : function(e)
20363 var tabGroup = this.parent();
20365 if(this.endX > this.startX){ // swiping right
20366 tabGroup.showPanelPrev();
20370 if(this.startX > this.endX){ // swiping left
20371 tabGroup.showPanelNext();
20390 * @class Roo.bootstrap.DateField
20391 * @extends Roo.bootstrap.Input
20392 * Bootstrap DateField class
20393 * @cfg {Number} weekStart default 0
20394 * @cfg {String} viewMode default empty, (months|years)
20395 * @cfg {String} minViewMode default empty, (months|years)
20396 * @cfg {Number} startDate default -Infinity
20397 * @cfg {Number} endDate default Infinity
20398 * @cfg {Boolean} todayHighlight default false
20399 * @cfg {Boolean} todayBtn default false
20400 * @cfg {Boolean} calendarWeeks default false
20401 * @cfg {Object} daysOfWeekDisabled default empty
20402 * @cfg {Boolean} singleMode default false (true | false)
20404 * @cfg {Boolean} keyboardNavigation default true
20405 * @cfg {String} language default en
20408 * Create a new DateField
20409 * @param {Object} config The config object
20412 Roo.bootstrap.DateField = function(config){
20413 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20417 * Fires when this field show.
20418 * @param {Roo.bootstrap.DateField} this
20419 * @param {Mixed} date The date value
20424 * Fires when this field hide.
20425 * @param {Roo.bootstrap.DateField} this
20426 * @param {Mixed} date The date value
20431 * Fires when select a date.
20432 * @param {Roo.bootstrap.DateField} this
20433 * @param {Mixed} date The date value
20437 * @event beforeselect
20438 * Fires when before select a date.
20439 * @param {Roo.bootstrap.DateField} this
20440 * @param {Mixed} date The date value
20442 beforeselect : true
20446 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20449 * @cfg {String} format
20450 * The default date format string which can be overriden for localization support. The format must be
20451 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20455 * @cfg {String} altFormats
20456 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20457 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20459 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20467 todayHighlight : false,
20473 keyboardNavigation: true,
20475 calendarWeeks: false,
20477 startDate: -Infinity,
20481 daysOfWeekDisabled: [],
20485 singleMode : false,
20487 UTCDate: function()
20489 return new Date(Date.UTC.apply(Date, arguments));
20492 UTCToday: function()
20494 var today = new Date();
20495 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20498 getDate: function() {
20499 var d = this.getUTCDate();
20500 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20503 getUTCDate: function() {
20507 setDate: function(d) {
20508 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20511 setUTCDate: function(d) {
20513 this.setValue(this.formatDate(this.date));
20516 onRender: function(ct, position)
20519 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20521 this.language = this.language || 'en';
20522 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20523 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20525 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20526 this.format = this.format || 'm/d/y';
20527 this.isInline = false;
20528 this.isInput = true;
20529 this.component = this.el.select('.add-on', true).first() || false;
20530 this.component = (this.component && this.component.length === 0) ? false : this.component;
20531 this.hasInput = this.component && this.inputEl().length;
20533 if (typeof(this.minViewMode === 'string')) {
20534 switch (this.minViewMode) {
20536 this.minViewMode = 1;
20539 this.minViewMode = 2;
20542 this.minViewMode = 0;
20547 if (typeof(this.viewMode === 'string')) {
20548 switch (this.viewMode) {
20561 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20563 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20565 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20567 this.picker().on('mousedown', this.onMousedown, this);
20568 this.picker().on('click', this.onClick, this);
20570 this.picker().addClass('datepicker-dropdown');
20572 this.startViewMode = this.viewMode;
20574 if(this.singleMode){
20575 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20576 v.setVisibilityMode(Roo.Element.DISPLAY);
20580 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20581 v.setStyle('width', '189px');
20585 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20586 if(!this.calendarWeeks){
20591 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20592 v.attr('colspan', function(i, val){
20593 return parseInt(val) + 1;
20598 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20600 this.setStartDate(this.startDate);
20601 this.setEndDate(this.endDate);
20603 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20610 if(this.isInline) {
20615 picker : function()
20617 return this.pickerEl;
20618 // return this.el.select('.datepicker', true).first();
20621 fillDow: function()
20623 var dowCnt = this.weekStart;
20632 if(this.calendarWeeks){
20640 while (dowCnt < this.weekStart + 7) {
20644 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20648 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20651 fillMonths: function()
20654 var months = this.picker().select('>.datepicker-months td', true).first();
20656 months.dom.innerHTML = '';
20662 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20665 months.createChild(month);
20672 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;
20674 if (this.date < this.startDate) {
20675 this.viewDate = new Date(this.startDate);
20676 } else if (this.date > this.endDate) {
20677 this.viewDate = new Date(this.endDate);
20679 this.viewDate = new Date(this.date);
20687 var d = new Date(this.viewDate),
20688 year = d.getUTCFullYear(),
20689 month = d.getUTCMonth(),
20690 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20691 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20692 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20693 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20694 currentDate = this.date && this.date.valueOf(),
20695 today = this.UTCToday();
20697 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20699 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20701 // this.picker.select('>tfoot th.today').
20702 // .text(dates[this.language].today)
20703 // .toggle(this.todayBtn !== false);
20705 this.updateNavArrows();
20708 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20710 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20712 prevMonth.setUTCDate(day);
20714 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20716 var nextMonth = new Date(prevMonth);
20718 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20720 nextMonth = nextMonth.valueOf();
20722 var fillMonths = false;
20724 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20726 while(prevMonth.valueOf() <= nextMonth) {
20729 if (prevMonth.getUTCDay() === this.weekStart) {
20731 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20739 if(this.calendarWeeks){
20740 // ISO 8601: First week contains first thursday.
20741 // ISO also states week starts on Monday, but we can be more abstract here.
20743 // Start of current week: based on weekstart/current date
20744 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20745 // Thursday of this week
20746 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20747 // First Thursday of year, year from thursday
20748 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20749 // Calendar week: ms between thursdays, div ms per day, div 7 days
20750 calWeek = (th - yth) / 864e5 / 7 + 1;
20752 fillMonths.cn.push({
20760 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20762 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20765 if (this.todayHighlight &&
20766 prevMonth.getUTCFullYear() == today.getFullYear() &&
20767 prevMonth.getUTCMonth() == today.getMonth() &&
20768 prevMonth.getUTCDate() == today.getDate()) {
20769 clsName += ' today';
20772 if (currentDate && prevMonth.valueOf() === currentDate) {
20773 clsName += ' active';
20776 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20777 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20778 clsName += ' disabled';
20781 fillMonths.cn.push({
20783 cls: 'day ' + clsName,
20784 html: prevMonth.getDate()
20787 prevMonth.setDate(prevMonth.getDate()+1);
20790 var currentYear = this.date && this.date.getUTCFullYear();
20791 var currentMonth = this.date && this.date.getUTCMonth();
20793 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20795 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20796 v.removeClass('active');
20798 if(currentYear === year && k === currentMonth){
20799 v.addClass('active');
20802 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20803 v.addClass('disabled');
20809 year = parseInt(year/10, 10) * 10;
20811 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20813 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20816 for (var i = -1; i < 11; i++) {
20817 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20819 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20827 showMode: function(dir)
20830 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20833 Roo.each(this.picker().select('>div',true).elements, function(v){
20834 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20837 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20842 if(this.isInline) {
20846 this.picker().removeClass(['bottom', 'top']);
20848 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20850 * place to the top of element!
20854 this.picker().addClass('top');
20855 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20860 this.picker().addClass('bottom');
20862 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20865 parseDate : function(value)
20867 if(!value || value instanceof Date){
20870 var v = Date.parseDate(value, this.format);
20871 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20872 v = Date.parseDate(value, 'Y-m-d');
20874 if(!v && this.altFormats){
20875 if(!this.altFormatsArray){
20876 this.altFormatsArray = this.altFormats.split("|");
20878 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20879 v = Date.parseDate(value, this.altFormatsArray[i]);
20885 formatDate : function(date, fmt)
20887 return (!date || !(date instanceof Date)) ?
20888 date : date.dateFormat(fmt || this.format);
20891 onFocus : function()
20893 Roo.bootstrap.DateField.superclass.onFocus.call(this);
20897 onBlur : function()
20899 Roo.bootstrap.DateField.superclass.onBlur.call(this);
20901 var d = this.inputEl().getValue();
20908 showPopup : function()
20910 this.picker().show();
20914 this.fireEvent('showpopup', this, this.date);
20917 hidePopup : function()
20919 if(this.isInline) {
20922 this.picker().hide();
20923 this.viewMode = this.startViewMode;
20926 this.fireEvent('hidepopup', this, this.date);
20930 onMousedown: function(e)
20932 e.stopPropagation();
20933 e.preventDefault();
20938 Roo.bootstrap.DateField.superclass.keyup.call(this);
20942 setValue: function(v)
20944 if(this.fireEvent('beforeselect', this, v) !== false){
20945 var d = new Date(this.parseDate(v) ).clearTime();
20947 if(isNaN(d.getTime())){
20948 this.date = this.viewDate = '';
20949 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20953 v = this.formatDate(d);
20955 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20957 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20961 this.fireEvent('select', this, this.date);
20965 getValue: function()
20967 return this.formatDate(this.date);
20970 fireKey: function(e)
20972 if (!this.picker().isVisible()){
20973 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20979 var dateChanged = false,
20981 newDate, newViewDate;
20986 e.preventDefault();
20990 if (!this.keyboardNavigation) {
20993 dir = e.keyCode == 37 ? -1 : 1;
20996 newDate = this.moveYear(this.date, dir);
20997 newViewDate = this.moveYear(this.viewDate, dir);
20998 } else if (e.shiftKey){
20999 newDate = this.moveMonth(this.date, dir);
21000 newViewDate = this.moveMonth(this.viewDate, dir);
21002 newDate = new Date(this.date);
21003 newDate.setUTCDate(this.date.getUTCDate() + dir);
21004 newViewDate = new Date(this.viewDate);
21005 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21007 if (this.dateWithinRange(newDate)){
21008 this.date = newDate;
21009 this.viewDate = newViewDate;
21010 this.setValue(this.formatDate(this.date));
21012 e.preventDefault();
21013 dateChanged = true;
21018 if (!this.keyboardNavigation) {
21021 dir = e.keyCode == 38 ? -1 : 1;
21023 newDate = this.moveYear(this.date, dir);
21024 newViewDate = this.moveYear(this.viewDate, dir);
21025 } else if (e.shiftKey){
21026 newDate = this.moveMonth(this.date, dir);
21027 newViewDate = this.moveMonth(this.viewDate, dir);
21029 newDate = new Date(this.date);
21030 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21031 newViewDate = new Date(this.viewDate);
21032 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21034 if (this.dateWithinRange(newDate)){
21035 this.date = newDate;
21036 this.viewDate = newViewDate;
21037 this.setValue(this.formatDate(this.date));
21039 e.preventDefault();
21040 dateChanged = true;
21044 this.setValue(this.formatDate(this.date));
21046 e.preventDefault();
21049 this.setValue(this.formatDate(this.date));
21063 onClick: function(e)
21065 e.stopPropagation();
21066 e.preventDefault();
21068 var target = e.getTarget();
21070 if(target.nodeName.toLowerCase() === 'i'){
21071 target = Roo.get(target).dom.parentNode;
21074 var nodeName = target.nodeName;
21075 var className = target.className;
21076 var html = target.innerHTML;
21077 //Roo.log(nodeName);
21079 switch(nodeName.toLowerCase()) {
21081 switch(className) {
21087 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21088 switch(this.viewMode){
21090 this.viewDate = this.moveMonth(this.viewDate, dir);
21094 this.viewDate = this.moveYear(this.viewDate, dir);
21100 var date = new Date();
21101 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21103 this.setValue(this.formatDate(this.date));
21110 if (className.indexOf('disabled') < 0) {
21111 this.viewDate.setUTCDate(1);
21112 if (className.indexOf('month') > -1) {
21113 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21115 var year = parseInt(html, 10) || 0;
21116 this.viewDate.setUTCFullYear(year);
21120 if(this.singleMode){
21121 this.setValue(this.formatDate(this.viewDate));
21132 //Roo.log(className);
21133 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21134 var day = parseInt(html, 10) || 1;
21135 var year = this.viewDate.getUTCFullYear(),
21136 month = this.viewDate.getUTCMonth();
21138 if (className.indexOf('old') > -1) {
21145 } else if (className.indexOf('new') > -1) {
21153 //Roo.log([year,month,day]);
21154 this.date = this.UTCDate(year, month, day,0,0,0,0);
21155 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21157 //Roo.log(this.formatDate(this.date));
21158 this.setValue(this.formatDate(this.date));
21165 setStartDate: function(startDate)
21167 this.startDate = startDate || -Infinity;
21168 if (this.startDate !== -Infinity) {
21169 this.startDate = this.parseDate(this.startDate);
21172 this.updateNavArrows();
21175 setEndDate: function(endDate)
21177 this.endDate = endDate || Infinity;
21178 if (this.endDate !== Infinity) {
21179 this.endDate = this.parseDate(this.endDate);
21182 this.updateNavArrows();
21185 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21187 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21188 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21189 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21191 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21192 return parseInt(d, 10);
21195 this.updateNavArrows();
21198 updateNavArrows: function()
21200 if(this.singleMode){
21204 var d = new Date(this.viewDate),
21205 year = d.getUTCFullYear(),
21206 month = d.getUTCMonth();
21208 Roo.each(this.picker().select('.prev', true).elements, function(v){
21210 switch (this.viewMode) {
21213 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21219 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21226 Roo.each(this.picker().select('.next', true).elements, function(v){
21228 switch (this.viewMode) {
21231 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21237 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21245 moveMonth: function(date, dir)
21250 var new_date = new Date(date.valueOf()),
21251 day = new_date.getUTCDate(),
21252 month = new_date.getUTCMonth(),
21253 mag = Math.abs(dir),
21255 dir = dir > 0 ? 1 : -1;
21258 // If going back one month, make sure month is not current month
21259 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21261 return new_date.getUTCMonth() == month;
21263 // If going forward one month, make sure month is as expected
21264 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21266 return new_date.getUTCMonth() != new_month;
21268 new_month = month + dir;
21269 new_date.setUTCMonth(new_month);
21270 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21271 if (new_month < 0 || new_month > 11) {
21272 new_month = (new_month + 12) % 12;
21275 // For magnitudes >1, move one month at a time...
21276 for (var i=0; i<mag; i++) {
21277 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21278 new_date = this.moveMonth(new_date, dir);
21280 // ...then reset the day, keeping it in the new month
21281 new_month = new_date.getUTCMonth();
21282 new_date.setUTCDate(day);
21284 return new_month != new_date.getUTCMonth();
21287 // Common date-resetting loop -- if date is beyond end of month, make it
21290 new_date.setUTCDate(--day);
21291 new_date.setUTCMonth(new_month);
21296 moveYear: function(date, dir)
21298 return this.moveMonth(date, dir*12);
21301 dateWithinRange: function(date)
21303 return date >= this.startDate && date <= this.endDate;
21309 this.picker().remove();
21312 validateValue : function(value)
21314 if(this.getVisibilityEl().hasClass('hidden')){
21318 if(value.length < 1) {
21319 if(this.allowBlank){
21325 if(value.length < this.minLength){
21328 if(value.length > this.maxLength){
21332 var vt = Roo.form.VTypes;
21333 if(!vt[this.vtype](value, this)){
21337 if(typeof this.validator == "function"){
21338 var msg = this.validator(value);
21344 if(this.regex && !this.regex.test(value)){
21348 if(typeof(this.parseDate(value)) == 'undefined'){
21352 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21356 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21366 this.date = this.viewDate = '';
21368 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21373 Roo.apply(Roo.bootstrap.DateField, {
21384 html: '<i class="fa fa-arrow-left"/>'
21394 html: '<i class="fa fa-arrow-right"/>'
21436 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21437 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21438 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21439 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21440 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21453 navFnc: 'FullYear',
21458 navFnc: 'FullYear',
21463 Roo.apply(Roo.bootstrap.DateField, {
21467 cls: 'datepicker dropdown-menu roo-dynamic',
21471 cls: 'datepicker-days',
21475 cls: 'table-condensed',
21477 Roo.bootstrap.DateField.head,
21481 Roo.bootstrap.DateField.footer
21488 cls: 'datepicker-months',
21492 cls: 'table-condensed',
21494 Roo.bootstrap.DateField.head,
21495 Roo.bootstrap.DateField.content,
21496 Roo.bootstrap.DateField.footer
21503 cls: 'datepicker-years',
21507 cls: 'table-condensed',
21509 Roo.bootstrap.DateField.head,
21510 Roo.bootstrap.DateField.content,
21511 Roo.bootstrap.DateField.footer
21530 * @class Roo.bootstrap.TimeField
21531 * @extends Roo.bootstrap.Input
21532 * Bootstrap DateField class
21536 * Create a new TimeField
21537 * @param {Object} config The config object
21540 Roo.bootstrap.TimeField = function(config){
21541 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21545 * Fires when this field show.
21546 * @param {Roo.bootstrap.DateField} thisthis
21547 * @param {Mixed} date The date value
21552 * Fires when this field hide.
21553 * @param {Roo.bootstrap.DateField} this
21554 * @param {Mixed} date The date value
21559 * Fires when select a date.
21560 * @param {Roo.bootstrap.DateField} this
21561 * @param {Mixed} date The date value
21567 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21570 * @cfg {String} format
21571 * The default time format string which can be overriden for localization support. The format must be
21572 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21576 onRender: function(ct, position)
21579 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21581 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21583 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21585 this.pop = this.picker().select('>.datepicker-time',true).first();
21586 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21588 this.picker().on('mousedown', this.onMousedown, this);
21589 this.picker().on('click', this.onClick, this);
21591 this.picker().addClass('datepicker-dropdown');
21596 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21597 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21598 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21599 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21600 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21601 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21605 fireKey: function(e){
21606 if (!this.picker().isVisible()){
21607 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21613 e.preventDefault();
21621 this.onTogglePeriod();
21624 this.onIncrementMinutes();
21627 this.onDecrementMinutes();
21636 onClick: function(e) {
21637 e.stopPropagation();
21638 e.preventDefault();
21641 picker : function()
21643 return this.el.select('.datepicker', true).first();
21646 fillTime: function()
21648 var time = this.pop.select('tbody', true).first();
21650 time.dom.innerHTML = '';
21665 cls: 'hours-up glyphicon glyphicon-chevron-up'
21685 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21706 cls: 'timepicker-hour',
21721 cls: 'timepicker-minute',
21736 cls: 'btn btn-primary period',
21758 cls: 'hours-down glyphicon glyphicon-chevron-down'
21778 cls: 'minutes-down glyphicon glyphicon-chevron-down'
21796 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21803 var hours = this.time.getHours();
21804 var minutes = this.time.getMinutes();
21817 hours = hours - 12;
21821 hours = '0' + hours;
21825 minutes = '0' + minutes;
21828 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21829 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21830 this.pop.select('button', true).first().dom.innerHTML = period;
21836 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21838 var cls = ['bottom'];
21840 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21847 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21852 this.picker().addClass(cls.join('-'));
21856 Roo.each(cls, function(c){
21858 _this.picker().setTop(_this.inputEl().getHeight());
21862 _this.picker().setTop(0 - _this.picker().getHeight());
21867 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21871 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21878 onFocus : function()
21880 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21884 onBlur : function()
21886 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21892 this.picker().show();
21897 this.fireEvent('show', this, this.date);
21902 this.picker().hide();
21905 this.fireEvent('hide', this, this.date);
21908 setTime : function()
21911 this.setValue(this.time.format(this.format));
21913 this.fireEvent('select', this, this.date);
21918 onMousedown: function(e){
21919 e.stopPropagation();
21920 e.preventDefault();
21923 onIncrementHours: function()
21925 Roo.log('onIncrementHours');
21926 this.time = this.time.add(Date.HOUR, 1);
21931 onDecrementHours: function()
21933 Roo.log('onDecrementHours');
21934 this.time = this.time.add(Date.HOUR, -1);
21938 onIncrementMinutes: function()
21940 Roo.log('onIncrementMinutes');
21941 this.time = this.time.add(Date.MINUTE, 1);
21945 onDecrementMinutes: function()
21947 Roo.log('onDecrementMinutes');
21948 this.time = this.time.add(Date.MINUTE, -1);
21952 onTogglePeriod: function()
21954 Roo.log('onTogglePeriod');
21955 this.time = this.time.add(Date.HOUR, 12);
21962 Roo.apply(Roo.bootstrap.TimeField, {
21992 cls: 'btn btn-info ok',
22004 Roo.apply(Roo.bootstrap.TimeField, {
22008 cls: 'datepicker dropdown-menu',
22012 cls: 'datepicker-time',
22016 cls: 'table-condensed',
22018 Roo.bootstrap.TimeField.content,
22019 Roo.bootstrap.TimeField.footer
22038 * @class Roo.bootstrap.MonthField
22039 * @extends Roo.bootstrap.Input
22040 * Bootstrap MonthField class
22042 * @cfg {String} language default en
22045 * Create a new MonthField
22046 * @param {Object} config The config object
22049 Roo.bootstrap.MonthField = function(config){
22050 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22055 * Fires when this field show.
22056 * @param {Roo.bootstrap.MonthField} this
22057 * @param {Mixed} date The date value
22062 * Fires when this field hide.
22063 * @param {Roo.bootstrap.MonthField} this
22064 * @param {Mixed} date The date value
22069 * Fires when select a date.
22070 * @param {Roo.bootstrap.MonthField} this
22071 * @param {String} oldvalue The old value
22072 * @param {String} newvalue The new value
22078 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22080 onRender: function(ct, position)
22083 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22085 this.language = this.language || 'en';
22086 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22087 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22089 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22090 this.isInline = false;
22091 this.isInput = true;
22092 this.component = this.el.select('.add-on', true).first() || false;
22093 this.component = (this.component && this.component.length === 0) ? false : this.component;
22094 this.hasInput = this.component && this.inputEL().length;
22096 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22098 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22100 this.picker().on('mousedown', this.onMousedown, this);
22101 this.picker().on('click', this.onClick, this);
22103 this.picker().addClass('datepicker-dropdown');
22105 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22106 v.setStyle('width', '189px');
22113 if(this.isInline) {
22119 setValue: function(v, suppressEvent)
22121 var o = this.getValue();
22123 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22127 if(suppressEvent !== true){
22128 this.fireEvent('select', this, o, v);
22133 getValue: function()
22138 onClick: function(e)
22140 e.stopPropagation();
22141 e.preventDefault();
22143 var target = e.getTarget();
22145 if(target.nodeName.toLowerCase() === 'i'){
22146 target = Roo.get(target).dom.parentNode;
22149 var nodeName = target.nodeName;
22150 var className = target.className;
22151 var html = target.innerHTML;
22153 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22157 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22159 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22165 picker : function()
22167 return this.pickerEl;
22170 fillMonths: function()
22173 var months = this.picker().select('>.datepicker-months td', true).first();
22175 months.dom.innerHTML = '';
22181 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22184 months.createChild(month);
22193 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22194 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22197 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22198 e.removeClass('active');
22200 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22201 e.addClass('active');
22208 if(this.isInline) {
22212 this.picker().removeClass(['bottom', 'top']);
22214 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22216 * place to the top of element!
22220 this.picker().addClass('top');
22221 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22226 this.picker().addClass('bottom');
22228 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22231 onFocus : function()
22233 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22237 onBlur : function()
22239 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22241 var d = this.inputEl().getValue();
22250 this.picker().show();
22251 this.picker().select('>.datepicker-months', true).first().show();
22255 this.fireEvent('show', this, this.date);
22260 if(this.isInline) {
22263 this.picker().hide();
22264 this.fireEvent('hide', this, this.date);
22268 onMousedown: function(e)
22270 e.stopPropagation();
22271 e.preventDefault();
22276 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22280 fireKey: function(e)
22282 if (!this.picker().isVisible()){
22283 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22294 e.preventDefault();
22298 dir = e.keyCode == 37 ? -1 : 1;
22300 this.vIndex = this.vIndex + dir;
22302 if(this.vIndex < 0){
22306 if(this.vIndex > 11){
22310 if(isNaN(this.vIndex)){
22314 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22320 dir = e.keyCode == 38 ? -1 : 1;
22322 this.vIndex = this.vIndex + dir * 4;
22324 if(this.vIndex < 0){
22328 if(this.vIndex > 11){
22332 if(isNaN(this.vIndex)){
22336 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22341 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22342 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22346 e.preventDefault();
22349 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22350 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22366 this.picker().remove();
22371 Roo.apply(Roo.bootstrap.MonthField, {
22390 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22391 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22396 Roo.apply(Roo.bootstrap.MonthField, {
22400 cls: 'datepicker dropdown-menu roo-dynamic',
22404 cls: 'datepicker-months',
22408 cls: 'table-condensed',
22410 Roo.bootstrap.DateField.content
22430 * @class Roo.bootstrap.CheckBox
22431 * @extends Roo.bootstrap.Input
22432 * Bootstrap CheckBox class
22434 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22435 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22436 * @cfg {String} boxLabel The text that appears beside the checkbox
22437 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22438 * @cfg {Boolean} checked initnal the element
22439 * @cfg {Boolean} inline inline the element (default false)
22440 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22441 * @cfg {String} tooltip label tooltip
22444 * Create a new CheckBox
22445 * @param {Object} config The config object
22448 Roo.bootstrap.CheckBox = function(config){
22449 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22454 * Fires when the element is checked or unchecked.
22455 * @param {Roo.bootstrap.CheckBox} this This input
22456 * @param {Boolean} checked The new checked value
22461 * Fires when the element is click.
22462 * @param {Roo.bootstrap.CheckBox} this This input
22469 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22471 inputType: 'checkbox',
22480 // checkbox success does not make any sense really..
22485 getAutoCreate : function()
22487 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22493 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22496 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22502 type : this.inputType,
22503 value : this.inputValue,
22504 cls : 'roo-' + this.inputType, //'form-box',
22505 placeholder : this.placeholder || ''
22509 if(this.inputType != 'radio'){
22513 cls : 'roo-hidden-value',
22514 value : this.checked ? this.inputValue : this.valueOff
22519 if (this.weight) { // Validity check?
22520 cfg.cls += " " + this.inputType + "-" + this.weight;
22523 if (this.disabled) {
22524 input.disabled=true;
22528 input.checked = this.checked;
22533 input.name = this.name;
22535 if(this.inputType != 'radio'){
22536 hidden.name = this.name;
22537 input.name = '_hidden_' + this.name;
22542 input.cls += ' input-' + this.size;
22547 ['xs','sm','md','lg'].map(function(size){
22548 if (settings[size]) {
22549 cfg.cls += ' col-' + size + '-' + settings[size];
22553 var inputblock = input;
22555 if (this.before || this.after) {
22558 cls : 'input-group',
22563 inputblock.cn.push({
22565 cls : 'input-group-addon',
22570 inputblock.cn.push(input);
22572 if(this.inputType != 'radio'){
22573 inputblock.cn.push(hidden);
22577 inputblock.cn.push({
22579 cls : 'input-group-addon',
22585 var boxLabelCfg = false;
22591 //'for': id, // box label is handled by onclick - so no for...
22593 html: this.boxLabel
22596 boxLabelCfg.tooltip = this.tooltip;
22602 if (align ==='left' && this.fieldLabel.length) {
22603 // Roo.log("left and has label");
22608 cls : 'control-label',
22609 html : this.fieldLabel
22620 cfg.cn[1].cn.push(boxLabelCfg);
22623 if(this.labelWidth > 12){
22624 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22627 if(this.labelWidth < 13 && this.labelmd == 0){
22628 this.labelmd = this.labelWidth;
22631 if(this.labellg > 0){
22632 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22633 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22636 if(this.labelmd > 0){
22637 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22638 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22641 if(this.labelsm > 0){
22642 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22643 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22646 if(this.labelxs > 0){
22647 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22648 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22651 } else if ( this.fieldLabel.length) {
22652 // Roo.log(" label");
22656 tag: this.boxLabel ? 'span' : 'label',
22658 cls: 'control-label box-input-label',
22659 //cls : 'input-group-addon',
22660 html : this.fieldLabel
22667 cfg.cn.push(boxLabelCfg);
22672 // Roo.log(" no label && no align");
22673 cfg.cn = [ inputblock ] ;
22675 cfg.cn.push(boxLabelCfg);
22683 if(this.inputType != 'radio'){
22684 cfg.cn.push(hidden);
22692 * return the real input element.
22694 inputEl: function ()
22696 return this.el.select('input.roo-' + this.inputType,true).first();
22698 hiddenEl: function ()
22700 return this.el.select('input.roo-hidden-value',true).first();
22703 labelEl: function()
22705 return this.el.select('label.control-label',true).first();
22707 /* depricated... */
22711 return this.labelEl();
22714 boxLabelEl: function()
22716 return this.el.select('label.box-label',true).first();
22719 initEvents : function()
22721 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22723 this.inputEl().on('click', this.onClick, this);
22725 if (this.boxLabel) {
22726 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22729 this.startValue = this.getValue();
22732 Roo.bootstrap.CheckBox.register(this);
22736 onClick : function(e)
22738 if(this.fireEvent('click', this, e) !== false){
22739 this.setChecked(!this.checked);
22744 setChecked : function(state,suppressEvent)
22746 this.startValue = this.getValue();
22748 if(this.inputType == 'radio'){
22750 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22751 e.dom.checked = false;
22754 this.inputEl().dom.checked = true;
22756 this.inputEl().dom.value = this.inputValue;
22758 if(suppressEvent !== true){
22759 this.fireEvent('check', this, true);
22767 this.checked = state;
22769 this.inputEl().dom.checked = state;
22772 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22774 if(suppressEvent !== true){
22775 this.fireEvent('check', this, state);
22781 getValue : function()
22783 if(this.inputType == 'radio'){
22784 return this.getGroupValue();
22787 return this.hiddenEl().dom.value;
22791 getGroupValue : function()
22793 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22797 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22800 setValue : function(v,suppressEvent)
22802 if(this.inputType == 'radio'){
22803 this.setGroupValue(v, suppressEvent);
22807 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22812 setGroupValue : function(v, suppressEvent)
22814 this.startValue = this.getValue();
22816 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22817 e.dom.checked = false;
22819 if(e.dom.value == v){
22820 e.dom.checked = true;
22824 if(suppressEvent !== true){
22825 this.fireEvent('check', this, true);
22833 validate : function()
22835 if(this.getVisibilityEl().hasClass('hidden')){
22841 (this.inputType == 'radio' && this.validateRadio()) ||
22842 (this.inputType == 'checkbox' && this.validateCheckbox())
22848 this.markInvalid();
22852 validateRadio : function()
22854 if(this.getVisibilityEl().hasClass('hidden')){
22858 if(this.allowBlank){
22864 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22865 if(!e.dom.checked){
22877 validateCheckbox : function()
22880 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22881 //return (this.getValue() == this.inputValue) ? true : false;
22884 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22892 for(var i in group){
22893 if(group[i].el.isVisible(true)){
22901 for(var i in group){
22906 r = (group[i].getValue() == group[i].inputValue) ? true : false;
22913 * Mark this field as valid
22915 markValid : function()
22919 this.fireEvent('valid', this);
22921 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22924 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22931 if(this.inputType == 'radio'){
22932 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22933 var fg = e.findParent('.form-group', false, true);
22934 if (Roo.bootstrap.version == 3) {
22935 fg.removeClass([_this.invalidClass, _this.validClass]);
22936 fg.addClass(_this.validClass);
22938 fg.removeClass(['is-valid', 'is-invalid']);
22939 fg.addClass('is-valid');
22947 var fg = this.el.findParent('.form-group', false, true);
22948 if (Roo.bootstrap.version == 3) {
22949 fg.removeClass([this.invalidClass, this.validClass]);
22950 fg.addClass(this.validClass);
22952 fg.removeClass(['is-valid', 'is-invalid']);
22953 fg.addClass('is-valid');
22958 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22964 for(var i in group){
22965 var fg = group[i].el.findParent('.form-group', false, true);
22966 if (Roo.bootstrap.version == 3) {
22967 fg.removeClass([this.invalidClass, this.validClass]);
22968 fg.addClass(this.validClass);
22970 fg.removeClass(['is-valid', 'is-invalid']);
22971 fg.addClass('is-valid');
22977 * Mark this field as invalid
22978 * @param {String} msg The validation message
22980 markInvalid : function(msg)
22982 if(this.allowBlank){
22988 this.fireEvent('invalid', this, msg);
22990 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22993 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22997 label.markInvalid();
23000 if(this.inputType == 'radio'){
23002 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23003 var fg = e.findParent('.form-group', false, true);
23004 if (Roo.bootstrap.version == 3) {
23005 fg.removeClass([_this.invalidClass, _this.validClass]);
23006 fg.addClass(_this.invalidClass);
23008 fg.removeClass(['is-invalid', 'is-valid']);
23009 fg.addClass('is-invalid');
23017 var fg = this.el.findParent('.form-group', false, true);
23018 if (Roo.bootstrap.version == 3) {
23019 fg.removeClass([_this.invalidClass, _this.validClass]);
23020 fg.addClass(_this.invalidClass);
23022 fg.removeClass(['is-invalid', 'is-valid']);
23023 fg.addClass('is-invalid');
23028 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23034 for(var i in group){
23035 var fg = group[i].el.findParent('.form-group', false, true);
23036 if (Roo.bootstrap.version == 3) {
23037 fg.removeClass([_this.invalidClass, _this.validClass]);
23038 fg.addClass(_this.invalidClass);
23040 fg.removeClass(['is-invalid', 'is-valid']);
23041 fg.addClass('is-invalid');
23047 clearInvalid : function()
23049 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23051 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23053 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23055 if (label && label.iconEl) {
23056 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23057 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23061 disable : function()
23063 if(this.inputType != 'radio'){
23064 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23071 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23072 _this.getActionEl().addClass(this.disabledClass);
23073 e.dom.disabled = true;
23077 this.disabled = true;
23078 this.fireEvent("disable", this);
23082 enable : function()
23084 if(this.inputType != 'radio'){
23085 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23092 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23093 _this.getActionEl().removeClass(this.disabledClass);
23094 e.dom.disabled = false;
23098 this.disabled = false;
23099 this.fireEvent("enable", this);
23103 setBoxLabel : function(v)
23108 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23114 Roo.apply(Roo.bootstrap.CheckBox, {
23119 * register a CheckBox Group
23120 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23122 register : function(checkbox)
23124 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23125 this.groups[checkbox.groupId] = {};
23128 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23132 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23136 * fetch a CheckBox Group based on the group ID
23137 * @param {string} the group ID
23138 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23140 get: function(groupId) {
23141 if (typeof(this.groups[groupId]) == 'undefined') {
23145 return this.groups[groupId] ;
23158 * @class Roo.bootstrap.Radio
23159 * @extends Roo.bootstrap.Component
23160 * Bootstrap Radio class
23161 * @cfg {String} boxLabel - the label associated
23162 * @cfg {String} value - the value of radio
23165 * Create a new Radio
23166 * @param {Object} config The config object
23168 Roo.bootstrap.Radio = function(config){
23169 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23173 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23179 getAutoCreate : function()
23183 cls : 'form-group radio',
23188 html : this.boxLabel
23196 initEvents : function()
23198 this.parent().register(this);
23200 this.el.on('click', this.onClick, this);
23204 onClick : function(e)
23206 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23207 this.setChecked(true);
23211 setChecked : function(state, suppressEvent)
23213 this.parent().setValue(this.value, suppressEvent);
23217 setBoxLabel : function(v)
23222 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23237 * @class Roo.bootstrap.SecurePass
23238 * @extends Roo.bootstrap.Input
23239 * Bootstrap SecurePass class
23243 * Create a new SecurePass
23244 * @param {Object} config The config object
23247 Roo.bootstrap.SecurePass = function (config) {
23248 // these go here, so the translation tool can replace them..
23250 PwdEmpty: "Please type a password, and then retype it to confirm.",
23251 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23252 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23253 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23254 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23255 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23256 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23257 TooWeak: "Your password is Too Weak."
23259 this.meterLabel = "Password strength:";
23260 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23261 this.meterClass = [
23262 "roo-password-meter-tooweak",
23263 "roo-password-meter-weak",
23264 "roo-password-meter-medium",
23265 "roo-password-meter-strong",
23266 "roo-password-meter-grey"
23271 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23274 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23276 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23278 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23279 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23280 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23281 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23282 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23283 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23284 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23294 * @cfg {String/Object} Label for the strength meter (defaults to
23295 * 'Password strength:')
23300 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23301 * ['Weak', 'Medium', 'Strong'])
23304 pwdStrengths: false,
23317 initEvents: function ()
23319 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23321 if (this.el.is('input[type=password]') && Roo.isSafari) {
23322 this.el.on('keydown', this.SafariOnKeyDown, this);
23325 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23328 onRender: function (ct, position)
23330 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23331 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23332 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23334 this.trigger.createChild({
23339 cls: 'roo-password-meter-grey col-xs-12',
23342 //width: this.meterWidth + 'px'
23346 cls: 'roo-password-meter-text'
23352 if (this.hideTrigger) {
23353 this.trigger.setDisplayed(false);
23355 this.setSize(this.width || '', this.height || '');
23358 onDestroy: function ()
23360 if (this.trigger) {
23361 this.trigger.removeAllListeners();
23362 this.trigger.remove();
23365 this.wrap.remove();
23367 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23370 checkStrength: function ()
23372 var pwd = this.inputEl().getValue();
23373 if (pwd == this._lastPwd) {
23378 if (this.ClientSideStrongPassword(pwd)) {
23380 } else if (this.ClientSideMediumPassword(pwd)) {
23382 } else if (this.ClientSideWeakPassword(pwd)) {
23388 Roo.log('strength1: ' + strength);
23390 //var pm = this.trigger.child('div/div/div').dom;
23391 var pm = this.trigger.child('div/div');
23392 pm.removeClass(this.meterClass);
23393 pm.addClass(this.meterClass[strength]);
23396 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23398 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23400 this._lastPwd = pwd;
23404 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23406 this._lastPwd = '';
23408 var pm = this.trigger.child('div/div');
23409 pm.removeClass(this.meterClass);
23410 pm.addClass('roo-password-meter-grey');
23413 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23416 this.inputEl().dom.type='password';
23419 validateValue: function (value)
23422 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23425 if (value.length == 0) {
23426 if (this.allowBlank) {
23427 this.clearInvalid();
23431 this.markInvalid(this.errors.PwdEmpty);
23432 this.errorMsg = this.errors.PwdEmpty;
23440 if ('[\x21-\x7e]*'.match(value)) {
23441 this.markInvalid(this.errors.PwdBadChar);
23442 this.errorMsg = this.errors.PwdBadChar;
23445 if (value.length < 6) {
23446 this.markInvalid(this.errors.PwdShort);
23447 this.errorMsg = this.errors.PwdShort;
23450 if (value.length > 16) {
23451 this.markInvalid(this.errors.PwdLong);
23452 this.errorMsg = this.errors.PwdLong;
23456 if (this.ClientSideStrongPassword(value)) {
23458 } else if (this.ClientSideMediumPassword(value)) {
23460 } else if (this.ClientSideWeakPassword(value)) {
23467 if (strength < 2) {
23468 //this.markInvalid(this.errors.TooWeak);
23469 this.errorMsg = this.errors.TooWeak;
23474 console.log('strength2: ' + strength);
23476 //var pm = this.trigger.child('div/div/div').dom;
23478 var pm = this.trigger.child('div/div');
23479 pm.removeClass(this.meterClass);
23480 pm.addClass(this.meterClass[strength]);
23482 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23484 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23486 this.errorMsg = '';
23490 CharacterSetChecks: function (type)
23493 this.fResult = false;
23496 isctype: function (character, type)
23499 case this.kCapitalLetter:
23500 if (character >= 'A' && character <= 'Z') {
23505 case this.kSmallLetter:
23506 if (character >= 'a' && character <= 'z') {
23512 if (character >= '0' && character <= '9') {
23517 case this.kPunctuation:
23518 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23529 IsLongEnough: function (pwd, size)
23531 return !(pwd == null || isNaN(size) || pwd.length < size);
23534 SpansEnoughCharacterSets: function (word, nb)
23536 if (!this.IsLongEnough(word, nb))
23541 var characterSetChecks = new Array(
23542 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23543 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23546 for (var index = 0; index < word.length; ++index) {
23547 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23548 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23549 characterSetChecks[nCharSet].fResult = true;
23556 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23557 if (characterSetChecks[nCharSet].fResult) {
23562 if (nCharSets < nb) {
23568 ClientSideStrongPassword: function (pwd)
23570 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23573 ClientSideMediumPassword: function (pwd)
23575 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23578 ClientSideWeakPassword: function (pwd)
23580 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23583 })//<script type="text/javascript">
23586 * Based Ext JS Library 1.1.1
23587 * Copyright(c) 2006-2007, Ext JS, LLC.
23593 * @class Roo.HtmlEditorCore
23594 * @extends Roo.Component
23595 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23597 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23600 Roo.HtmlEditorCore = function(config){
23603 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23608 * @event initialize
23609 * Fires when the editor is fully initialized (including the iframe)
23610 * @param {Roo.HtmlEditorCore} this
23615 * Fires when the editor is first receives the focus. Any insertion must wait
23616 * until after this event.
23617 * @param {Roo.HtmlEditorCore} this
23621 * @event beforesync
23622 * Fires before the textarea is updated with content from the editor iframe. Return false
23623 * to cancel the sync.
23624 * @param {Roo.HtmlEditorCore} this
23625 * @param {String} html
23629 * @event beforepush
23630 * Fires before the iframe editor is updated with content from the textarea. Return false
23631 * to cancel the push.
23632 * @param {Roo.HtmlEditorCore} this
23633 * @param {String} html
23638 * Fires when the textarea is updated with content from the editor iframe.
23639 * @param {Roo.HtmlEditorCore} this
23640 * @param {String} html
23645 * Fires when the iframe editor is updated with content from the textarea.
23646 * @param {Roo.HtmlEditorCore} this
23647 * @param {String} html
23652 * @event editorevent
23653 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23654 * @param {Roo.HtmlEditorCore} this
23660 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23662 // defaults : white / black...
23663 this.applyBlacklists();
23670 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23674 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23680 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23685 * @cfg {Number} height (in pixels)
23689 * @cfg {Number} width (in pixels)
23694 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23697 stylesheets: false,
23702 // private properties
23703 validationEvent : false,
23705 initialized : false,
23707 sourceEditMode : false,
23708 onFocus : Roo.emptyFn,
23710 hideMode:'offsets',
23714 // blacklist + whitelisted elements..
23721 * Protected method that will not generally be called directly. It
23722 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23723 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23725 getDocMarkup : function(){
23729 // inherit styels from page...??
23730 if (this.stylesheets === false) {
23732 Roo.get(document.head).select('style').each(function(node) {
23733 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23736 Roo.get(document.head).select('link').each(function(node) {
23737 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23740 } else if (!this.stylesheets.length) {
23742 st = '<style type="text/css">' +
23743 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23746 st = '<style type="text/css">' +
23751 st += '<style type="text/css">' +
23752 'IMG { cursor: pointer } ' +
23755 var cls = 'roo-htmleditor-body';
23757 if(this.bodyCls.length){
23758 cls += ' ' + this.bodyCls;
23761 return '<html><head>' + st +
23762 //<style type="text/css">' +
23763 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23765 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23769 onRender : function(ct, position)
23772 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23773 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23776 this.el.dom.style.border = '0 none';
23777 this.el.dom.setAttribute('tabIndex', -1);
23778 this.el.addClass('x-hidden hide');
23782 if(Roo.isIE){ // fix IE 1px bogus margin
23783 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23787 this.frameId = Roo.id();
23791 var iframe = this.owner.wrap.createChild({
23793 cls: 'form-control', // bootstrap..
23795 name: this.frameId,
23796 frameBorder : 'no',
23797 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
23802 this.iframe = iframe.dom;
23804 this.assignDocWin();
23806 this.doc.designMode = 'on';
23809 this.doc.write(this.getDocMarkup());
23813 var task = { // must defer to wait for browser to be ready
23815 //console.log("run task?" + this.doc.readyState);
23816 this.assignDocWin();
23817 if(this.doc.body || this.doc.readyState == 'complete'){
23819 this.doc.designMode="on";
23823 Roo.TaskMgr.stop(task);
23824 this.initEditor.defer(10, this);
23831 Roo.TaskMgr.start(task);
23836 onResize : function(w, h)
23838 Roo.log('resize: ' +w + ',' + h );
23839 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23843 if(typeof w == 'number'){
23845 this.iframe.style.width = w + 'px';
23847 if(typeof h == 'number'){
23849 this.iframe.style.height = h + 'px';
23851 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23858 * Toggles the editor between standard and source edit mode.
23859 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23861 toggleSourceEdit : function(sourceEditMode){
23863 this.sourceEditMode = sourceEditMode === true;
23865 if(this.sourceEditMode){
23867 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
23870 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23871 //this.iframe.className = '';
23874 //this.setSize(this.owner.wrap.getSize());
23875 //this.fireEvent('editmodechange', this, this.sourceEditMode);
23882 * Protected method that will not generally be called directly. If you need/want
23883 * custom HTML cleanup, this is the method you should override.
23884 * @param {String} html The HTML to be cleaned
23885 * return {String} The cleaned HTML
23887 cleanHtml : function(html){
23888 html = String(html);
23889 if(html.length > 5){
23890 if(Roo.isSafari){ // strip safari nonsense
23891 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23894 if(html == ' '){
23901 * HTML Editor -> Textarea
23902 * Protected method that will not generally be called directly. Syncs the contents
23903 * of the editor iframe with the textarea.
23905 syncValue : function(){
23906 if(this.initialized){
23907 var bd = (this.doc.body || this.doc.documentElement);
23908 //this.cleanUpPaste(); -- this is done else where and causes havoc..
23909 var html = bd.innerHTML;
23911 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23912 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23914 html = '<div style="'+m[0]+'">' + html + '</div>';
23917 html = this.cleanHtml(html);
23918 // fix up the special chars.. normaly like back quotes in word...
23919 // however we do not want to do this with chinese..
23920 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23922 var cc = match.charCodeAt();
23924 // Get the character value, handling surrogate pairs
23925 if (match.length == 2) {
23926 // It's a surrogate pair, calculate the Unicode code point
23927 var high = match.charCodeAt(0) - 0xD800;
23928 var low = match.charCodeAt(1) - 0xDC00;
23929 cc = (high * 0x400) + low + 0x10000;
23931 (cc >= 0x4E00 && cc < 0xA000 ) ||
23932 (cc >= 0x3400 && cc < 0x4E00 ) ||
23933 (cc >= 0xf900 && cc < 0xfb00 )
23938 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23939 return "&#" + cc + ";";
23946 if(this.owner.fireEvent('beforesync', this, html) !== false){
23947 this.el.dom.value = html;
23948 this.owner.fireEvent('sync', this, html);
23954 * Protected method that will not generally be called directly. Pushes the value of the textarea
23955 * into the iframe editor.
23957 pushValue : function(){
23958 if(this.initialized){
23959 var v = this.el.dom.value.trim();
23961 // if(v.length < 1){
23965 if(this.owner.fireEvent('beforepush', this, v) !== false){
23966 var d = (this.doc.body || this.doc.documentElement);
23968 this.cleanUpPaste();
23969 this.el.dom.value = d.innerHTML;
23970 this.owner.fireEvent('push', this, v);
23976 deferFocus : function(){
23977 this.focus.defer(10, this);
23981 focus : function(){
23982 if(this.win && !this.sourceEditMode){
23989 assignDocWin: function()
23991 var iframe = this.iframe;
23994 this.doc = iframe.contentWindow.document;
23995 this.win = iframe.contentWindow;
23997 // if (!Roo.get(this.frameId)) {
24000 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24001 // this.win = Roo.get(this.frameId).dom.contentWindow;
24003 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24007 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24008 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24013 initEditor : function(){
24014 //console.log("INIT EDITOR");
24015 this.assignDocWin();
24019 this.doc.designMode="on";
24021 this.doc.write(this.getDocMarkup());
24024 var dbody = (this.doc.body || this.doc.documentElement);
24025 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24026 // this copies styles from the containing element into thsi one..
24027 // not sure why we need all of this..
24028 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24030 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24031 //ss['background-attachment'] = 'fixed'; // w3c
24032 dbody.bgProperties = 'fixed'; // ie
24033 //Roo.DomHelper.applyStyles(dbody, ss);
24034 Roo.EventManager.on(this.doc, {
24035 //'mousedown': this.onEditorEvent,
24036 'mouseup': this.onEditorEvent,
24037 'dblclick': this.onEditorEvent,
24038 'click': this.onEditorEvent,
24039 'keyup': this.onEditorEvent,
24044 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24046 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24047 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24049 this.initialized = true;
24051 this.owner.fireEvent('initialize', this);
24056 onDestroy : function(){
24062 //for (var i =0; i < this.toolbars.length;i++) {
24063 // // fixme - ask toolbars for heights?
24064 // this.toolbars[i].onDestroy();
24067 //this.wrap.dom.innerHTML = '';
24068 //this.wrap.remove();
24073 onFirstFocus : function(){
24075 this.assignDocWin();
24078 this.activated = true;
24081 if(Roo.isGecko){ // prevent silly gecko errors
24083 var s = this.win.getSelection();
24084 if(!s.focusNode || s.focusNode.nodeType != 3){
24085 var r = s.getRangeAt(0);
24086 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24091 this.execCmd('useCSS', true);
24092 this.execCmd('styleWithCSS', false);
24095 this.owner.fireEvent('activate', this);
24099 adjustFont: function(btn){
24100 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24101 //if(Roo.isSafari){ // safari
24104 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24105 if(Roo.isSafari){ // safari
24106 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24107 v = (v < 10) ? 10 : v;
24108 v = (v > 48) ? 48 : v;
24109 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24114 v = Math.max(1, v+adjust);
24116 this.execCmd('FontSize', v );
24119 onEditorEvent : function(e)
24121 this.owner.fireEvent('editorevent', this, e);
24122 // this.updateToolbar();
24123 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24126 insertTag : function(tg)
24128 // could be a bit smarter... -> wrap the current selected tRoo..
24129 if (tg.toLowerCase() == 'span' ||
24130 tg.toLowerCase() == 'code' ||
24131 tg.toLowerCase() == 'sup' ||
24132 tg.toLowerCase() == 'sub'
24135 range = this.createRange(this.getSelection());
24136 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24137 wrappingNode.appendChild(range.extractContents());
24138 range.insertNode(wrappingNode);
24145 this.execCmd("formatblock", tg);
24149 insertText : function(txt)
24153 var range = this.createRange();
24154 range.deleteContents();
24155 //alert(Sender.getAttribute('label'));
24157 range.insertNode(this.doc.createTextNode(txt));
24163 * Executes a Midas editor command on the editor document and performs necessary focus and
24164 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24165 * @param {String} cmd The Midas command
24166 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24168 relayCmd : function(cmd, value){
24170 this.execCmd(cmd, value);
24171 this.owner.fireEvent('editorevent', this);
24172 //this.updateToolbar();
24173 this.owner.deferFocus();
24177 * Executes a Midas editor command directly on the editor document.
24178 * For visual commands, you should use {@link #relayCmd} instead.
24179 * <b>This should only be called after the editor is initialized.</b>
24180 * @param {String} cmd The Midas command
24181 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24183 execCmd : function(cmd, value){
24184 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24191 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24193 * @param {String} text | dom node..
24195 insertAtCursor : function(text)
24198 if(!this.activated){
24204 var r = this.doc.selection.createRange();
24215 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24219 // from jquery ui (MIT licenced)
24221 var win = this.win;
24223 if (win.getSelection && win.getSelection().getRangeAt) {
24224 range = win.getSelection().getRangeAt(0);
24225 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24226 range.insertNode(node);
24227 } else if (win.document.selection && win.document.selection.createRange) {
24228 // no firefox support
24229 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24230 win.document.selection.createRange().pasteHTML(txt);
24232 // no firefox support
24233 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24234 this.execCmd('InsertHTML', txt);
24243 mozKeyPress : function(e){
24245 var c = e.getCharCode(), cmd;
24248 c = String.fromCharCode(c).toLowerCase();
24262 this.cleanUpPaste.defer(100, this);
24270 e.preventDefault();
24278 fixKeys : function(){ // load time branching for fastest keydown performance
24280 return function(e){
24281 var k = e.getKey(), r;
24284 r = this.doc.selection.createRange();
24287 r.pasteHTML('    ');
24294 r = this.doc.selection.createRange();
24296 var target = r.parentElement();
24297 if(!target || target.tagName.toLowerCase() != 'li'){
24299 r.pasteHTML('<br />');
24305 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24306 this.cleanUpPaste.defer(100, this);
24312 }else if(Roo.isOpera){
24313 return function(e){
24314 var k = e.getKey();
24318 this.execCmd('InsertHTML','    ');
24321 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24322 this.cleanUpPaste.defer(100, this);
24327 }else if(Roo.isSafari){
24328 return function(e){
24329 var k = e.getKey();
24333 this.execCmd('InsertText','\t');
24337 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24338 this.cleanUpPaste.defer(100, this);
24346 getAllAncestors: function()
24348 var p = this.getSelectedNode();
24351 a.push(p); // push blank onto stack..
24352 p = this.getParentElement();
24356 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24360 a.push(this.doc.body);
24364 lastSelNode : false,
24367 getSelection : function()
24369 this.assignDocWin();
24370 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24373 getSelectedNode: function()
24375 // this may only work on Gecko!!!
24377 // should we cache this!!!!
24382 var range = this.createRange(this.getSelection()).cloneRange();
24385 var parent = range.parentElement();
24387 var testRange = range.duplicate();
24388 testRange.moveToElementText(parent);
24389 if (testRange.inRange(range)) {
24392 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24395 parent = parent.parentElement;
24400 // is ancestor a text element.
24401 var ac = range.commonAncestorContainer;
24402 if (ac.nodeType == 3) {
24403 ac = ac.parentNode;
24406 var ar = ac.childNodes;
24409 var other_nodes = [];
24410 var has_other_nodes = false;
24411 for (var i=0;i<ar.length;i++) {
24412 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24415 // fullly contained node.
24417 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24422 // probably selected..
24423 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24424 other_nodes.push(ar[i]);
24428 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24433 has_other_nodes = true;
24435 if (!nodes.length && other_nodes.length) {
24436 nodes= other_nodes;
24438 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24444 createRange: function(sel)
24446 // this has strange effects when using with
24447 // top toolbar - not sure if it's a great idea.
24448 //this.editor.contentWindow.focus();
24449 if (typeof sel != "undefined") {
24451 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24453 return this.doc.createRange();
24456 return this.doc.createRange();
24459 getParentElement: function()
24462 this.assignDocWin();
24463 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24465 var range = this.createRange(sel);
24468 var p = range.commonAncestorContainer;
24469 while (p.nodeType == 3) { // text node
24480 * Range intersection.. the hard stuff...
24484 * [ -- selected range --- ]
24488 * if end is before start or hits it. fail.
24489 * if start is after end or hits it fail.
24491 * if either hits (but other is outside. - then it's not
24497 // @see http://www.thismuchiknow.co.uk/?p=64.
24498 rangeIntersectsNode : function(range, node)
24500 var nodeRange = node.ownerDocument.createRange();
24502 nodeRange.selectNode(node);
24504 nodeRange.selectNodeContents(node);
24507 var rangeStartRange = range.cloneRange();
24508 rangeStartRange.collapse(true);
24510 var rangeEndRange = range.cloneRange();
24511 rangeEndRange.collapse(false);
24513 var nodeStartRange = nodeRange.cloneRange();
24514 nodeStartRange.collapse(true);
24516 var nodeEndRange = nodeRange.cloneRange();
24517 nodeEndRange.collapse(false);
24519 return rangeStartRange.compareBoundaryPoints(
24520 Range.START_TO_START, nodeEndRange) == -1 &&
24521 rangeEndRange.compareBoundaryPoints(
24522 Range.START_TO_START, nodeStartRange) == 1;
24526 rangeCompareNode : function(range, node)
24528 var nodeRange = node.ownerDocument.createRange();
24530 nodeRange.selectNode(node);
24532 nodeRange.selectNodeContents(node);
24536 range.collapse(true);
24538 nodeRange.collapse(true);
24540 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24541 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24543 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24545 var nodeIsBefore = ss == 1;
24546 var nodeIsAfter = ee == -1;
24548 if (nodeIsBefore && nodeIsAfter) {
24551 if (!nodeIsBefore && nodeIsAfter) {
24552 return 1; //right trailed.
24555 if (nodeIsBefore && !nodeIsAfter) {
24556 return 2; // left trailed.
24562 // private? - in a new class?
24563 cleanUpPaste : function()
24565 // cleans up the whole document..
24566 Roo.log('cleanuppaste');
24568 this.cleanUpChildren(this.doc.body);
24569 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24570 if (clean != this.doc.body.innerHTML) {
24571 this.doc.body.innerHTML = clean;
24576 cleanWordChars : function(input) {// change the chars to hex code
24577 var he = Roo.HtmlEditorCore;
24579 var output = input;
24580 Roo.each(he.swapCodes, function(sw) {
24581 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24583 output = output.replace(swapper, sw[1]);
24590 cleanUpChildren : function (n)
24592 if (!n.childNodes.length) {
24595 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24596 this.cleanUpChild(n.childNodes[i]);
24603 cleanUpChild : function (node)
24606 //console.log(node);
24607 if (node.nodeName == "#text") {
24608 // clean up silly Windows -- stuff?
24611 if (node.nodeName == "#comment") {
24612 node.parentNode.removeChild(node);
24613 // clean up silly Windows -- stuff?
24616 var lcname = node.tagName.toLowerCase();
24617 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24618 // whitelist of tags..
24620 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24622 node.parentNode.removeChild(node);
24627 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24629 // spans with no attributes - just remove them..
24630 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24631 remove_keep_children = true;
24634 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24635 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24637 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24638 // remove_keep_children = true;
24641 if (remove_keep_children) {
24642 this.cleanUpChildren(node);
24643 // inserts everything just before this node...
24644 while (node.childNodes.length) {
24645 var cn = node.childNodes[0];
24646 node.removeChild(cn);
24647 node.parentNode.insertBefore(cn, node);
24649 node.parentNode.removeChild(node);
24653 if (!node.attributes || !node.attributes.length) {
24658 this.cleanUpChildren(node);
24662 function cleanAttr(n,v)
24665 if (v.match(/^\./) || v.match(/^\//)) {
24668 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24671 if (v.match(/^#/)) {
24674 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24675 node.removeAttribute(n);
24679 var cwhite = this.cwhite;
24680 var cblack = this.cblack;
24682 function cleanStyle(n,v)
24684 if (v.match(/expression/)) { //XSS?? should we even bother..
24685 node.removeAttribute(n);
24689 var parts = v.split(/;/);
24692 Roo.each(parts, function(p) {
24693 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24697 var l = p.split(':').shift().replace(/\s+/g,'');
24698 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24700 if ( cwhite.length && cblack.indexOf(l) > -1) {
24701 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24702 //node.removeAttribute(n);
24706 // only allow 'c whitelisted system attributes'
24707 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24708 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24709 //node.removeAttribute(n);
24719 if (clean.length) {
24720 node.setAttribute(n, clean.join(';'));
24722 node.removeAttribute(n);
24728 for (var i = node.attributes.length-1; i > -1 ; i--) {
24729 var a = node.attributes[i];
24732 if (a.name.toLowerCase().substr(0,2)=='on') {
24733 node.removeAttribute(a.name);
24736 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24737 node.removeAttribute(a.name);
24740 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24741 cleanAttr(a.name,a.value); // fixme..
24744 if (a.name == 'style') {
24745 cleanStyle(a.name,a.value);
24748 /// clean up MS crap..
24749 // tecnically this should be a list of valid class'es..
24752 if (a.name == 'class') {
24753 if (a.value.match(/^Mso/)) {
24754 node.removeAttribute('class');
24757 if (a.value.match(/^body$/)) {
24758 node.removeAttribute('class');
24769 this.cleanUpChildren(node);
24775 * Clean up MS wordisms...
24777 cleanWord : function(node)
24780 this.cleanWord(this.doc.body);
24785 node.nodeName == 'SPAN' &&
24786 !node.hasAttributes() &&
24787 node.childNodes.length == 1 &&
24788 node.firstChild.nodeName == "#text"
24790 var textNode = node.firstChild;
24791 node.removeChild(textNode);
24792 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24793 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24795 node.parentNode.insertBefore(textNode, node);
24796 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24797 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24799 node.parentNode.removeChild(node);
24802 if (node.nodeName == "#text") {
24803 // clean up silly Windows -- stuff?
24806 if (node.nodeName == "#comment") {
24807 node.parentNode.removeChild(node);
24808 // clean up silly Windows -- stuff?
24812 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24813 node.parentNode.removeChild(node);
24816 //Roo.log(node.tagName);
24817 // remove - but keep children..
24818 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24819 //Roo.log('-- removed');
24820 while (node.childNodes.length) {
24821 var cn = node.childNodes[0];
24822 node.removeChild(cn);
24823 node.parentNode.insertBefore(cn, node);
24824 // move node to parent - and clean it..
24825 this.cleanWord(cn);
24827 node.parentNode.removeChild(node);
24828 /// no need to iterate chidlren = it's got none..
24829 //this.iterateChildren(node, this.cleanWord);
24833 if (node.className.length) {
24835 var cn = node.className.split(/\W+/);
24837 Roo.each(cn, function(cls) {
24838 if (cls.match(/Mso[a-zA-Z]+/)) {
24843 node.className = cna.length ? cna.join(' ') : '';
24845 node.removeAttribute("class");
24849 if (node.hasAttribute("lang")) {
24850 node.removeAttribute("lang");
24853 if (node.hasAttribute("style")) {
24855 var styles = node.getAttribute("style").split(";");
24857 Roo.each(styles, function(s) {
24858 if (!s.match(/:/)) {
24861 var kv = s.split(":");
24862 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24865 // what ever is left... we allow.
24868 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24869 if (!nstyle.length) {
24870 node.removeAttribute('style');
24873 this.iterateChildren(node, this.cleanWord);
24879 * iterateChildren of a Node, calling fn each time, using this as the scole..
24880 * @param {DomNode} node node to iterate children of.
24881 * @param {Function} fn method of this class to call on each item.
24883 iterateChildren : function(node, fn)
24885 if (!node.childNodes.length) {
24888 for (var i = node.childNodes.length-1; i > -1 ; i--) {
24889 fn.call(this, node.childNodes[i])
24895 * cleanTableWidths.
24897 * Quite often pasting from word etc.. results in tables with column and widths.
24898 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24901 cleanTableWidths : function(node)
24906 this.cleanTableWidths(this.doc.body);
24911 if (node.nodeName == "#text" || node.nodeName == "#comment") {
24914 Roo.log(node.tagName);
24915 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24916 this.iterateChildren(node, this.cleanTableWidths);
24919 if (node.hasAttribute('width')) {
24920 node.removeAttribute('width');
24924 if (node.hasAttribute("style")) {
24927 var styles = node.getAttribute("style").split(";");
24929 Roo.each(styles, function(s) {
24930 if (!s.match(/:/)) {
24933 var kv = s.split(":");
24934 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24937 // what ever is left... we allow.
24940 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24941 if (!nstyle.length) {
24942 node.removeAttribute('style');
24946 this.iterateChildren(node, this.cleanTableWidths);
24954 domToHTML : function(currentElement, depth, nopadtext) {
24956 depth = depth || 0;
24957 nopadtext = nopadtext || false;
24959 if (!currentElement) {
24960 return this.domToHTML(this.doc.body);
24963 //Roo.log(currentElement);
24965 var allText = false;
24966 var nodeName = currentElement.nodeName;
24967 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24969 if (nodeName == '#text') {
24971 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24976 if (nodeName != 'BODY') {
24979 // Prints the node tagName, such as <A>, <IMG>, etc
24982 for(i = 0; i < currentElement.attributes.length;i++) {
24984 var aname = currentElement.attributes.item(i).name;
24985 if (!currentElement.attributes.item(i).value.length) {
24988 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24991 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25000 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25003 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25008 // Traverse the tree
25010 var currentElementChild = currentElement.childNodes.item(i);
25011 var allText = true;
25012 var innerHTML = '';
25014 while (currentElementChild) {
25015 // Formatting code (indent the tree so it looks nice on the screen)
25016 var nopad = nopadtext;
25017 if (lastnode == 'SPAN') {
25021 if (currentElementChild.nodeName == '#text') {
25022 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25023 toadd = nopadtext ? toadd : toadd.trim();
25024 if (!nopad && toadd.length > 80) {
25025 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25027 innerHTML += toadd;
25030 currentElementChild = currentElement.childNodes.item(i);
25036 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25038 // Recursively traverse the tree structure of the child node
25039 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25040 lastnode = currentElementChild.nodeName;
25042 currentElementChild=currentElement.childNodes.item(i);
25048 // The remaining code is mostly for formatting the tree
25049 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25054 ret+= "</"+tagName+">";
25060 applyBlacklists : function()
25062 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25063 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25067 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25068 if (b.indexOf(tag) > -1) {
25071 this.white.push(tag);
25075 Roo.each(w, function(tag) {
25076 if (b.indexOf(tag) > -1) {
25079 if (this.white.indexOf(tag) > -1) {
25082 this.white.push(tag);
25087 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25088 if (w.indexOf(tag) > -1) {
25091 this.black.push(tag);
25095 Roo.each(b, function(tag) {
25096 if (w.indexOf(tag) > -1) {
25099 if (this.black.indexOf(tag) > -1) {
25102 this.black.push(tag);
25107 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25108 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25112 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25113 if (b.indexOf(tag) > -1) {
25116 this.cwhite.push(tag);
25120 Roo.each(w, function(tag) {
25121 if (b.indexOf(tag) > -1) {
25124 if (this.cwhite.indexOf(tag) > -1) {
25127 this.cwhite.push(tag);
25132 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25133 if (w.indexOf(tag) > -1) {
25136 this.cblack.push(tag);
25140 Roo.each(b, function(tag) {
25141 if (w.indexOf(tag) > -1) {
25144 if (this.cblack.indexOf(tag) > -1) {
25147 this.cblack.push(tag);
25152 setStylesheets : function(stylesheets)
25154 if(typeof(stylesheets) == 'string'){
25155 Roo.get(this.iframe.contentDocument.head).createChild({
25157 rel : 'stylesheet',
25166 Roo.each(stylesheets, function(s) {
25171 Roo.get(_this.iframe.contentDocument.head).createChild({
25173 rel : 'stylesheet',
25182 removeStylesheets : function()
25186 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25191 setStyle : function(style)
25193 Roo.get(this.iframe.contentDocument.head).createChild({
25202 // hide stuff that is not compatible
25216 * @event specialkey
25220 * @cfg {String} fieldClass @hide
25223 * @cfg {String} focusClass @hide
25226 * @cfg {String} autoCreate @hide
25229 * @cfg {String} inputType @hide
25232 * @cfg {String} invalidClass @hide
25235 * @cfg {String} invalidText @hide
25238 * @cfg {String} msgFx @hide
25241 * @cfg {String} validateOnBlur @hide
25245 Roo.HtmlEditorCore.white = [
25246 'area', 'br', 'img', 'input', 'hr', 'wbr',
25248 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25249 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25250 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25251 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25252 'table', 'ul', 'xmp',
25254 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25257 'dir', 'menu', 'ol', 'ul', 'dl',
25263 Roo.HtmlEditorCore.black = [
25264 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25266 'base', 'basefont', 'bgsound', 'blink', 'body',
25267 'frame', 'frameset', 'head', 'html', 'ilayer',
25268 'iframe', 'layer', 'link', 'meta', 'object',
25269 'script', 'style' ,'title', 'xml' // clean later..
25271 Roo.HtmlEditorCore.clean = [
25272 'script', 'style', 'title', 'xml'
25274 Roo.HtmlEditorCore.remove = [
25279 Roo.HtmlEditorCore.ablack = [
25283 Roo.HtmlEditorCore.aclean = [
25284 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25288 Roo.HtmlEditorCore.pwhite= [
25289 'http', 'https', 'mailto'
25292 // white listed style attributes.
25293 Roo.HtmlEditorCore.cwhite= [
25294 // 'text-align', /// default is to allow most things..
25300 // black listed style attributes.
25301 Roo.HtmlEditorCore.cblack= [
25302 // 'font-size' -- this can be set by the project
25306 Roo.HtmlEditorCore.swapCodes =[
25325 * @class Roo.bootstrap.HtmlEditor
25326 * @extends Roo.bootstrap.TextArea
25327 * Bootstrap HtmlEditor class
25330 * Create a new HtmlEditor
25331 * @param {Object} config The config object
25334 Roo.bootstrap.HtmlEditor = function(config){
25335 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25336 if (!this.toolbars) {
25337 this.toolbars = [];
25340 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25343 * @event initialize
25344 * Fires when the editor is fully initialized (including the iframe)
25345 * @param {HtmlEditor} this
25350 * Fires when the editor is first receives the focus. Any insertion must wait
25351 * until after this event.
25352 * @param {HtmlEditor} this
25356 * @event beforesync
25357 * Fires before the textarea is updated with content from the editor iframe. Return false
25358 * to cancel the sync.
25359 * @param {HtmlEditor} this
25360 * @param {String} html
25364 * @event beforepush
25365 * Fires before the iframe editor is updated with content from the textarea. Return false
25366 * to cancel the push.
25367 * @param {HtmlEditor} this
25368 * @param {String} html
25373 * Fires when the textarea is updated with content from the editor iframe.
25374 * @param {HtmlEditor} this
25375 * @param {String} html
25380 * Fires when the iframe editor is updated with content from the textarea.
25381 * @param {HtmlEditor} this
25382 * @param {String} html
25386 * @event editmodechange
25387 * Fires when the editor switches edit modes
25388 * @param {HtmlEditor} this
25389 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25391 editmodechange: true,
25393 * @event editorevent
25394 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25395 * @param {HtmlEditor} this
25399 * @event firstfocus
25400 * Fires when on first focus - needed by toolbars..
25401 * @param {HtmlEditor} this
25406 * Auto save the htmlEditor value as a file into Events
25407 * @param {HtmlEditor} this
25411 * @event savedpreview
25412 * preview the saved version of htmlEditor
25413 * @param {HtmlEditor} this
25420 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25424 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25429 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25434 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25439 * @cfg {Number} height (in pixels)
25443 * @cfg {Number} width (in pixels)
25448 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25451 stylesheets: false,
25456 // private properties
25457 validationEvent : false,
25459 initialized : false,
25462 onFocus : Roo.emptyFn,
25464 hideMode:'offsets',
25466 tbContainer : false,
25470 toolbarContainer :function() {
25471 return this.wrap.select('.x-html-editor-tb',true).first();
25475 * Protected method that will not generally be called directly. It
25476 * is called when the editor creates its toolbar. Override this method if you need to
25477 * add custom toolbar buttons.
25478 * @param {HtmlEditor} editor
25480 createToolbar : function(){
25481 Roo.log('renewing');
25482 Roo.log("create toolbars");
25484 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25485 this.toolbars[0].render(this.toolbarContainer());
25489 // if (!editor.toolbars || !editor.toolbars.length) {
25490 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25493 // for (var i =0 ; i < editor.toolbars.length;i++) {
25494 // editor.toolbars[i] = Roo.factory(
25495 // typeof(editor.toolbars[i]) == 'string' ?
25496 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25497 // Roo.bootstrap.HtmlEditor);
25498 // editor.toolbars[i].init(editor);
25504 onRender : function(ct, position)
25506 // Roo.log("Call onRender: " + this.xtype);
25508 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25510 this.wrap = this.inputEl().wrap({
25511 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25514 this.editorcore.onRender(ct, position);
25516 if (this.resizable) {
25517 this.resizeEl = new Roo.Resizable(this.wrap, {
25521 minHeight : this.height,
25522 height: this.height,
25523 handles : this.resizable,
25526 resize : function(r, w, h) {
25527 _t.onResize(w,h); // -something
25533 this.createToolbar(this);
25536 if(!this.width && this.resizable){
25537 this.setSize(this.wrap.getSize());
25539 if (this.resizeEl) {
25540 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25541 // should trigger onReize..
25547 onResize : function(w, h)
25549 Roo.log('resize: ' +w + ',' + h );
25550 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25554 if(this.inputEl() ){
25555 if(typeof w == 'number'){
25556 var aw = w - this.wrap.getFrameWidth('lr');
25557 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25560 if(typeof h == 'number'){
25561 var tbh = -11; // fixme it needs to tool bar size!
25562 for (var i =0; i < this.toolbars.length;i++) {
25563 // fixme - ask toolbars for heights?
25564 tbh += this.toolbars[i].el.getHeight();
25565 //if (this.toolbars[i].footer) {
25566 // tbh += this.toolbars[i].footer.el.getHeight();
25574 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25575 ah -= 5; // knock a few pixes off for look..
25576 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25580 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25581 this.editorcore.onResize(ew,eh);
25586 * Toggles the editor between standard and source edit mode.
25587 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25589 toggleSourceEdit : function(sourceEditMode)
25591 this.editorcore.toggleSourceEdit(sourceEditMode);
25593 if(this.editorcore.sourceEditMode){
25594 Roo.log('editor - showing textarea');
25597 // Roo.log(this.syncValue());
25599 this.inputEl().removeClass(['hide', 'x-hidden']);
25600 this.inputEl().dom.removeAttribute('tabIndex');
25601 this.inputEl().focus();
25603 Roo.log('editor - hiding textarea');
25605 // Roo.log(this.pushValue());
25608 this.inputEl().addClass(['hide', 'x-hidden']);
25609 this.inputEl().dom.setAttribute('tabIndex', -1);
25610 //this.deferFocus();
25613 if(this.resizable){
25614 this.setSize(this.wrap.getSize());
25617 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25620 // private (for BoxComponent)
25621 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25623 // private (for BoxComponent)
25624 getResizeEl : function(){
25628 // private (for BoxComponent)
25629 getPositionEl : function(){
25634 initEvents : function(){
25635 this.originalValue = this.getValue();
25639 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25642 // markInvalid : Roo.emptyFn,
25644 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25647 // clearInvalid : Roo.emptyFn,
25649 setValue : function(v){
25650 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25651 this.editorcore.pushValue();
25656 deferFocus : function(){
25657 this.focus.defer(10, this);
25661 focus : function(){
25662 this.editorcore.focus();
25668 onDestroy : function(){
25674 for (var i =0; i < this.toolbars.length;i++) {
25675 // fixme - ask toolbars for heights?
25676 this.toolbars[i].onDestroy();
25679 this.wrap.dom.innerHTML = '';
25680 this.wrap.remove();
25685 onFirstFocus : function(){
25686 //Roo.log("onFirstFocus");
25687 this.editorcore.onFirstFocus();
25688 for (var i =0; i < this.toolbars.length;i++) {
25689 this.toolbars[i].onFirstFocus();
25695 syncValue : function()
25697 this.editorcore.syncValue();
25700 pushValue : function()
25702 this.editorcore.pushValue();
25706 // hide stuff that is not compatible
25720 * @event specialkey
25724 * @cfg {String} fieldClass @hide
25727 * @cfg {String} focusClass @hide
25730 * @cfg {String} autoCreate @hide
25733 * @cfg {String} inputType @hide
25737 * @cfg {String} invalidText @hide
25740 * @cfg {String} msgFx @hide
25743 * @cfg {String} validateOnBlur @hide
25752 Roo.namespace('Roo.bootstrap.htmleditor');
25754 * @class Roo.bootstrap.HtmlEditorToolbar1
25760 new Roo.bootstrap.HtmlEditor({
25763 new Roo.bootstrap.HtmlEditorToolbar1({
25764 disable : { fonts: 1 , format: 1, ..., ... , ...],
25770 * @cfg {Object} disable List of elements to disable..
25771 * @cfg {Array} btns List of additional buttons.
25775 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25778 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25781 Roo.apply(this, config);
25783 // default disabled, based on 'good practice'..
25784 this.disable = this.disable || {};
25785 Roo.applyIf(this.disable, {
25788 specialElements : true
25790 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25792 this.editor = config.editor;
25793 this.editorcore = config.editor.editorcore;
25795 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25797 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25798 // dont call parent... till later.
25800 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
25805 editorcore : false,
25810 "h1","h2","h3","h4","h5","h6",
25812 "abbr", "acronym", "address", "cite", "samp", "var",
25816 onRender : function(ct, position)
25818 // Roo.log("Call onRender: " + this.xtype);
25820 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25822 this.el.dom.style.marginBottom = '0';
25824 var editorcore = this.editorcore;
25825 var editor= this.editor;
25828 var btn = function(id,cmd , toggle, handler, html){
25830 var event = toggle ? 'toggle' : 'click';
25835 xns: Roo.bootstrap,
25839 enableToggle:toggle !== false,
25841 pressed : toggle ? false : null,
25844 a.listeners[toggle ? 'toggle' : 'click'] = function() {
25845 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
25851 // var cb_box = function...
25856 xns: Roo.bootstrap,
25861 xns: Roo.bootstrap,
25865 Roo.each(this.formats, function(f) {
25866 style.menu.items.push({
25868 xns: Roo.bootstrap,
25869 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25874 editorcore.insertTag(this.tagname);
25881 children.push(style);
25883 btn('bold',false,true);
25884 btn('italic',false,true);
25885 btn('align-left', 'justifyleft',true);
25886 btn('align-center', 'justifycenter',true);
25887 btn('align-right' , 'justifyright',true);
25888 btn('link', false, false, function(btn) {
25889 //Roo.log("create link?");
25890 var url = prompt(this.createLinkText, this.defaultLinkValue);
25891 if(url && url != 'http:/'+'/'){
25892 this.editorcore.relayCmd('createlink', url);
25895 btn('list','insertunorderedlist',true);
25896 btn('pencil', false,true, function(btn){
25898 this.toggleSourceEdit(btn.pressed);
25901 if (this.editor.btns.length > 0) {
25902 for (var i = 0; i<this.editor.btns.length; i++) {
25903 children.push(this.editor.btns[i]);
25911 xns: Roo.bootstrap,
25916 xns: Roo.bootstrap,
25921 cog.menu.items.push({
25923 xns: Roo.bootstrap,
25924 html : Clean styles,
25929 editorcore.insertTag(this.tagname);
25938 this.xtype = 'NavSimplebar';
25940 for(var i=0;i< children.length;i++) {
25942 this.buttons.add(this.addxtypeChild(children[i]));
25946 editor.on('editorevent', this.updateToolbar, this);
25948 onBtnClick : function(id)
25950 this.editorcore.relayCmd(id);
25951 this.editorcore.focus();
25955 * Protected method that will not generally be called directly. It triggers
25956 * a toolbar update by reading the markup state of the current selection in the editor.
25958 updateToolbar: function(){
25960 if(!this.editorcore.activated){
25961 this.editor.onFirstFocus(); // is this neeed?
25965 var btns = this.buttons;
25966 var doc = this.editorcore.doc;
25967 btns.get('bold').setActive(doc.queryCommandState('bold'));
25968 btns.get('italic').setActive(doc.queryCommandState('italic'));
25969 //btns.get('underline').setActive(doc.queryCommandState('underline'));
25971 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25972 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25973 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25975 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25976 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25979 var ans = this.editorcore.getAllAncestors();
25980 if (this.formatCombo) {
25983 var store = this.formatCombo.store;
25984 this.formatCombo.setValue("");
25985 for (var i =0; i < ans.length;i++) {
25986 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25988 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25996 // hides menus... - so this cant be on a menu...
25997 Roo.bootstrap.MenuMgr.hideAll();
25999 Roo.bootstrap.MenuMgr.hideAll();
26000 //this.editorsyncValue();
26002 onFirstFocus: function() {
26003 this.buttons.each(function(item){
26007 toggleSourceEdit : function(sourceEditMode){
26010 if(sourceEditMode){
26011 Roo.log("disabling buttons");
26012 this.buttons.each( function(item){
26013 if(item.cmd != 'pencil'){
26019 Roo.log("enabling buttons");
26020 if(this.editorcore.initialized){
26021 this.buttons.each( function(item){
26027 Roo.log("calling toggole on editor");
26028 // tell the editor that it's been pressed..
26029 this.editor.toggleSourceEdit(sourceEditMode);
26039 * @class Roo.bootstrap.Table.AbstractSelectionModel
26040 * @extends Roo.util.Observable
26041 * Abstract base class for grid SelectionModels. It provides the interface that should be
26042 * implemented by descendant classes. This class should not be directly instantiated.
26045 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26046 this.locked = false;
26047 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26051 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26052 /** @ignore Called by the grid automatically. Do not call directly. */
26053 init : function(grid){
26059 * Locks the selections.
26062 this.locked = true;
26066 * Unlocks the selections.
26068 unlock : function(){
26069 this.locked = false;
26073 * Returns true if the selections are locked.
26074 * @return {Boolean}
26076 isLocked : function(){
26077 return this.locked;
26081 initEvents : function ()
26087 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26088 * @class Roo.bootstrap.Table.RowSelectionModel
26089 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26090 * It supports multiple selections and keyboard selection/navigation.
26092 * @param {Object} config
26095 Roo.bootstrap.Table.RowSelectionModel = function(config){
26096 Roo.apply(this, config);
26097 this.selections = new Roo.util.MixedCollection(false, function(o){
26102 this.lastActive = false;
26106 * @event selectionchange
26107 * Fires when the selection changes
26108 * @param {SelectionModel} this
26110 "selectionchange" : true,
26112 * @event afterselectionchange
26113 * Fires after the selection changes (eg. by key press or clicking)
26114 * @param {SelectionModel} this
26116 "afterselectionchange" : true,
26118 * @event beforerowselect
26119 * Fires when a row is selected being selected, return false to cancel.
26120 * @param {SelectionModel} this
26121 * @param {Number} rowIndex The selected index
26122 * @param {Boolean} keepExisting False if other selections will be cleared
26124 "beforerowselect" : true,
26127 * Fires when a row is selected.
26128 * @param {SelectionModel} this
26129 * @param {Number} rowIndex The selected index
26130 * @param {Roo.data.Record} r The record
26132 "rowselect" : true,
26134 * @event rowdeselect
26135 * Fires when a row is deselected.
26136 * @param {SelectionModel} this
26137 * @param {Number} rowIndex The selected index
26139 "rowdeselect" : true
26141 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26142 this.locked = false;
26145 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26147 * @cfg {Boolean} singleSelect
26148 * True to allow selection of only one row at a time (defaults to false)
26150 singleSelect : false,
26153 initEvents : function()
26156 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26157 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26158 //}else{ // allow click to work like normal
26159 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26161 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26162 this.grid.on("rowclick", this.handleMouseDown, this);
26164 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26165 "up" : function(e){
26167 this.selectPrevious(e.shiftKey);
26168 }else if(this.last !== false && this.lastActive !== false){
26169 var last = this.last;
26170 this.selectRange(this.last, this.lastActive-1);
26171 this.grid.getView().focusRow(this.lastActive);
26172 if(last !== false){
26176 this.selectFirstRow();
26178 this.fireEvent("afterselectionchange", this);
26180 "down" : function(e){
26182 this.selectNext(e.shiftKey);
26183 }else if(this.last !== false && this.lastActive !== false){
26184 var last = this.last;
26185 this.selectRange(this.last, this.lastActive+1);
26186 this.grid.getView().focusRow(this.lastActive);
26187 if(last !== false){
26191 this.selectFirstRow();
26193 this.fireEvent("afterselectionchange", this);
26197 this.grid.store.on('load', function(){
26198 this.selections.clear();
26201 var view = this.grid.view;
26202 view.on("refresh", this.onRefresh, this);
26203 view.on("rowupdated", this.onRowUpdated, this);
26204 view.on("rowremoved", this.onRemove, this);
26209 onRefresh : function()
26211 var ds = this.grid.store, i, v = this.grid.view;
26212 var s = this.selections;
26213 s.each(function(r){
26214 if((i = ds.indexOfId(r.id)) != -1){
26223 onRemove : function(v, index, r){
26224 this.selections.remove(r);
26228 onRowUpdated : function(v, index, r){
26229 if(this.isSelected(r)){
26230 v.onRowSelect(index);
26236 * @param {Array} records The records to select
26237 * @param {Boolean} keepExisting (optional) True to keep existing selections
26239 selectRecords : function(records, keepExisting)
26242 this.clearSelections();
26244 var ds = this.grid.store;
26245 for(var i = 0, len = records.length; i < len; i++){
26246 this.selectRow(ds.indexOf(records[i]), true);
26251 * Gets the number of selected rows.
26254 getCount : function(){
26255 return this.selections.length;
26259 * Selects the first row in the grid.
26261 selectFirstRow : function(){
26266 * Select the last row.
26267 * @param {Boolean} keepExisting (optional) True to keep existing selections
26269 selectLastRow : function(keepExisting){
26270 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26271 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26275 * Selects the row immediately following the last selected row.
26276 * @param {Boolean} keepExisting (optional) True to keep existing selections
26278 selectNext : function(keepExisting)
26280 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26281 this.selectRow(this.last+1, keepExisting);
26282 this.grid.getView().focusRow(this.last);
26287 * Selects the row that precedes the last selected row.
26288 * @param {Boolean} keepExisting (optional) True to keep existing selections
26290 selectPrevious : function(keepExisting){
26292 this.selectRow(this.last-1, keepExisting);
26293 this.grid.getView().focusRow(this.last);
26298 * Returns the selected records
26299 * @return {Array} Array of selected records
26301 getSelections : function(){
26302 return [].concat(this.selections.items);
26306 * Returns the first selected record.
26309 getSelected : function(){
26310 return this.selections.itemAt(0);
26315 * Clears all selections.
26317 clearSelections : function(fast)
26323 var ds = this.grid.store;
26324 var s = this.selections;
26325 s.each(function(r){
26326 this.deselectRow(ds.indexOfId(r.id));
26330 this.selections.clear();
26337 * Selects all rows.
26339 selectAll : function(){
26343 this.selections.clear();
26344 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26345 this.selectRow(i, true);
26350 * Returns True if there is a selection.
26351 * @return {Boolean}
26353 hasSelection : function(){
26354 return this.selections.length > 0;
26358 * Returns True if the specified row is selected.
26359 * @param {Number/Record} record The record or index of the record to check
26360 * @return {Boolean}
26362 isSelected : function(index){
26363 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26364 return (r && this.selections.key(r.id) ? true : false);
26368 * Returns True if the specified record id is selected.
26369 * @param {String} id The id of record to check
26370 * @return {Boolean}
26372 isIdSelected : function(id){
26373 return (this.selections.key(id) ? true : false);
26378 handleMouseDBClick : function(e, t){
26382 handleMouseDown : function(e, t)
26384 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26385 if(this.isLocked() || rowIndex < 0 ){
26388 if(e.shiftKey && this.last !== false){
26389 var last = this.last;
26390 this.selectRange(last, rowIndex, e.ctrlKey);
26391 this.last = last; // reset the last
26395 var isSelected = this.isSelected(rowIndex);
26396 //Roo.log("select row:" + rowIndex);
26398 this.deselectRow(rowIndex);
26400 this.selectRow(rowIndex, true);
26404 if(e.button !== 0 && isSelected){
26405 alert('rowIndex 2: ' + rowIndex);
26406 view.focusRow(rowIndex);
26407 }else if(e.ctrlKey && isSelected){
26408 this.deselectRow(rowIndex);
26409 }else if(!isSelected){
26410 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26411 view.focusRow(rowIndex);
26415 this.fireEvent("afterselectionchange", this);
26418 handleDragableRowClick : function(grid, rowIndex, e)
26420 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26421 this.selectRow(rowIndex, false);
26422 grid.view.focusRow(rowIndex);
26423 this.fireEvent("afterselectionchange", this);
26428 * Selects multiple rows.
26429 * @param {Array} rows Array of the indexes of the row to select
26430 * @param {Boolean} keepExisting (optional) True to keep existing selections
26432 selectRows : function(rows, keepExisting){
26434 this.clearSelections();
26436 for(var i = 0, len = rows.length; i < len; i++){
26437 this.selectRow(rows[i], true);
26442 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26443 * @param {Number} startRow The index of the first row in the range
26444 * @param {Number} endRow The index of the last row in the range
26445 * @param {Boolean} keepExisting (optional) True to retain existing selections
26447 selectRange : function(startRow, endRow, keepExisting){
26452 this.clearSelections();
26454 if(startRow <= endRow){
26455 for(var i = startRow; i <= endRow; i++){
26456 this.selectRow(i, true);
26459 for(var i = startRow; i >= endRow; i--){
26460 this.selectRow(i, true);
26466 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26467 * @param {Number} startRow The index of the first row in the range
26468 * @param {Number} endRow The index of the last row in the range
26470 deselectRange : function(startRow, endRow, preventViewNotify){
26474 for(var i = startRow; i <= endRow; i++){
26475 this.deselectRow(i, preventViewNotify);
26481 * @param {Number} row The index of the row to select
26482 * @param {Boolean} keepExisting (optional) True to keep existing selections
26484 selectRow : function(index, keepExisting, preventViewNotify)
26486 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26489 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26490 if(!keepExisting || this.singleSelect){
26491 this.clearSelections();
26494 var r = this.grid.store.getAt(index);
26495 //console.log('selectRow - record id :' + r.id);
26497 this.selections.add(r);
26498 this.last = this.lastActive = index;
26499 if(!preventViewNotify){
26500 var proxy = new Roo.Element(
26501 this.grid.getRowDom(index)
26503 proxy.addClass('bg-info info');
26505 this.fireEvent("rowselect", this, index, r);
26506 this.fireEvent("selectionchange", this);
26512 * @param {Number} row The index of the row to deselect
26514 deselectRow : function(index, preventViewNotify)
26519 if(this.last == index){
26522 if(this.lastActive == index){
26523 this.lastActive = false;
26526 var r = this.grid.store.getAt(index);
26531 this.selections.remove(r);
26532 //.console.log('deselectRow - record id :' + r.id);
26533 if(!preventViewNotify){
26535 var proxy = new Roo.Element(
26536 this.grid.getRowDom(index)
26538 proxy.removeClass('bg-info info');
26540 this.fireEvent("rowdeselect", this, index);
26541 this.fireEvent("selectionchange", this);
26545 restoreLast : function(){
26547 this.last = this._last;
26552 acceptsNav : function(row, col, cm){
26553 return !cm.isHidden(col) && cm.isCellEditable(col, row);
26557 onEditorKey : function(field, e){
26558 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26563 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26565 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26567 }else if(k == e.ENTER && !e.ctrlKey){
26571 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26573 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26575 }else if(k == e.ESC){
26579 g.startEditing(newCell[0], newCell[1]);
26585 * Ext JS Library 1.1.1
26586 * Copyright(c) 2006-2007, Ext JS, LLC.
26588 * Originally Released Under LGPL - original licence link has changed is not relivant.
26591 * <script type="text/javascript">
26595 * @class Roo.bootstrap.PagingToolbar
26596 * @extends Roo.bootstrap.NavSimplebar
26597 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26599 * Create a new PagingToolbar
26600 * @param {Object} config The config object
26601 * @param {Roo.data.Store} store
26603 Roo.bootstrap.PagingToolbar = function(config)
26605 // old args format still supported... - xtype is prefered..
26606 // created from xtype...
26608 this.ds = config.dataSource;
26610 if (config.store && !this.ds) {
26611 this.store= Roo.factory(config.store, Roo.data);
26612 this.ds = this.store;
26613 this.ds.xmodule = this.xmodule || false;
26616 this.toolbarItems = [];
26617 if (config.items) {
26618 this.toolbarItems = config.items;
26621 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26626 this.bind(this.ds);
26629 if (Roo.bootstrap.version == 4) {
26630 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26632 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26637 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26639 * @cfg {Roo.data.Store} dataSource
26640 * The underlying data store providing the paged data
26643 * @cfg {String/HTMLElement/Element} container
26644 * container The id or element that will contain the toolbar
26647 * @cfg {Boolean} displayInfo
26648 * True to display the displayMsg (defaults to false)
26651 * @cfg {Number} pageSize
26652 * The number of records to display per page (defaults to 20)
26656 * @cfg {String} displayMsg
26657 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26659 displayMsg : 'Displaying {0} - {1} of {2}',
26661 * @cfg {String} emptyMsg
26662 * The message to display when no records are found (defaults to "No data to display")
26664 emptyMsg : 'No data to display',
26666 * Customizable piece of the default paging text (defaults to "Page")
26669 beforePageText : "Page",
26671 * Customizable piece of the default paging text (defaults to "of %0")
26674 afterPageText : "of {0}",
26676 * Customizable piece of the default paging text (defaults to "First Page")
26679 firstText : "First Page",
26681 * Customizable piece of the default paging text (defaults to "Previous Page")
26684 prevText : "Previous Page",
26686 * Customizable piece of the default paging text (defaults to "Next Page")
26689 nextText : "Next Page",
26691 * Customizable piece of the default paging text (defaults to "Last Page")
26694 lastText : "Last Page",
26696 * Customizable piece of the default paging text (defaults to "Refresh")
26699 refreshText : "Refresh",
26703 onRender : function(ct, position)
26705 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26706 this.navgroup.parentId = this.id;
26707 this.navgroup.onRender(this.el, null);
26708 // add the buttons to the navgroup
26710 if(this.displayInfo){
26711 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26712 this.displayEl = this.el.select('.x-paging-info', true).first();
26713 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26714 // this.displayEl = navel.el.select('span',true).first();
26720 Roo.each(_this.buttons, function(e){ // this might need to use render????
26721 Roo.factory(e).render(_this.el);
26725 Roo.each(_this.toolbarItems, function(e) {
26726 _this.navgroup.addItem(e);
26730 this.first = this.navgroup.addItem({
26731 tooltip: this.firstText,
26732 cls: "prev btn-outline-secondary",
26733 html : ' <i class="fa fa-step-backward"></i>',
26735 preventDefault: true,
26736 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26739 this.prev = this.navgroup.addItem({
26740 tooltip: this.prevText,
26741 cls: "prev btn-outline-secondary",
26742 html : ' <i class="fa fa-backward"></i>',
26744 preventDefault: true,
26745 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
26747 //this.addSeparator();
26750 var field = this.navgroup.addItem( {
26752 cls : 'x-paging-position btn-outline-secondary',
26754 html : this.beforePageText +
26755 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26756 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
26759 this.field = field.el.select('input', true).first();
26760 this.field.on("keydown", this.onPagingKeydown, this);
26761 this.field.on("focus", function(){this.dom.select();});
26764 this.afterTextEl = field.el.select('.x-paging-after',true).first();
26765 //this.field.setHeight(18);
26766 //this.addSeparator();
26767 this.next = this.navgroup.addItem({
26768 tooltip: this.nextText,
26769 cls: "next btn-outline-secondary",
26770 html : ' <i class="fa fa-forward"></i>',
26772 preventDefault: true,
26773 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
26775 this.last = this.navgroup.addItem({
26776 tooltip: this.lastText,
26777 html : ' <i class="fa fa-step-forward"></i>',
26778 cls: "next btn-outline-secondary",
26780 preventDefault: true,
26781 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
26783 //this.addSeparator();
26784 this.loading = this.navgroup.addItem({
26785 tooltip: this.refreshText,
26786 cls: "btn-outline-secondary",
26787 html : ' <i class="fa fa-refresh"></i>',
26788 preventDefault: true,
26789 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26795 updateInfo : function(){
26796 if(this.displayEl){
26797 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26798 var msg = count == 0 ?
26802 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
26804 this.displayEl.update(msg);
26809 onLoad : function(ds, r, o)
26811 this.cursor = o.params.start ? o.params.start : 0;
26813 var d = this.getPageData(),
26818 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26819 this.field.dom.value = ap;
26820 this.first.setDisabled(ap == 1);
26821 this.prev.setDisabled(ap == 1);
26822 this.next.setDisabled(ap == ps);
26823 this.last.setDisabled(ap == ps);
26824 this.loading.enable();
26829 getPageData : function(){
26830 var total = this.ds.getTotalCount();
26833 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26834 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26839 onLoadError : function(){
26840 this.loading.enable();
26844 onPagingKeydown : function(e){
26845 var k = e.getKey();
26846 var d = this.getPageData();
26848 var v = this.field.dom.value, pageNum;
26849 if(!v || isNaN(pageNum = parseInt(v, 10))){
26850 this.field.dom.value = d.activePage;
26853 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26854 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26857 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))
26859 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26860 this.field.dom.value = pageNum;
26861 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26864 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26866 var v = this.field.dom.value, pageNum;
26867 var increment = (e.shiftKey) ? 10 : 1;
26868 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26871 if(!v || isNaN(pageNum = parseInt(v, 10))) {
26872 this.field.dom.value = d.activePage;
26875 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26877 this.field.dom.value = parseInt(v, 10) + increment;
26878 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26879 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26886 beforeLoad : function(){
26888 this.loading.disable();
26893 onClick : function(which){
26902 ds.load({params:{start: 0, limit: this.pageSize}});
26905 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26908 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26911 var total = ds.getTotalCount();
26912 var extra = total % this.pageSize;
26913 var lastStart = extra ? (total - extra) : total-this.pageSize;
26914 ds.load({params:{start: lastStart, limit: this.pageSize}});
26917 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26923 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26924 * @param {Roo.data.Store} store The data store to unbind
26926 unbind : function(ds){
26927 ds.un("beforeload", this.beforeLoad, this);
26928 ds.un("load", this.onLoad, this);
26929 ds.un("loadexception", this.onLoadError, this);
26930 ds.un("remove", this.updateInfo, this);
26931 ds.un("add", this.updateInfo, this);
26932 this.ds = undefined;
26936 * Binds the paging toolbar to the specified {@link Roo.data.Store}
26937 * @param {Roo.data.Store} store The data store to bind
26939 bind : function(ds){
26940 ds.on("beforeload", this.beforeLoad, this);
26941 ds.on("load", this.onLoad, this);
26942 ds.on("loadexception", this.onLoadError, this);
26943 ds.on("remove", this.updateInfo, this);
26944 ds.on("add", this.updateInfo, this);
26955 * @class Roo.bootstrap.MessageBar
26956 * @extends Roo.bootstrap.Component
26957 * Bootstrap MessageBar class
26958 * @cfg {String} html contents of the MessageBar
26959 * @cfg {String} weight (info | success | warning | danger) default info
26960 * @cfg {String} beforeClass insert the bar before the given class
26961 * @cfg {Boolean} closable (true | false) default false
26962 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26965 * Create a new Element
26966 * @param {Object} config The config object
26969 Roo.bootstrap.MessageBar = function(config){
26970 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26973 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
26979 beforeClass: 'bootstrap-sticky-wrap',
26981 getAutoCreate : function(){
26985 cls: 'alert alert-dismissable alert-' + this.weight,
26990 html: this.html || ''
26996 cfg.cls += ' alert-messages-fixed';
27010 onRender : function(ct, position)
27012 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27015 var cfg = Roo.apply({}, this.getAutoCreate());
27019 cfg.cls += ' ' + this.cls;
27022 cfg.style = this.style;
27024 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27026 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27029 this.el.select('>button.close').on('click', this.hide, this);
27035 if (!this.rendered) {
27041 this.fireEvent('show', this);
27047 if (!this.rendered) {
27053 this.fireEvent('hide', this);
27056 update : function()
27058 // var e = this.el.dom.firstChild;
27060 // if(this.closable){
27061 // e = e.nextSibling;
27064 // e.data = this.html || '';
27066 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27082 * @class Roo.bootstrap.Graph
27083 * @extends Roo.bootstrap.Component
27084 * Bootstrap Graph class
27088 @cfg {String} graphtype bar | vbar | pie
27089 @cfg {number} g_x coodinator | centre x (pie)
27090 @cfg {number} g_y coodinator | centre y (pie)
27091 @cfg {number} g_r radius (pie)
27092 @cfg {number} g_height height of the chart (respected by all elements in the set)
27093 @cfg {number} g_width width of the chart (respected by all elements in the set)
27094 @cfg {Object} title The title of the chart
27097 -opts (object) options for the chart
27099 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27100 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27102 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.
27103 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27105 o stretch (boolean)
27107 -opts (object) options for the pie
27110 o startAngle (number)
27111 o endAngle (number)
27115 * Create a new Input
27116 * @param {Object} config The config object
27119 Roo.bootstrap.Graph = function(config){
27120 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27126 * The img click event for the img.
27127 * @param {Roo.EventObject} e
27133 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27144 //g_colors: this.colors,
27151 getAutoCreate : function(){
27162 onRender : function(ct,position){
27165 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27167 if (typeof(Raphael) == 'undefined') {
27168 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27172 this.raphael = Raphael(this.el.dom);
27174 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27175 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27176 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27177 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27179 r.text(160, 10, "Single Series Chart").attr(txtattr);
27180 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27181 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27182 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27184 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27185 r.barchart(330, 10, 300, 220, data1);
27186 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27187 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27190 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27191 // r.barchart(30, 30, 560, 250, xdata, {
27192 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27193 // axis : "0 0 1 1",
27194 // axisxlabels : xdata
27195 // //yvalues : cols,
27198 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27200 // this.load(null,xdata,{
27201 // axis : "0 0 1 1",
27202 // axisxlabels : xdata
27207 load : function(graphtype,xdata,opts)
27209 this.raphael.clear();
27211 graphtype = this.graphtype;
27216 var r = this.raphael,
27217 fin = function () {
27218 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27220 fout = function () {
27221 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27223 pfin = function() {
27224 this.sector.stop();
27225 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27228 this.label[0].stop();
27229 this.label[0].attr({ r: 7.5 });
27230 this.label[1].attr({ "font-weight": 800 });
27233 pfout = function() {
27234 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27237 this.label[0].animate({ r: 5 }, 500, "bounce");
27238 this.label[1].attr({ "font-weight": 400 });
27244 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27247 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27250 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27251 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27253 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27260 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27265 setTitle: function(o)
27270 initEvents: function() {
27273 this.el.on('click', this.onClick, this);
27277 onClick : function(e)
27279 Roo.log('img onclick');
27280 this.fireEvent('click', this, e);
27292 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27295 * @class Roo.bootstrap.dash.NumberBox
27296 * @extends Roo.bootstrap.Component
27297 * Bootstrap NumberBox class
27298 * @cfg {String} headline Box headline
27299 * @cfg {String} content Box content
27300 * @cfg {String} icon Box icon
27301 * @cfg {String} footer Footer text
27302 * @cfg {String} fhref Footer href
27305 * Create a new NumberBox
27306 * @param {Object} config The config object
27310 Roo.bootstrap.dash.NumberBox = function(config){
27311 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27315 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27324 getAutoCreate : function(){
27328 cls : 'small-box ',
27336 cls : 'roo-headline',
27337 html : this.headline
27341 cls : 'roo-content',
27342 html : this.content
27356 cls : 'ion ' + this.icon
27365 cls : 'small-box-footer',
27366 href : this.fhref || '#',
27370 cfg.cn.push(footer);
27377 onRender : function(ct,position){
27378 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27385 setHeadline: function (value)
27387 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27390 setFooter: function (value, href)
27392 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27395 this.el.select('a.small-box-footer',true).first().attr('href', href);
27400 setContent: function (value)
27402 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27405 initEvents: function()
27419 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27422 * @class Roo.bootstrap.dash.TabBox
27423 * @extends Roo.bootstrap.Component
27424 * Bootstrap TabBox class
27425 * @cfg {String} title Title of the TabBox
27426 * @cfg {String} icon Icon of the TabBox
27427 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27428 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27431 * Create a new TabBox
27432 * @param {Object} config The config object
27436 Roo.bootstrap.dash.TabBox = function(config){
27437 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27442 * When a pane is added
27443 * @param {Roo.bootstrap.dash.TabPane} pane
27447 * @event activatepane
27448 * When a pane is activated
27449 * @param {Roo.bootstrap.dash.TabPane} pane
27451 "activatepane" : true
27459 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27464 tabScrollable : false,
27466 getChildContainer : function()
27468 return this.el.select('.tab-content', true).first();
27471 getAutoCreate : function(){
27475 cls: 'pull-left header',
27483 cls: 'fa ' + this.icon
27489 cls: 'nav nav-tabs pull-right',
27495 if(this.tabScrollable){
27502 cls: 'nav nav-tabs pull-right',
27513 cls: 'nav-tabs-custom',
27518 cls: 'tab-content no-padding',
27526 initEvents : function()
27528 //Roo.log('add add pane handler');
27529 this.on('addpane', this.onAddPane, this);
27532 * Updates the box title
27533 * @param {String} html to set the title to.
27535 setTitle : function(value)
27537 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27539 onAddPane : function(pane)
27541 this.panes.push(pane);
27542 //Roo.log('addpane');
27544 // tabs are rendere left to right..
27545 if(!this.showtabs){
27549 var ctr = this.el.select('.nav-tabs', true).first();
27552 var existing = ctr.select('.nav-tab',true);
27553 var qty = existing.getCount();;
27556 var tab = ctr.createChild({
27558 cls : 'nav-tab' + (qty ? '' : ' active'),
27566 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27569 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27571 pane.el.addClass('active');
27576 onTabClick : function(ev,un,ob,pane)
27578 //Roo.log('tab - prev default');
27579 ev.preventDefault();
27582 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27583 pane.tab.addClass('active');
27584 //Roo.log(pane.title);
27585 this.getChildContainer().select('.tab-pane',true).removeClass('active');
27586 // technically we should have a deactivate event.. but maybe add later.
27587 // and it should not de-activate the selected tab...
27588 this.fireEvent('activatepane', pane);
27589 pane.el.addClass('active');
27590 pane.fireEvent('activate');
27595 getActivePane : function()
27598 Roo.each(this.panes, function(p) {
27599 if(p.el.hasClass('active')){
27620 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27622 * @class Roo.bootstrap.TabPane
27623 * @extends Roo.bootstrap.Component
27624 * Bootstrap TabPane class
27625 * @cfg {Boolean} active (false | true) Default false
27626 * @cfg {String} title title of panel
27630 * Create a new TabPane
27631 * @param {Object} config The config object
27634 Roo.bootstrap.dash.TabPane = function(config){
27635 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27641 * When a pane is activated
27642 * @param {Roo.bootstrap.dash.TabPane} pane
27649 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27654 // the tabBox that this is attached to.
27657 getAutoCreate : function()
27665 cfg.cls += ' active';
27670 initEvents : function()
27672 //Roo.log('trigger add pane handler');
27673 this.parent().fireEvent('addpane', this)
27677 * Updates the tab title
27678 * @param {String} html to set the title to.
27680 setTitle: function(str)
27686 this.tab.select('a', true).first().dom.innerHTML = str;
27703 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27706 * @class Roo.bootstrap.menu.Menu
27707 * @extends Roo.bootstrap.Component
27708 * Bootstrap Menu class - container for Menu
27709 * @cfg {String} html Text of the menu
27710 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27711 * @cfg {String} icon Font awesome icon
27712 * @cfg {String} pos Menu align to (top | bottom) default bottom
27716 * Create a new Menu
27717 * @param {Object} config The config object
27721 Roo.bootstrap.menu.Menu = function(config){
27722 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27726 * @event beforeshow
27727 * Fires before this menu is displayed
27728 * @param {Roo.bootstrap.menu.Menu} this
27732 * @event beforehide
27733 * Fires before this menu is hidden
27734 * @param {Roo.bootstrap.menu.Menu} this
27739 * Fires after this menu is displayed
27740 * @param {Roo.bootstrap.menu.Menu} this
27745 * Fires after this menu is hidden
27746 * @param {Roo.bootstrap.menu.Menu} this
27751 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27752 * @param {Roo.bootstrap.menu.Menu} this
27753 * @param {Roo.EventObject} e
27760 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
27764 weight : 'default',
27769 getChildContainer : function() {
27770 if(this.isSubMenu){
27774 return this.el.select('ul.dropdown-menu', true).first();
27777 getAutoCreate : function()
27782 cls : 'roo-menu-text',
27790 cls : 'fa ' + this.icon
27801 cls : 'dropdown-button btn btn-' + this.weight,
27806 cls : 'dropdown-toggle btn btn-' + this.weight,
27816 cls : 'dropdown-menu'
27822 if(this.pos == 'top'){
27823 cfg.cls += ' dropup';
27826 if(this.isSubMenu){
27829 cls : 'dropdown-menu'
27836 onRender : function(ct, position)
27838 this.isSubMenu = ct.hasClass('dropdown-submenu');
27840 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27843 initEvents : function()
27845 if(this.isSubMenu){
27849 this.hidden = true;
27851 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27852 this.triggerEl.on('click', this.onTriggerPress, this);
27854 this.buttonEl = this.el.select('button.dropdown-button', true).first();
27855 this.buttonEl.on('click', this.onClick, this);
27861 if(this.isSubMenu){
27865 return this.el.select('ul.dropdown-menu', true).first();
27868 onClick : function(e)
27870 this.fireEvent("click", this, e);
27873 onTriggerPress : function(e)
27875 if (this.isVisible()) {
27882 isVisible : function(){
27883 return !this.hidden;
27888 this.fireEvent("beforeshow", this);
27890 this.hidden = false;
27891 this.el.addClass('open');
27893 Roo.get(document).on("mouseup", this.onMouseUp, this);
27895 this.fireEvent("show", this);
27902 this.fireEvent("beforehide", this);
27904 this.hidden = true;
27905 this.el.removeClass('open');
27907 Roo.get(document).un("mouseup", this.onMouseUp);
27909 this.fireEvent("hide", this);
27912 onMouseUp : function()
27926 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27929 * @class Roo.bootstrap.menu.Item
27930 * @extends Roo.bootstrap.Component
27931 * Bootstrap MenuItem class
27932 * @cfg {Boolean} submenu (true | false) default false
27933 * @cfg {String} html text of the item
27934 * @cfg {String} href the link
27935 * @cfg {Boolean} disable (true | false) default false
27936 * @cfg {Boolean} preventDefault (true | false) default true
27937 * @cfg {String} icon Font awesome icon
27938 * @cfg {String} pos Submenu align to (left | right) default right
27942 * Create a new Item
27943 * @param {Object} config The config object
27947 Roo.bootstrap.menu.Item = function(config){
27948 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27952 * Fires when the mouse is hovering over this menu
27953 * @param {Roo.bootstrap.menu.Item} this
27954 * @param {Roo.EventObject} e
27959 * Fires when the mouse exits this menu
27960 * @param {Roo.bootstrap.menu.Item} this
27961 * @param {Roo.EventObject} e
27967 * The raw click event for the entire grid.
27968 * @param {Roo.EventObject} e
27974 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
27979 preventDefault: true,
27984 getAutoCreate : function()
27989 cls : 'roo-menu-item-text',
27997 cls : 'fa ' + this.icon
28006 href : this.href || '#',
28013 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28017 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28019 if(this.pos == 'left'){
28020 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28027 initEvents : function()
28029 this.el.on('mouseover', this.onMouseOver, this);
28030 this.el.on('mouseout', this.onMouseOut, this);
28032 this.el.select('a', true).first().on('click', this.onClick, this);
28036 onClick : function(e)
28038 if(this.preventDefault){
28039 e.preventDefault();
28042 this.fireEvent("click", this, e);
28045 onMouseOver : function(e)
28047 if(this.submenu && this.pos == 'left'){
28048 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28051 this.fireEvent("mouseover", this, e);
28054 onMouseOut : function(e)
28056 this.fireEvent("mouseout", this, e);
28068 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28071 * @class Roo.bootstrap.menu.Separator
28072 * @extends Roo.bootstrap.Component
28073 * Bootstrap Separator class
28076 * Create a new Separator
28077 * @param {Object} config The config object
28081 Roo.bootstrap.menu.Separator = function(config){
28082 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28085 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28087 getAutoCreate : function(){
28108 * @class Roo.bootstrap.Tooltip
28109 * Bootstrap Tooltip class
28110 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28111 * to determine which dom element triggers the tooltip.
28113 * It needs to add support for additional attributes like tooltip-position
28116 * Create a new Toolti
28117 * @param {Object} config The config object
28120 Roo.bootstrap.Tooltip = function(config){
28121 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28123 this.alignment = Roo.bootstrap.Tooltip.alignment;
28125 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28126 this.alignment = config.alignment;
28131 Roo.apply(Roo.bootstrap.Tooltip, {
28133 * @function init initialize tooltip monitoring.
28137 currentTip : false,
28138 currentRegion : false,
28144 Roo.get(document).on('mouseover', this.enter ,this);
28145 Roo.get(document).on('mouseout', this.leave, this);
28148 this.currentTip = new Roo.bootstrap.Tooltip();
28151 enter : function(ev)
28153 var dom = ev.getTarget();
28155 //Roo.log(['enter',dom]);
28156 var el = Roo.fly(dom);
28157 if (this.currentEl) {
28159 //Roo.log(this.currentEl);
28160 //Roo.log(this.currentEl.contains(dom));
28161 if (this.currentEl == el) {
28164 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28170 if (this.currentTip.el) {
28171 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28175 if(!el || el.dom == document){
28181 // you can not look for children, as if el is the body.. then everythign is the child..
28182 if (!el.attr('tooltip')) { //
28183 if (!el.select("[tooltip]").elements.length) {
28186 // is the mouse over this child...?
28187 bindEl = el.select("[tooltip]").first();
28188 var xy = ev.getXY();
28189 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28190 //Roo.log("not in region.");
28193 //Roo.log("child element over..");
28196 this.currentEl = bindEl;
28197 this.currentTip.bind(bindEl);
28198 this.currentRegion = Roo.lib.Region.getRegion(dom);
28199 this.currentTip.enter();
28202 leave : function(ev)
28204 var dom = ev.getTarget();
28205 //Roo.log(['leave',dom]);
28206 if (!this.currentEl) {
28211 if (dom != this.currentEl.dom) {
28214 var xy = ev.getXY();
28215 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28218 // only activate leave if mouse cursor is outside... bounding box..
28223 if (this.currentTip) {
28224 this.currentTip.leave();
28226 //Roo.log('clear currentEl');
28227 this.currentEl = false;
28232 'left' : ['r-l', [-2,0], 'right'],
28233 'right' : ['l-r', [2,0], 'left'],
28234 'bottom' : ['t-b', [0,2], 'top'],
28235 'top' : [ 'b-t', [0,-2], 'bottom']
28241 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28246 delay : null, // can be { show : 300 , hide: 500}
28250 hoverState : null, //???
28252 placement : 'bottom',
28256 getAutoCreate : function(){
28263 cls : 'tooltip-arrow'
28266 cls : 'tooltip-inner'
28273 bind : function(el)
28279 enter : function () {
28281 if (this.timeout != null) {
28282 clearTimeout(this.timeout);
28285 this.hoverState = 'in';
28286 //Roo.log("enter - show");
28287 if (!this.delay || !this.delay.show) {
28292 this.timeout = setTimeout(function () {
28293 if (_t.hoverState == 'in') {
28296 }, this.delay.show);
28300 clearTimeout(this.timeout);
28302 this.hoverState = 'out';
28303 if (!this.delay || !this.delay.hide) {
28309 this.timeout = setTimeout(function () {
28310 //Roo.log("leave - timeout");
28312 if (_t.hoverState == 'out') {
28314 Roo.bootstrap.Tooltip.currentEl = false;
28319 show : function (msg)
28322 this.render(document.body);
28325 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28327 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28329 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28331 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
28333 var placement = typeof this.placement == 'function' ?
28334 this.placement.call(this, this.el, on_el) :
28337 var autoToken = /\s?auto?\s?/i;
28338 var autoPlace = autoToken.test(placement);
28340 placement = placement.replace(autoToken, '') || 'top';
28344 //this.el.setXY([0,0]);
28346 //this.el.dom.style.display='block';
28348 //this.el.appendTo(on_el);
28350 var p = this.getPosition();
28351 var box = this.el.getBox();
28357 var align = this.alignment[placement];
28359 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28361 if(placement == 'top' || placement == 'bottom'){
28363 placement = 'right';
28366 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28367 placement = 'left';
28370 var scroll = Roo.select('body', true).first().getScroll();
28372 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28376 align = this.alignment[placement];
28379 this.el.alignTo(this.bindEl, align[0],align[1]);
28380 //var arrow = this.el.select('.arrow',true).first();
28381 //arrow.set(align[2],
28383 this.el.addClass(placement);
28385 this.el.addClass('in fade');
28387 this.hoverState = null;
28389 if (this.el.hasClass('fade')) {
28400 //this.el.setXY([0,0]);
28401 this.el.removeClass('in');
28417 * @class Roo.bootstrap.LocationPicker
28418 * @extends Roo.bootstrap.Component
28419 * Bootstrap LocationPicker class
28420 * @cfg {Number} latitude Position when init default 0
28421 * @cfg {Number} longitude Position when init default 0
28422 * @cfg {Number} zoom default 15
28423 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28424 * @cfg {Boolean} mapTypeControl default false
28425 * @cfg {Boolean} disableDoubleClickZoom default false
28426 * @cfg {Boolean} scrollwheel default true
28427 * @cfg {Boolean} streetViewControl default false
28428 * @cfg {Number} radius default 0
28429 * @cfg {String} locationName
28430 * @cfg {Boolean} draggable default true
28431 * @cfg {Boolean} enableAutocomplete default false
28432 * @cfg {Boolean} enableReverseGeocode default true
28433 * @cfg {String} markerTitle
28436 * Create a new LocationPicker
28437 * @param {Object} config The config object
28441 Roo.bootstrap.LocationPicker = function(config){
28443 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28448 * Fires when the picker initialized.
28449 * @param {Roo.bootstrap.LocationPicker} this
28450 * @param {Google Location} location
28454 * @event positionchanged
28455 * Fires when the picker position changed.
28456 * @param {Roo.bootstrap.LocationPicker} this
28457 * @param {Google Location} location
28459 positionchanged : true,
28462 * Fires when the map resize.
28463 * @param {Roo.bootstrap.LocationPicker} this
28468 * Fires when the map show.
28469 * @param {Roo.bootstrap.LocationPicker} this
28474 * Fires when the map hide.
28475 * @param {Roo.bootstrap.LocationPicker} this
28480 * Fires when click the map.
28481 * @param {Roo.bootstrap.LocationPicker} this
28482 * @param {Map event} e
28486 * @event mapRightClick
28487 * Fires when right click the map.
28488 * @param {Roo.bootstrap.LocationPicker} this
28489 * @param {Map event} e
28491 mapRightClick : true,
28493 * @event markerClick
28494 * Fires when click the marker.
28495 * @param {Roo.bootstrap.LocationPicker} this
28496 * @param {Map event} e
28498 markerClick : true,
28500 * @event markerRightClick
28501 * Fires when right click the marker.
28502 * @param {Roo.bootstrap.LocationPicker} this
28503 * @param {Map event} e
28505 markerRightClick : true,
28507 * @event OverlayViewDraw
28508 * Fires when OverlayView Draw
28509 * @param {Roo.bootstrap.LocationPicker} this
28511 OverlayViewDraw : true,
28513 * @event OverlayViewOnAdd
28514 * Fires when OverlayView Draw
28515 * @param {Roo.bootstrap.LocationPicker} this
28517 OverlayViewOnAdd : true,
28519 * @event OverlayViewOnRemove
28520 * Fires when OverlayView Draw
28521 * @param {Roo.bootstrap.LocationPicker} this
28523 OverlayViewOnRemove : true,
28525 * @event OverlayViewShow
28526 * Fires when OverlayView Draw
28527 * @param {Roo.bootstrap.LocationPicker} this
28528 * @param {Pixel} cpx
28530 OverlayViewShow : true,
28532 * @event OverlayViewHide
28533 * Fires when OverlayView Draw
28534 * @param {Roo.bootstrap.LocationPicker} this
28536 OverlayViewHide : true,
28538 * @event loadexception
28539 * Fires when load google lib failed.
28540 * @param {Roo.bootstrap.LocationPicker} this
28542 loadexception : true
28547 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
28549 gMapContext: false,
28555 mapTypeControl: false,
28556 disableDoubleClickZoom: false,
28558 streetViewControl: false,
28562 enableAutocomplete: false,
28563 enableReverseGeocode: true,
28566 getAutoCreate: function()
28571 cls: 'roo-location-picker'
28577 initEvents: function(ct, position)
28579 if(!this.el.getWidth() || this.isApplied()){
28583 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28588 initial: function()
28590 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28591 this.fireEvent('loadexception', this);
28595 if(!this.mapTypeId){
28596 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28599 this.gMapContext = this.GMapContext();
28601 this.initOverlayView();
28603 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28607 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28608 _this.setPosition(_this.gMapContext.marker.position);
28611 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28612 _this.fireEvent('mapClick', this, event);
28616 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28617 _this.fireEvent('mapRightClick', this, event);
28621 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28622 _this.fireEvent('markerClick', this, event);
28626 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28627 _this.fireEvent('markerRightClick', this, event);
28631 this.setPosition(this.gMapContext.location);
28633 this.fireEvent('initial', this, this.gMapContext.location);
28636 initOverlayView: function()
28640 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28644 _this.fireEvent('OverlayViewDraw', _this);
28649 _this.fireEvent('OverlayViewOnAdd', _this);
28652 onRemove: function()
28654 _this.fireEvent('OverlayViewOnRemove', _this);
28657 show: function(cpx)
28659 _this.fireEvent('OverlayViewShow', _this, cpx);
28664 _this.fireEvent('OverlayViewHide', _this);
28670 fromLatLngToContainerPixel: function(event)
28672 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28675 isApplied: function()
28677 return this.getGmapContext() == false ? false : true;
28680 getGmapContext: function()
28682 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28685 GMapContext: function()
28687 var position = new google.maps.LatLng(this.latitude, this.longitude);
28689 var _map = new google.maps.Map(this.el.dom, {
28692 mapTypeId: this.mapTypeId,
28693 mapTypeControl: this.mapTypeControl,
28694 disableDoubleClickZoom: this.disableDoubleClickZoom,
28695 scrollwheel: this.scrollwheel,
28696 streetViewControl: this.streetViewControl,
28697 locationName: this.locationName,
28698 draggable: this.draggable,
28699 enableAutocomplete: this.enableAutocomplete,
28700 enableReverseGeocode: this.enableReverseGeocode
28703 var _marker = new google.maps.Marker({
28704 position: position,
28706 title: this.markerTitle,
28707 draggable: this.draggable
28714 location: position,
28715 radius: this.radius,
28716 locationName: this.locationName,
28717 addressComponents: {
28718 formatted_address: null,
28719 addressLine1: null,
28720 addressLine2: null,
28722 streetNumber: null,
28726 stateOrProvince: null
28729 domContainer: this.el.dom,
28730 geodecoder: new google.maps.Geocoder()
28734 drawCircle: function(center, radius, options)
28736 if (this.gMapContext.circle != null) {
28737 this.gMapContext.circle.setMap(null);
28741 options = Roo.apply({}, options, {
28742 strokeColor: "#0000FF",
28743 strokeOpacity: .35,
28745 fillColor: "#0000FF",
28749 options.map = this.gMapContext.map;
28750 options.radius = radius;
28751 options.center = center;
28752 this.gMapContext.circle = new google.maps.Circle(options);
28753 return this.gMapContext.circle;
28759 setPosition: function(location)
28761 this.gMapContext.location = location;
28762 this.gMapContext.marker.setPosition(location);
28763 this.gMapContext.map.panTo(location);
28764 this.drawCircle(location, this.gMapContext.radius, {});
28768 if (this.gMapContext.settings.enableReverseGeocode) {
28769 this.gMapContext.geodecoder.geocode({
28770 latLng: this.gMapContext.location
28771 }, function(results, status) {
28773 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28774 _this.gMapContext.locationName = results[0].formatted_address;
28775 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28777 _this.fireEvent('positionchanged', this, location);
28784 this.fireEvent('positionchanged', this, location);
28789 google.maps.event.trigger(this.gMapContext.map, "resize");
28791 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28793 this.fireEvent('resize', this);
28796 setPositionByLatLng: function(latitude, longitude)
28798 this.setPosition(new google.maps.LatLng(latitude, longitude));
28801 getCurrentPosition: function()
28804 latitude: this.gMapContext.location.lat(),
28805 longitude: this.gMapContext.location.lng()
28809 getAddressName: function()
28811 return this.gMapContext.locationName;
28814 getAddressComponents: function()
28816 return this.gMapContext.addressComponents;
28819 address_component_from_google_geocode: function(address_components)
28823 for (var i = 0; i < address_components.length; i++) {
28824 var component = address_components[i];
28825 if (component.types.indexOf("postal_code") >= 0) {
28826 result.postalCode = component.short_name;
28827 } else if (component.types.indexOf("street_number") >= 0) {
28828 result.streetNumber = component.short_name;
28829 } else if (component.types.indexOf("route") >= 0) {
28830 result.streetName = component.short_name;
28831 } else if (component.types.indexOf("neighborhood") >= 0) {
28832 result.city = component.short_name;
28833 } else if (component.types.indexOf("locality") >= 0) {
28834 result.city = component.short_name;
28835 } else if (component.types.indexOf("sublocality") >= 0) {
28836 result.district = component.short_name;
28837 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28838 result.stateOrProvince = component.short_name;
28839 } else if (component.types.indexOf("country") >= 0) {
28840 result.country = component.short_name;
28844 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28845 result.addressLine2 = "";
28849 setZoomLevel: function(zoom)
28851 this.gMapContext.map.setZoom(zoom);
28864 this.fireEvent('show', this);
28875 this.fireEvent('hide', this);
28880 Roo.apply(Roo.bootstrap.LocationPicker, {
28882 OverlayView : function(map, options)
28884 options = options || {};
28891 * @class Roo.bootstrap.Alert
28892 * @extends Roo.bootstrap.Component
28893 * Bootstrap Alert class - shows an alert area box
28895 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28896 Enter a valid email address
28899 * @cfg {String} title The title of alert
28900 * @cfg {String} html The content of alert
28901 * @cfg {String} weight ( success | info | warning | danger )
28902 * @cfg {String} faicon font-awesomeicon
28905 * Create a new alert
28906 * @param {Object} config The config object
28910 Roo.bootstrap.Alert = function(config){
28911 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28915 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
28922 getAutoCreate : function()
28931 cls : 'roo-alert-icon'
28936 cls : 'roo-alert-title',
28941 cls : 'roo-alert-text',
28948 cfg.cn[0].cls += ' fa ' + this.faicon;
28952 cfg.cls += ' alert-' + this.weight;
28958 initEvents: function()
28960 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28963 setTitle : function(str)
28965 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28968 setText : function(str)
28970 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28973 setWeight : function(weight)
28976 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28979 this.weight = weight;
28981 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28984 setIcon : function(icon)
28987 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28990 this.faicon = icon;
28992 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29013 * @class Roo.bootstrap.UploadCropbox
29014 * @extends Roo.bootstrap.Component
29015 * Bootstrap UploadCropbox class
29016 * @cfg {String} emptyText show when image has been loaded
29017 * @cfg {String} rotateNotify show when image too small to rotate
29018 * @cfg {Number} errorTimeout default 3000
29019 * @cfg {Number} minWidth default 300
29020 * @cfg {Number} minHeight default 300
29021 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29022 * @cfg {Boolean} isDocument (true|false) default false
29023 * @cfg {String} url action url
29024 * @cfg {String} paramName default 'imageUpload'
29025 * @cfg {String} method default POST
29026 * @cfg {Boolean} loadMask (true|false) default true
29027 * @cfg {Boolean} loadingText default 'Loading...'
29030 * Create a new UploadCropbox
29031 * @param {Object} config The config object
29034 Roo.bootstrap.UploadCropbox = function(config){
29035 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29039 * @event beforeselectfile
29040 * Fire before select file
29041 * @param {Roo.bootstrap.UploadCropbox} this
29043 "beforeselectfile" : true,
29046 * Fire after initEvent
29047 * @param {Roo.bootstrap.UploadCropbox} this
29052 * Fire after initEvent
29053 * @param {Roo.bootstrap.UploadCropbox} this
29054 * @param {String} data
29059 * Fire when preparing the file data
29060 * @param {Roo.bootstrap.UploadCropbox} this
29061 * @param {Object} file
29066 * Fire when get exception
29067 * @param {Roo.bootstrap.UploadCropbox} this
29068 * @param {XMLHttpRequest} xhr
29070 "exception" : true,
29072 * @event beforeloadcanvas
29073 * Fire before load the canvas
29074 * @param {Roo.bootstrap.UploadCropbox} this
29075 * @param {String} src
29077 "beforeloadcanvas" : true,
29080 * Fire when trash image
29081 * @param {Roo.bootstrap.UploadCropbox} this
29086 * Fire when download the image
29087 * @param {Roo.bootstrap.UploadCropbox} this
29091 * @event footerbuttonclick
29092 * Fire when footerbuttonclick
29093 * @param {Roo.bootstrap.UploadCropbox} this
29094 * @param {String} type
29096 "footerbuttonclick" : true,
29100 * @param {Roo.bootstrap.UploadCropbox} this
29105 * Fire when rotate the image
29106 * @param {Roo.bootstrap.UploadCropbox} this
29107 * @param {String} pos
29112 * Fire when inspect the file
29113 * @param {Roo.bootstrap.UploadCropbox} this
29114 * @param {Object} file
29119 * Fire when xhr upload the file
29120 * @param {Roo.bootstrap.UploadCropbox} this
29121 * @param {Object} data
29126 * Fire when arrange the file data
29127 * @param {Roo.bootstrap.UploadCropbox} this
29128 * @param {Object} formData
29133 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29136 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29138 emptyText : 'Click to upload image',
29139 rotateNotify : 'Image is too small to rotate',
29140 errorTimeout : 3000,
29154 cropType : 'image/jpeg',
29156 canvasLoaded : false,
29157 isDocument : false,
29159 paramName : 'imageUpload',
29161 loadingText : 'Loading...',
29164 getAutoCreate : function()
29168 cls : 'roo-upload-cropbox',
29172 cls : 'roo-upload-cropbox-selector',
29177 cls : 'roo-upload-cropbox-body',
29178 style : 'cursor:pointer',
29182 cls : 'roo-upload-cropbox-preview'
29186 cls : 'roo-upload-cropbox-thumb'
29190 cls : 'roo-upload-cropbox-empty-notify',
29191 html : this.emptyText
29195 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29196 html : this.rotateNotify
29202 cls : 'roo-upload-cropbox-footer',
29205 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29215 onRender : function(ct, position)
29217 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29219 if (this.buttons.length) {
29221 Roo.each(this.buttons, function(bb) {
29223 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29225 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29231 this.maskEl = this.el;
29235 initEvents : function()
29237 this.urlAPI = (window.createObjectURL && window) ||
29238 (window.URL && URL.revokeObjectURL && URL) ||
29239 (window.webkitURL && webkitURL);
29241 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29242 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29244 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29245 this.selectorEl.hide();
29247 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29248 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29250 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29251 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29252 this.thumbEl.hide();
29254 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29255 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29257 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29258 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29259 this.errorEl.hide();
29261 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29262 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29263 this.footerEl.hide();
29265 this.setThumbBoxSize();
29271 this.fireEvent('initial', this);
29278 window.addEventListener("resize", function() { _this.resize(); } );
29280 this.bodyEl.on('click', this.beforeSelectFile, this);
29283 this.bodyEl.on('touchstart', this.onTouchStart, this);
29284 this.bodyEl.on('touchmove', this.onTouchMove, this);
29285 this.bodyEl.on('touchend', this.onTouchEnd, this);
29289 this.bodyEl.on('mousedown', this.onMouseDown, this);
29290 this.bodyEl.on('mousemove', this.onMouseMove, this);
29291 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29292 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29293 Roo.get(document).on('mouseup', this.onMouseUp, this);
29296 this.selectorEl.on('change', this.onFileSelected, this);
29302 this.baseScale = 1;
29304 this.baseRotate = 1;
29305 this.dragable = false;
29306 this.pinching = false;
29309 this.cropData = false;
29310 this.notifyEl.dom.innerHTML = this.emptyText;
29312 this.selectorEl.dom.value = '';
29316 resize : function()
29318 if(this.fireEvent('resize', this) != false){
29319 this.setThumbBoxPosition();
29320 this.setCanvasPosition();
29324 onFooterButtonClick : function(e, el, o, type)
29327 case 'rotate-left' :
29328 this.onRotateLeft(e);
29330 case 'rotate-right' :
29331 this.onRotateRight(e);
29334 this.beforeSelectFile(e);
29349 this.fireEvent('footerbuttonclick', this, type);
29352 beforeSelectFile : function(e)
29354 e.preventDefault();
29356 if(this.fireEvent('beforeselectfile', this) != false){
29357 this.selectorEl.dom.click();
29361 onFileSelected : function(e)
29363 e.preventDefault();
29365 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29369 var file = this.selectorEl.dom.files[0];
29371 if(this.fireEvent('inspect', this, file) != false){
29372 this.prepare(file);
29377 trash : function(e)
29379 this.fireEvent('trash', this);
29382 download : function(e)
29384 this.fireEvent('download', this);
29387 loadCanvas : function(src)
29389 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29393 this.imageEl = document.createElement('img');
29397 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29399 this.imageEl.src = src;
29403 onLoadCanvas : function()
29405 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29406 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29408 this.bodyEl.un('click', this.beforeSelectFile, this);
29410 this.notifyEl.hide();
29411 this.thumbEl.show();
29412 this.footerEl.show();
29414 this.baseRotateLevel();
29416 if(this.isDocument){
29417 this.setThumbBoxSize();
29420 this.setThumbBoxPosition();
29422 this.baseScaleLevel();
29428 this.canvasLoaded = true;
29431 this.maskEl.unmask();
29436 setCanvasPosition : function()
29438 if(!this.canvasEl){
29442 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29443 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29445 this.previewEl.setLeft(pw);
29446 this.previewEl.setTop(ph);
29450 onMouseDown : function(e)
29454 this.dragable = true;
29455 this.pinching = false;
29457 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29458 this.dragable = false;
29462 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29463 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29467 onMouseMove : function(e)
29471 if(!this.canvasLoaded){
29475 if (!this.dragable){
29479 var minX = Math.ceil(this.thumbEl.getLeft(true));
29480 var minY = Math.ceil(this.thumbEl.getTop(true));
29482 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29483 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29485 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29486 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29488 x = x - this.mouseX;
29489 y = y - this.mouseY;
29491 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29492 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29494 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29495 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29497 this.previewEl.setLeft(bgX);
29498 this.previewEl.setTop(bgY);
29500 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29501 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29504 onMouseUp : function(e)
29508 this.dragable = false;
29511 onMouseWheel : function(e)
29515 this.startScale = this.scale;
29517 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29519 if(!this.zoomable()){
29520 this.scale = this.startScale;
29529 zoomable : function()
29531 var minScale = this.thumbEl.getWidth() / this.minWidth;
29533 if(this.minWidth < this.minHeight){
29534 minScale = this.thumbEl.getHeight() / this.minHeight;
29537 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29538 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29542 (this.rotate == 0 || this.rotate == 180) &&
29544 width > this.imageEl.OriginWidth ||
29545 height > this.imageEl.OriginHeight ||
29546 (width < this.minWidth && height < this.minHeight)
29554 (this.rotate == 90 || this.rotate == 270) &&
29556 width > this.imageEl.OriginWidth ||
29557 height > this.imageEl.OriginHeight ||
29558 (width < this.minHeight && height < this.minWidth)
29565 !this.isDocument &&
29566 (this.rotate == 0 || this.rotate == 180) &&
29568 width < this.minWidth ||
29569 width > this.imageEl.OriginWidth ||
29570 height < this.minHeight ||
29571 height > this.imageEl.OriginHeight
29578 !this.isDocument &&
29579 (this.rotate == 90 || this.rotate == 270) &&
29581 width < this.minHeight ||
29582 width > this.imageEl.OriginWidth ||
29583 height < this.minWidth ||
29584 height > this.imageEl.OriginHeight
29594 onRotateLeft : function(e)
29596 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29598 var minScale = this.thumbEl.getWidth() / this.minWidth;
29600 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29601 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29603 this.startScale = this.scale;
29605 while (this.getScaleLevel() < minScale){
29607 this.scale = this.scale + 1;
29609 if(!this.zoomable()){
29614 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29615 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29620 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29627 this.scale = this.startScale;
29629 this.onRotateFail();
29634 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29636 if(this.isDocument){
29637 this.setThumbBoxSize();
29638 this.setThumbBoxPosition();
29639 this.setCanvasPosition();
29644 this.fireEvent('rotate', this, 'left');
29648 onRotateRight : function(e)
29650 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29652 var minScale = this.thumbEl.getWidth() / this.minWidth;
29654 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29655 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29657 this.startScale = this.scale;
29659 while (this.getScaleLevel() < minScale){
29661 this.scale = this.scale + 1;
29663 if(!this.zoomable()){
29668 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29669 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29674 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29681 this.scale = this.startScale;
29683 this.onRotateFail();
29688 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29690 if(this.isDocument){
29691 this.setThumbBoxSize();
29692 this.setThumbBoxPosition();
29693 this.setCanvasPosition();
29698 this.fireEvent('rotate', this, 'right');
29701 onRotateFail : function()
29703 this.errorEl.show(true);
29707 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29712 this.previewEl.dom.innerHTML = '';
29714 var canvasEl = document.createElement("canvas");
29716 var contextEl = canvasEl.getContext("2d");
29718 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29719 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29720 var center = this.imageEl.OriginWidth / 2;
29722 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29723 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29724 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29725 center = this.imageEl.OriginHeight / 2;
29728 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29730 contextEl.translate(center, center);
29731 contextEl.rotate(this.rotate * Math.PI / 180);
29733 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29735 this.canvasEl = document.createElement("canvas");
29737 this.contextEl = this.canvasEl.getContext("2d");
29739 switch (this.rotate) {
29742 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29743 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29745 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29750 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29751 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29753 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29754 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);
29758 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29763 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29764 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29766 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29767 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);
29771 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);
29776 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29777 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29779 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29780 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29784 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);
29791 this.previewEl.appendChild(this.canvasEl);
29793 this.setCanvasPosition();
29798 if(!this.canvasLoaded){
29802 var imageCanvas = document.createElement("canvas");
29804 var imageContext = imageCanvas.getContext("2d");
29806 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29807 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29809 var center = imageCanvas.width / 2;
29811 imageContext.translate(center, center);
29813 imageContext.rotate(this.rotate * Math.PI / 180);
29815 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29817 var canvas = document.createElement("canvas");
29819 var context = canvas.getContext("2d");
29821 canvas.width = this.minWidth;
29822 canvas.height = this.minHeight;
29824 switch (this.rotate) {
29827 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29828 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (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 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29867 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29868 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29870 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29871 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29873 var targetWidth = this.minWidth - 2 * x;
29874 var targetHeight = this.minHeight - 2 * y;
29878 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29879 scale = targetWidth / width;
29882 if(x > 0 && y == 0){
29883 scale = targetHeight / height;
29886 if(x > 0 && y > 0){
29887 scale = targetWidth / width;
29889 if(width < height){
29890 scale = targetHeight / height;
29894 context.scale(scale, scale);
29896 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29897 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29899 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29900 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29902 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29904 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29909 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29910 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29912 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29913 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29915 var targetWidth = this.minWidth - 2 * x;
29916 var targetHeight = this.minHeight - 2 * y;
29920 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29921 scale = targetWidth / width;
29924 if(x > 0 && y == 0){
29925 scale = targetHeight / height;
29928 if(x > 0 && y > 0){
29929 scale = targetWidth / width;
29931 if(width < height){
29932 scale = targetHeight / height;
29936 context.scale(scale, scale);
29938 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29939 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29941 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29942 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29944 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29945 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29947 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29952 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29953 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29955 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29956 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29958 var targetWidth = this.minWidth - 2 * x;
29959 var targetHeight = this.minHeight - 2 * y;
29963 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29964 scale = targetWidth / width;
29967 if(x > 0 && y == 0){
29968 scale = targetHeight / height;
29971 if(x > 0 && y > 0){
29972 scale = targetWidth / width;
29974 if(width < height){
29975 scale = targetHeight / height;
29979 context.scale(scale, scale);
29981 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29982 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29984 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29985 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29987 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29989 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29996 this.cropData = canvas.toDataURL(this.cropType);
29998 if(this.fireEvent('crop', this, this.cropData) !== false){
29999 this.process(this.file, this.cropData);
30006 setThumbBoxSize : function()
30010 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30011 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30012 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30014 this.minWidth = width;
30015 this.minHeight = height;
30017 if(this.rotate == 90 || this.rotate == 270){
30018 this.minWidth = height;
30019 this.minHeight = width;
30024 width = Math.ceil(this.minWidth * height / this.minHeight);
30026 if(this.minWidth > this.minHeight){
30028 height = Math.ceil(this.minHeight * width / this.minWidth);
30031 this.thumbEl.setStyle({
30032 width : width + 'px',
30033 height : height + 'px'
30040 setThumbBoxPosition : function()
30042 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30043 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30045 this.thumbEl.setLeft(x);
30046 this.thumbEl.setTop(y);
30050 baseRotateLevel : function()
30052 this.baseRotate = 1;
30055 typeof(this.exif) != 'undefined' &&
30056 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30057 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30059 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30062 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30066 baseScaleLevel : function()
30070 if(this.isDocument){
30072 if(this.baseRotate == 6 || this.baseRotate == 8){
30074 height = this.thumbEl.getHeight();
30075 this.baseScale = height / this.imageEl.OriginWidth;
30077 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30078 width = this.thumbEl.getWidth();
30079 this.baseScale = width / this.imageEl.OriginHeight;
30085 height = this.thumbEl.getHeight();
30086 this.baseScale = height / this.imageEl.OriginHeight;
30088 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30089 width = this.thumbEl.getWidth();
30090 this.baseScale = width / this.imageEl.OriginWidth;
30096 if(this.baseRotate == 6 || this.baseRotate == 8){
30098 width = this.thumbEl.getHeight();
30099 this.baseScale = width / this.imageEl.OriginHeight;
30101 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30102 height = this.thumbEl.getWidth();
30103 this.baseScale = height / this.imageEl.OriginHeight;
30106 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30107 height = this.thumbEl.getWidth();
30108 this.baseScale = height / this.imageEl.OriginHeight;
30110 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30111 width = this.thumbEl.getHeight();
30112 this.baseScale = width / this.imageEl.OriginWidth;
30119 width = this.thumbEl.getWidth();
30120 this.baseScale = width / this.imageEl.OriginWidth;
30122 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30123 height = this.thumbEl.getHeight();
30124 this.baseScale = height / this.imageEl.OriginHeight;
30127 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30129 height = this.thumbEl.getHeight();
30130 this.baseScale = height / this.imageEl.OriginHeight;
30132 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30133 width = this.thumbEl.getWidth();
30134 this.baseScale = width / this.imageEl.OriginWidth;
30142 getScaleLevel : function()
30144 return this.baseScale * Math.pow(1.1, this.scale);
30147 onTouchStart : function(e)
30149 if(!this.canvasLoaded){
30150 this.beforeSelectFile(e);
30154 var touches = e.browserEvent.touches;
30160 if(touches.length == 1){
30161 this.onMouseDown(e);
30165 if(touches.length != 2){
30171 for(var i = 0, finger; finger = touches[i]; i++){
30172 coords.push(finger.pageX, finger.pageY);
30175 var x = Math.pow(coords[0] - coords[2], 2);
30176 var y = Math.pow(coords[1] - coords[3], 2);
30178 this.startDistance = Math.sqrt(x + y);
30180 this.startScale = this.scale;
30182 this.pinching = true;
30183 this.dragable = false;
30187 onTouchMove : function(e)
30189 if(!this.pinching && !this.dragable){
30193 var touches = e.browserEvent.touches;
30200 this.onMouseMove(e);
30206 for(var i = 0, finger; finger = touches[i]; i++){
30207 coords.push(finger.pageX, finger.pageY);
30210 var x = Math.pow(coords[0] - coords[2], 2);
30211 var y = Math.pow(coords[1] - coords[3], 2);
30213 this.endDistance = Math.sqrt(x + y);
30215 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30217 if(!this.zoomable()){
30218 this.scale = this.startScale;
30226 onTouchEnd : function(e)
30228 this.pinching = false;
30229 this.dragable = false;
30233 process : function(file, crop)
30236 this.maskEl.mask(this.loadingText);
30239 this.xhr = new XMLHttpRequest();
30241 file.xhr = this.xhr;
30243 this.xhr.open(this.method, this.url, true);
30246 "Accept": "application/json",
30247 "Cache-Control": "no-cache",
30248 "X-Requested-With": "XMLHttpRequest"
30251 for (var headerName in headers) {
30252 var headerValue = headers[headerName];
30254 this.xhr.setRequestHeader(headerName, headerValue);
30260 this.xhr.onload = function()
30262 _this.xhrOnLoad(_this.xhr);
30265 this.xhr.onerror = function()
30267 _this.xhrOnError(_this.xhr);
30270 var formData = new FormData();
30272 formData.append('returnHTML', 'NO');
30275 formData.append('crop', crop);
30278 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30279 formData.append(this.paramName, file, file.name);
30282 if(typeof(file.filename) != 'undefined'){
30283 formData.append('filename', file.filename);
30286 if(typeof(file.mimetype) != 'undefined'){
30287 formData.append('mimetype', file.mimetype);
30290 if(this.fireEvent('arrange', this, formData) != false){
30291 this.xhr.send(formData);
30295 xhrOnLoad : function(xhr)
30298 this.maskEl.unmask();
30301 if (xhr.readyState !== 4) {
30302 this.fireEvent('exception', this, xhr);
30306 var response = Roo.decode(xhr.responseText);
30308 if(!response.success){
30309 this.fireEvent('exception', this, xhr);
30313 var response = Roo.decode(xhr.responseText);
30315 this.fireEvent('upload', this, response);
30319 xhrOnError : function()
30322 this.maskEl.unmask();
30325 Roo.log('xhr on error');
30327 var response = Roo.decode(xhr.responseText);
30333 prepare : function(file)
30336 this.maskEl.mask(this.loadingText);
30342 if(typeof(file) === 'string'){
30343 this.loadCanvas(file);
30347 if(!file || !this.urlAPI){
30352 this.cropType = file.type;
30356 if(this.fireEvent('prepare', this, this.file) != false){
30358 var reader = new FileReader();
30360 reader.onload = function (e) {
30361 if (e.target.error) {
30362 Roo.log(e.target.error);
30366 var buffer = e.target.result,
30367 dataView = new DataView(buffer),
30369 maxOffset = dataView.byteLength - 4,
30373 if (dataView.getUint16(0) === 0xffd8) {
30374 while (offset < maxOffset) {
30375 markerBytes = dataView.getUint16(offset);
30377 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30378 markerLength = dataView.getUint16(offset + 2) + 2;
30379 if (offset + markerLength > dataView.byteLength) {
30380 Roo.log('Invalid meta data: Invalid segment size.');
30384 if(markerBytes == 0xffe1){
30385 _this.parseExifData(
30392 offset += markerLength;
30402 var url = _this.urlAPI.createObjectURL(_this.file);
30404 _this.loadCanvas(url);
30409 reader.readAsArrayBuffer(this.file);
30415 parseExifData : function(dataView, offset, length)
30417 var tiffOffset = offset + 10,
30421 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30422 // No Exif data, might be XMP data instead
30426 // Check for the ASCII code for "Exif" (0x45786966):
30427 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30428 // No Exif data, might be XMP data instead
30431 if (tiffOffset + 8 > dataView.byteLength) {
30432 Roo.log('Invalid Exif data: Invalid segment size.');
30435 // Check for the two null bytes:
30436 if (dataView.getUint16(offset + 8) !== 0x0000) {
30437 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30440 // Check the byte alignment:
30441 switch (dataView.getUint16(tiffOffset)) {
30443 littleEndian = true;
30446 littleEndian = false;
30449 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30452 // Check for the TIFF tag marker (0x002A):
30453 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30454 Roo.log('Invalid Exif data: Missing TIFF marker.');
30457 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30458 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30460 this.parseExifTags(
30463 tiffOffset + dirOffset,
30468 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30473 if (dirOffset + 6 > dataView.byteLength) {
30474 Roo.log('Invalid Exif data: Invalid directory offset.');
30477 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30478 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30479 if (dirEndOffset + 4 > dataView.byteLength) {
30480 Roo.log('Invalid Exif data: Invalid directory size.');
30483 for (i = 0; i < tagsNumber; i += 1) {
30487 dirOffset + 2 + 12 * i, // tag offset
30491 // Return the offset to the next directory:
30492 return dataView.getUint32(dirEndOffset, littleEndian);
30495 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30497 var tag = dataView.getUint16(offset, littleEndian);
30499 this.exif[tag] = this.getExifValue(
30503 dataView.getUint16(offset + 2, littleEndian), // tag type
30504 dataView.getUint32(offset + 4, littleEndian), // tag length
30509 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30511 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30520 Roo.log('Invalid Exif data: Invalid tag type.');
30524 tagSize = tagType.size * length;
30525 // Determine if the value is contained in the dataOffset bytes,
30526 // or if the value at the dataOffset is a pointer to the actual data:
30527 dataOffset = tagSize > 4 ?
30528 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30529 if (dataOffset + tagSize > dataView.byteLength) {
30530 Roo.log('Invalid Exif data: Invalid data offset.');
30533 if (length === 1) {
30534 return tagType.getValue(dataView, dataOffset, littleEndian);
30537 for (i = 0; i < length; i += 1) {
30538 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30541 if (tagType.ascii) {
30543 // Concatenate the chars:
30544 for (i = 0; i < values.length; i += 1) {
30546 // Ignore the terminating NULL byte(s):
30547 if (c === '\u0000') {
30559 Roo.apply(Roo.bootstrap.UploadCropbox, {
30561 'Orientation': 0x0112
30565 1: 0, //'top-left',
30567 3: 180, //'bottom-right',
30568 // 4: 'bottom-left',
30570 6: 90, //'right-top',
30571 // 7: 'right-bottom',
30572 8: 270 //'left-bottom'
30576 // byte, 8-bit unsigned int:
30578 getValue: function (dataView, dataOffset) {
30579 return dataView.getUint8(dataOffset);
30583 // ascii, 8-bit byte:
30585 getValue: function (dataView, dataOffset) {
30586 return String.fromCharCode(dataView.getUint8(dataOffset));
30591 // short, 16 bit int:
30593 getValue: function (dataView, dataOffset, littleEndian) {
30594 return dataView.getUint16(dataOffset, littleEndian);
30598 // long, 32 bit int:
30600 getValue: function (dataView, dataOffset, littleEndian) {
30601 return dataView.getUint32(dataOffset, littleEndian);
30605 // rational = two long values, first is numerator, second is denominator:
30607 getValue: function (dataView, dataOffset, littleEndian) {
30608 return dataView.getUint32(dataOffset, littleEndian) /
30609 dataView.getUint32(dataOffset + 4, littleEndian);
30613 // slong, 32 bit signed int:
30615 getValue: function (dataView, dataOffset, littleEndian) {
30616 return dataView.getInt32(dataOffset, littleEndian);
30620 // srational, two slongs, first is numerator, second is denominator:
30622 getValue: function (dataView, dataOffset, littleEndian) {
30623 return dataView.getInt32(dataOffset, littleEndian) /
30624 dataView.getInt32(dataOffset + 4, littleEndian);
30634 cls : 'btn-group roo-upload-cropbox-rotate-left',
30635 action : 'rotate-left',
30639 cls : 'btn btn-default',
30640 html : '<i class="fa fa-undo"></i>'
30646 cls : 'btn-group roo-upload-cropbox-picture',
30647 action : 'picture',
30651 cls : 'btn btn-default',
30652 html : '<i class="fa fa-picture-o"></i>'
30658 cls : 'btn-group roo-upload-cropbox-rotate-right',
30659 action : 'rotate-right',
30663 cls : 'btn btn-default',
30664 html : '<i class="fa fa-repeat"></i>'
30672 cls : 'btn-group roo-upload-cropbox-rotate-left',
30673 action : 'rotate-left',
30677 cls : 'btn btn-default',
30678 html : '<i class="fa fa-undo"></i>'
30684 cls : 'btn-group roo-upload-cropbox-download',
30685 action : 'download',
30689 cls : 'btn btn-default',
30690 html : '<i class="fa fa-download"></i>'
30696 cls : 'btn-group roo-upload-cropbox-crop',
30701 cls : 'btn btn-default',
30702 html : '<i class="fa fa-crop"></i>'
30708 cls : 'btn-group roo-upload-cropbox-trash',
30713 cls : 'btn btn-default',
30714 html : '<i class="fa fa-trash"></i>'
30720 cls : 'btn-group roo-upload-cropbox-rotate-right',
30721 action : 'rotate-right',
30725 cls : 'btn btn-default',
30726 html : '<i class="fa fa-repeat"></i>'
30734 cls : 'btn-group roo-upload-cropbox-rotate-left',
30735 action : 'rotate-left',
30739 cls : 'btn btn-default',
30740 html : '<i class="fa fa-undo"></i>'
30746 cls : 'btn-group roo-upload-cropbox-rotate-right',
30747 action : 'rotate-right',
30751 cls : 'btn btn-default',
30752 html : '<i class="fa fa-repeat"></i>'
30765 * @class Roo.bootstrap.DocumentManager
30766 * @extends Roo.bootstrap.Component
30767 * Bootstrap DocumentManager class
30768 * @cfg {String} paramName default 'imageUpload'
30769 * @cfg {String} toolTipName default 'filename'
30770 * @cfg {String} method default POST
30771 * @cfg {String} url action url
30772 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30773 * @cfg {Boolean} multiple multiple upload default true
30774 * @cfg {Number} thumbSize default 300
30775 * @cfg {String} fieldLabel
30776 * @cfg {Number} labelWidth default 4
30777 * @cfg {String} labelAlign (left|top) default left
30778 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30779 * @cfg {Number} labellg set the width of label (1-12)
30780 * @cfg {Number} labelmd set the width of label (1-12)
30781 * @cfg {Number} labelsm set the width of label (1-12)
30782 * @cfg {Number} labelxs set the width of label (1-12)
30785 * Create a new DocumentManager
30786 * @param {Object} config The config object
30789 Roo.bootstrap.DocumentManager = function(config){
30790 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30793 this.delegates = [];
30798 * Fire when initial the DocumentManager
30799 * @param {Roo.bootstrap.DocumentManager} this
30804 * inspect selected file
30805 * @param {Roo.bootstrap.DocumentManager} this
30806 * @param {File} file
30811 * Fire when xhr load exception
30812 * @param {Roo.bootstrap.DocumentManager} this
30813 * @param {XMLHttpRequest} xhr
30815 "exception" : true,
30817 * @event afterupload
30818 * Fire when xhr load exception
30819 * @param {Roo.bootstrap.DocumentManager} this
30820 * @param {XMLHttpRequest} xhr
30822 "afterupload" : true,
30825 * prepare the form data
30826 * @param {Roo.bootstrap.DocumentManager} this
30827 * @param {Object} formData
30832 * Fire when remove the file
30833 * @param {Roo.bootstrap.DocumentManager} this
30834 * @param {Object} file
30839 * Fire after refresh the file
30840 * @param {Roo.bootstrap.DocumentManager} this
30845 * Fire after click the image
30846 * @param {Roo.bootstrap.DocumentManager} this
30847 * @param {Object} file
30852 * Fire when upload a image and editable set to true
30853 * @param {Roo.bootstrap.DocumentManager} this
30854 * @param {Object} file
30858 * @event beforeselectfile
30859 * Fire before select file
30860 * @param {Roo.bootstrap.DocumentManager} this
30862 "beforeselectfile" : true,
30865 * Fire before process file
30866 * @param {Roo.bootstrap.DocumentManager} this
30867 * @param {Object} file
30871 * @event previewrendered
30872 * Fire when preview rendered
30873 * @param {Roo.bootstrap.DocumentManager} this
30874 * @param {Object} file
30876 "previewrendered" : true,
30879 "previewResize" : true
30884 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
30893 paramName : 'imageUpload',
30894 toolTipName : 'filename',
30897 labelAlign : 'left',
30907 getAutoCreate : function()
30909 var managerWidget = {
30911 cls : 'roo-document-manager',
30915 cls : 'roo-document-manager-selector',
30920 cls : 'roo-document-manager-uploader',
30924 cls : 'roo-document-manager-upload-btn',
30925 html : '<i class="fa fa-plus"></i>'
30936 cls : 'column col-md-12',
30941 if(this.fieldLabel.length){
30946 cls : 'column col-md-12',
30947 html : this.fieldLabel
30951 cls : 'column col-md-12',
30956 if(this.labelAlign == 'left'){
30961 html : this.fieldLabel
30970 if(this.labelWidth > 12){
30971 content[0].style = "width: " + this.labelWidth + 'px';
30974 if(this.labelWidth < 13 && this.labelmd == 0){
30975 this.labelmd = this.labelWidth;
30978 if(this.labellg > 0){
30979 content[0].cls += ' col-lg-' + this.labellg;
30980 content[1].cls += ' col-lg-' + (12 - this.labellg);
30983 if(this.labelmd > 0){
30984 content[0].cls += ' col-md-' + this.labelmd;
30985 content[1].cls += ' col-md-' + (12 - this.labelmd);
30988 if(this.labelsm > 0){
30989 content[0].cls += ' col-sm-' + this.labelsm;
30990 content[1].cls += ' col-sm-' + (12 - this.labelsm);
30993 if(this.labelxs > 0){
30994 content[0].cls += ' col-xs-' + this.labelxs;
30995 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31003 cls : 'row clearfix',
31011 initEvents : function()
31013 this.managerEl = this.el.select('.roo-document-manager', true).first();
31014 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31016 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31017 this.selectorEl.hide();
31020 this.selectorEl.attr('multiple', 'multiple');
31023 this.selectorEl.on('change', this.onFileSelected, this);
31025 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31026 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31028 this.uploader.on('click', this.onUploaderClick, this);
31030 this.renderProgressDialog();
31034 window.addEventListener("resize", function() { _this.refresh(); } );
31036 this.fireEvent('initial', this);
31039 renderProgressDialog : function()
31043 this.progressDialog = new Roo.bootstrap.Modal({
31044 cls : 'roo-document-manager-progress-dialog',
31045 allow_close : false,
31056 btnclick : function() {
31057 _this.uploadCancel();
31063 this.progressDialog.render(Roo.get(document.body));
31065 this.progress = new Roo.bootstrap.Progress({
31066 cls : 'roo-document-manager-progress',
31071 this.progress.render(this.progressDialog.getChildContainer());
31073 this.progressBar = new Roo.bootstrap.ProgressBar({
31074 cls : 'roo-document-manager-progress-bar',
31077 aria_valuemax : 12,
31081 this.progressBar.render(this.progress.getChildContainer());
31084 onUploaderClick : function(e)
31086 e.preventDefault();
31088 if(this.fireEvent('beforeselectfile', this) != false){
31089 this.selectorEl.dom.click();
31094 onFileSelected : function(e)
31096 e.preventDefault();
31098 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31102 Roo.each(this.selectorEl.dom.files, function(file){
31103 if(this.fireEvent('inspect', this, file) != false){
31104 this.files.push(file);
31114 this.selectorEl.dom.value = '';
31116 if(!this.files || !this.files.length){
31120 if(this.boxes > 0 && this.files.length > this.boxes){
31121 this.files = this.files.slice(0, this.boxes);
31124 this.uploader.show();
31126 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31127 this.uploader.hide();
31136 Roo.each(this.files, function(file){
31138 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31139 var f = this.renderPreview(file);
31144 if(file.type.indexOf('image') != -1){
31145 this.delegates.push(
31147 _this.process(file);
31148 }).createDelegate(this)
31156 _this.process(file);
31157 }).createDelegate(this)
31162 this.files = files;
31164 this.delegates = this.delegates.concat(docs);
31166 if(!this.delegates.length){
31171 this.progressBar.aria_valuemax = this.delegates.length;
31178 arrange : function()
31180 if(!this.delegates.length){
31181 this.progressDialog.hide();
31186 var delegate = this.delegates.shift();
31188 this.progressDialog.show();
31190 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31192 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31197 refresh : function()
31199 this.uploader.show();
31201 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31202 this.uploader.hide();
31205 Roo.isTouch ? this.closable(false) : this.closable(true);
31207 this.fireEvent('refresh', this);
31210 onRemove : function(e, el, o)
31212 e.preventDefault();
31214 this.fireEvent('remove', this, o);
31218 remove : function(o)
31222 Roo.each(this.files, function(file){
31223 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31232 this.files = files;
31239 Roo.each(this.files, function(file){
31244 file.target.remove();
31253 onClick : function(e, el, o)
31255 e.preventDefault();
31257 this.fireEvent('click', this, o);
31261 closable : function(closable)
31263 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31265 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31277 xhrOnLoad : function(xhr)
31279 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31283 if (xhr.readyState !== 4) {
31285 this.fireEvent('exception', this, xhr);
31289 var response = Roo.decode(xhr.responseText);
31291 if(!response.success){
31293 this.fireEvent('exception', this, xhr);
31297 var file = this.renderPreview(response.data);
31299 this.files.push(file);
31303 this.fireEvent('afterupload', this, xhr);
31307 xhrOnError : function(xhr)
31309 Roo.log('xhr on error');
31311 var response = Roo.decode(xhr.responseText);
31318 process : function(file)
31320 if(this.fireEvent('process', this, file) !== false){
31321 if(this.editable && file.type.indexOf('image') != -1){
31322 this.fireEvent('edit', this, file);
31326 this.uploadStart(file, false);
31333 uploadStart : function(file, crop)
31335 this.xhr = new XMLHttpRequest();
31337 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31342 file.xhr = this.xhr;
31344 this.managerEl.createChild({
31346 cls : 'roo-document-manager-loading',
31350 tooltip : file.name,
31351 cls : 'roo-document-manager-thumb',
31352 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31358 this.xhr.open(this.method, this.url, true);
31361 "Accept": "application/json",
31362 "Cache-Control": "no-cache",
31363 "X-Requested-With": "XMLHttpRequest"
31366 for (var headerName in headers) {
31367 var headerValue = headers[headerName];
31369 this.xhr.setRequestHeader(headerName, headerValue);
31375 this.xhr.onload = function()
31377 _this.xhrOnLoad(_this.xhr);
31380 this.xhr.onerror = function()
31382 _this.xhrOnError(_this.xhr);
31385 var formData = new FormData();
31387 formData.append('returnHTML', 'NO');
31390 formData.append('crop', crop);
31393 formData.append(this.paramName, file, file.name);
31400 if(this.fireEvent('prepare', this, formData, options) != false){
31402 if(options.manually){
31406 this.xhr.send(formData);
31410 this.uploadCancel();
31413 uploadCancel : function()
31419 this.delegates = [];
31421 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31428 renderPreview : function(file)
31430 if(typeof(file.target) != 'undefined' && file.target){
31434 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31436 var previewEl = this.managerEl.createChild({
31438 cls : 'roo-document-manager-preview',
31442 tooltip : file[this.toolTipName],
31443 cls : 'roo-document-manager-thumb',
31444 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31449 html : '<i class="fa fa-times-circle"></i>'
31454 var close = previewEl.select('button.close', true).first();
31456 close.on('click', this.onRemove, this, file);
31458 file.target = previewEl;
31460 var image = previewEl.select('img', true).first();
31464 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31466 image.on('click', this.onClick, this, file);
31468 this.fireEvent('previewrendered', this, file);
31474 onPreviewLoad : function(file, image)
31476 if(typeof(file.target) == 'undefined' || !file.target){
31480 var width = image.dom.naturalWidth || image.dom.width;
31481 var height = image.dom.naturalHeight || image.dom.height;
31483 if(!this.previewResize) {
31487 if(width > height){
31488 file.target.addClass('wide');
31492 file.target.addClass('tall');
31497 uploadFromSource : function(file, crop)
31499 this.xhr = new XMLHttpRequest();
31501 this.managerEl.createChild({
31503 cls : 'roo-document-manager-loading',
31507 tooltip : file.name,
31508 cls : 'roo-document-manager-thumb',
31509 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31515 this.xhr.open(this.method, this.url, true);
31518 "Accept": "application/json",
31519 "Cache-Control": "no-cache",
31520 "X-Requested-With": "XMLHttpRequest"
31523 for (var headerName in headers) {
31524 var headerValue = headers[headerName];
31526 this.xhr.setRequestHeader(headerName, headerValue);
31532 this.xhr.onload = function()
31534 _this.xhrOnLoad(_this.xhr);
31537 this.xhr.onerror = function()
31539 _this.xhrOnError(_this.xhr);
31542 var formData = new FormData();
31544 formData.append('returnHTML', 'NO');
31546 formData.append('crop', crop);
31548 if(typeof(file.filename) != 'undefined'){
31549 formData.append('filename', file.filename);
31552 if(typeof(file.mimetype) != 'undefined'){
31553 formData.append('mimetype', file.mimetype);
31558 if(this.fireEvent('prepare', this, formData) != false){
31559 this.xhr.send(formData);
31569 * @class Roo.bootstrap.DocumentViewer
31570 * @extends Roo.bootstrap.Component
31571 * Bootstrap DocumentViewer class
31572 * @cfg {Boolean} showDownload (true|false) show download button (default true)
31573 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31576 * Create a new DocumentViewer
31577 * @param {Object} config The config object
31580 Roo.bootstrap.DocumentViewer = function(config){
31581 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31586 * Fire after initEvent
31587 * @param {Roo.bootstrap.DocumentViewer} this
31593 * @param {Roo.bootstrap.DocumentViewer} this
31598 * Fire after download button
31599 * @param {Roo.bootstrap.DocumentViewer} this
31604 * Fire after trash button
31605 * @param {Roo.bootstrap.DocumentViewer} this
31612 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
31614 showDownload : true,
31618 getAutoCreate : function()
31622 cls : 'roo-document-viewer',
31626 cls : 'roo-document-viewer-body',
31630 cls : 'roo-document-viewer-thumb',
31634 cls : 'roo-document-viewer-image'
31642 cls : 'roo-document-viewer-footer',
31645 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31649 cls : 'btn-group roo-document-viewer-download',
31653 cls : 'btn btn-default',
31654 html : '<i class="fa fa-download"></i>'
31660 cls : 'btn-group roo-document-viewer-trash',
31664 cls : 'btn btn-default',
31665 html : '<i class="fa fa-trash"></i>'
31678 initEvents : function()
31680 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31681 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31683 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31684 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31686 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31687 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31689 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31690 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31692 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31693 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31695 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31696 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31698 this.bodyEl.on('click', this.onClick, this);
31699 this.downloadBtn.on('click', this.onDownload, this);
31700 this.trashBtn.on('click', this.onTrash, this);
31702 this.downloadBtn.hide();
31703 this.trashBtn.hide();
31705 if(this.showDownload){
31706 this.downloadBtn.show();
31709 if(this.showTrash){
31710 this.trashBtn.show();
31713 if(!this.showDownload && !this.showTrash) {
31714 this.footerEl.hide();
31719 initial : function()
31721 this.fireEvent('initial', this);
31725 onClick : function(e)
31727 e.preventDefault();
31729 this.fireEvent('click', this);
31732 onDownload : function(e)
31734 e.preventDefault();
31736 this.fireEvent('download', this);
31739 onTrash : function(e)
31741 e.preventDefault();
31743 this.fireEvent('trash', this);
31755 * @class Roo.bootstrap.NavProgressBar
31756 * @extends Roo.bootstrap.Component
31757 * Bootstrap NavProgressBar class
31760 * Create a new nav progress bar
31761 * @param {Object} config The config object
31764 Roo.bootstrap.NavProgressBar = function(config){
31765 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31767 this.bullets = this.bullets || [];
31769 // Roo.bootstrap.NavProgressBar.register(this);
31773 * Fires when the active item changes
31774 * @param {Roo.bootstrap.NavProgressBar} this
31775 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31776 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
31783 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
31788 getAutoCreate : function()
31790 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31794 cls : 'roo-navigation-bar-group',
31798 cls : 'roo-navigation-top-bar'
31802 cls : 'roo-navigation-bullets-bar',
31806 cls : 'roo-navigation-bar'
31813 cls : 'roo-navigation-bottom-bar'
31823 initEvents: function()
31828 onRender : function(ct, position)
31830 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31832 if(this.bullets.length){
31833 Roo.each(this.bullets, function(b){
31842 addItem : function(cfg)
31844 var item = new Roo.bootstrap.NavProgressItem(cfg);
31846 item.parentId = this.id;
31847 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31850 var top = new Roo.bootstrap.Element({
31852 cls : 'roo-navigation-bar-text'
31855 var bottom = new Roo.bootstrap.Element({
31857 cls : 'roo-navigation-bar-text'
31860 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31861 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31863 var topText = new Roo.bootstrap.Element({
31865 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31868 var bottomText = new Roo.bootstrap.Element({
31870 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31873 topText.onRender(top.el, null);
31874 bottomText.onRender(bottom.el, null);
31877 item.bottomEl = bottom;
31880 this.barItems.push(item);
31885 getActive : function()
31887 var active = false;
31889 Roo.each(this.barItems, function(v){
31891 if (!v.isActive()) {
31903 setActiveItem : function(item)
31907 Roo.each(this.barItems, function(v){
31908 if (v.rid == item.rid) {
31912 if (v.isActive()) {
31913 v.setActive(false);
31918 item.setActive(true);
31920 this.fireEvent('changed', this, item, prev);
31923 getBarItem: function(rid)
31927 Roo.each(this.barItems, function(e) {
31928 if (e.rid != rid) {
31939 indexOfItem : function(item)
31943 Roo.each(this.barItems, function(v, i){
31945 if (v.rid != item.rid) {
31956 setActiveNext : function()
31958 var i = this.indexOfItem(this.getActive());
31960 if (i > this.barItems.length) {
31964 this.setActiveItem(this.barItems[i+1]);
31967 setActivePrev : function()
31969 var i = this.indexOfItem(this.getActive());
31975 this.setActiveItem(this.barItems[i-1]);
31978 format : function()
31980 if(!this.barItems.length){
31984 var width = 100 / this.barItems.length;
31986 Roo.each(this.barItems, function(i){
31987 i.el.setStyle('width', width + '%');
31988 i.topEl.el.setStyle('width', width + '%');
31989 i.bottomEl.el.setStyle('width', width + '%');
31998 * Nav Progress Item
32003 * @class Roo.bootstrap.NavProgressItem
32004 * @extends Roo.bootstrap.Component
32005 * Bootstrap NavProgressItem class
32006 * @cfg {String} rid the reference id
32007 * @cfg {Boolean} active (true|false) Is item active default false
32008 * @cfg {Boolean} disabled (true|false) Is item active default false
32009 * @cfg {String} html
32010 * @cfg {String} position (top|bottom) text position default bottom
32011 * @cfg {String} icon show icon instead of number
32014 * Create a new NavProgressItem
32015 * @param {Object} config The config object
32017 Roo.bootstrap.NavProgressItem = function(config){
32018 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32023 * The raw click event for the entire grid.
32024 * @param {Roo.bootstrap.NavProgressItem} this
32025 * @param {Roo.EventObject} e
32032 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32038 position : 'bottom',
32041 getAutoCreate : function()
32043 var iconCls = 'roo-navigation-bar-item-icon';
32045 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32049 cls: 'roo-navigation-bar-item',
32059 cfg.cls += ' active';
32062 cfg.cls += ' disabled';
32068 disable : function()
32070 this.setDisabled(true);
32073 enable : function()
32075 this.setDisabled(false);
32078 initEvents: function()
32080 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32082 this.iconEl.on('click', this.onClick, this);
32085 onClick : function(e)
32087 e.preventDefault();
32093 if(this.fireEvent('click', this, e) === false){
32097 this.parent().setActiveItem(this);
32100 isActive: function ()
32102 return this.active;
32105 setActive : function(state)
32107 if(this.active == state){
32111 this.active = state;
32114 this.el.addClass('active');
32118 this.el.removeClass('active');
32123 setDisabled : function(state)
32125 if(this.disabled == state){
32129 this.disabled = state;
32132 this.el.addClass('disabled');
32136 this.el.removeClass('disabled');
32139 tooltipEl : function()
32141 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32154 * @class Roo.bootstrap.FieldLabel
32155 * @extends Roo.bootstrap.Component
32156 * Bootstrap FieldLabel class
32157 * @cfg {String} html contents of the element
32158 * @cfg {String} tag tag of the element default label
32159 * @cfg {String} cls class of the element
32160 * @cfg {String} target label target
32161 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32162 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32163 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32164 * @cfg {String} iconTooltip default "This field is required"
32165 * @cfg {String} indicatorpos (left|right) default left
32168 * Create a new FieldLabel
32169 * @param {Object} config The config object
32172 Roo.bootstrap.FieldLabel = function(config){
32173 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32178 * Fires after the field has been marked as invalid.
32179 * @param {Roo.form.FieldLabel} this
32180 * @param {String} msg The validation message
32185 * Fires after the field has been validated with no errors.
32186 * @param {Roo.form.FieldLabel} this
32192 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32199 invalidClass : 'has-warning',
32200 validClass : 'has-success',
32201 iconTooltip : 'This field is required',
32202 indicatorpos : 'left',
32204 getAutoCreate : function(){
32207 if (!this.allowBlank) {
32213 cls : 'roo-bootstrap-field-label ' + this.cls,
32218 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32219 tooltip : this.iconTooltip
32228 if(this.indicatorpos == 'right'){
32231 cls : 'roo-bootstrap-field-label ' + this.cls,
32240 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32241 tooltip : this.iconTooltip
32250 initEvents: function()
32252 Roo.bootstrap.Element.superclass.initEvents.call(this);
32254 this.indicator = this.indicatorEl();
32256 if(this.indicator){
32257 this.indicator.removeClass('visible');
32258 this.indicator.addClass('invisible');
32261 Roo.bootstrap.FieldLabel.register(this);
32264 indicatorEl : function()
32266 var indicator = this.el.select('i.roo-required-indicator',true).first();
32277 * Mark this field as valid
32279 markValid : function()
32281 if(this.indicator){
32282 this.indicator.removeClass('visible');
32283 this.indicator.addClass('invisible');
32285 if (Roo.bootstrap.version == 3) {
32286 this.el.removeClass(this.invalidClass);
32287 this.el.addClass(this.validClass);
32289 this.el.removeClass('is-invalid');
32290 this.el.addClass('is-valid');
32294 this.fireEvent('valid', this);
32298 * Mark this field as invalid
32299 * @param {String} msg The validation message
32301 markInvalid : function(msg)
32303 if(this.indicator){
32304 this.indicator.removeClass('invisible');
32305 this.indicator.addClass('visible');
32307 if (Roo.bootstrap.version == 3) {
32308 this.el.removeClass(this.validClass);
32309 this.el.addClass(this.invalidClass);
32311 this.el.removeClass('is-valid');
32312 this.el.addClass('is-invalid');
32316 this.fireEvent('invalid', this, msg);
32322 Roo.apply(Roo.bootstrap.FieldLabel, {
32327 * register a FieldLabel Group
32328 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32330 register : function(label)
32332 if(this.groups.hasOwnProperty(label.target)){
32336 this.groups[label.target] = label;
32340 * fetch a FieldLabel Group based on the target
32341 * @param {string} target
32342 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32344 get: function(target) {
32345 if (typeof(this.groups[target]) == 'undefined') {
32349 return this.groups[target] ;
32358 * page DateSplitField.
32364 * @class Roo.bootstrap.DateSplitField
32365 * @extends Roo.bootstrap.Component
32366 * Bootstrap DateSplitField class
32367 * @cfg {string} fieldLabel - the label associated
32368 * @cfg {Number} labelWidth set the width of label (0-12)
32369 * @cfg {String} labelAlign (top|left)
32370 * @cfg {Boolean} dayAllowBlank (true|false) default false
32371 * @cfg {Boolean} monthAllowBlank (true|false) default false
32372 * @cfg {Boolean} yearAllowBlank (true|false) default false
32373 * @cfg {string} dayPlaceholder
32374 * @cfg {string} monthPlaceholder
32375 * @cfg {string} yearPlaceholder
32376 * @cfg {string} dayFormat default 'd'
32377 * @cfg {string} monthFormat default 'm'
32378 * @cfg {string} yearFormat default 'Y'
32379 * @cfg {Number} labellg set the width of label (1-12)
32380 * @cfg {Number} labelmd set the width of label (1-12)
32381 * @cfg {Number} labelsm set the width of label (1-12)
32382 * @cfg {Number} labelxs set the width of label (1-12)
32386 * Create a new DateSplitField
32387 * @param {Object} config The config object
32390 Roo.bootstrap.DateSplitField = function(config){
32391 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32397 * getting the data of years
32398 * @param {Roo.bootstrap.DateSplitField} this
32399 * @param {Object} years
32404 * getting the data of days
32405 * @param {Roo.bootstrap.DateSplitField} this
32406 * @param {Object} days
32411 * Fires after the field has been marked as invalid.
32412 * @param {Roo.form.Field} this
32413 * @param {String} msg The validation message
32418 * Fires after the field has been validated with no errors.
32419 * @param {Roo.form.Field} this
32425 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32428 labelAlign : 'top',
32430 dayAllowBlank : false,
32431 monthAllowBlank : false,
32432 yearAllowBlank : false,
32433 dayPlaceholder : '',
32434 monthPlaceholder : '',
32435 yearPlaceholder : '',
32439 isFormField : true,
32445 getAutoCreate : function()
32449 cls : 'row roo-date-split-field-group',
32454 cls : 'form-hidden-field roo-date-split-field-group-value',
32460 var labelCls = 'col-md-12';
32461 var contentCls = 'col-md-4';
32463 if(this.fieldLabel){
32467 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32471 html : this.fieldLabel
32476 if(this.labelAlign == 'left'){
32478 if(this.labelWidth > 12){
32479 label.style = "width: " + this.labelWidth + 'px';
32482 if(this.labelWidth < 13 && this.labelmd == 0){
32483 this.labelmd = this.labelWidth;
32486 if(this.labellg > 0){
32487 labelCls = ' col-lg-' + this.labellg;
32488 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32491 if(this.labelmd > 0){
32492 labelCls = ' col-md-' + this.labelmd;
32493 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32496 if(this.labelsm > 0){
32497 labelCls = ' col-sm-' + this.labelsm;
32498 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32501 if(this.labelxs > 0){
32502 labelCls = ' col-xs-' + this.labelxs;
32503 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32507 label.cls += ' ' + labelCls;
32509 cfg.cn.push(label);
32512 Roo.each(['day', 'month', 'year'], function(t){
32515 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32522 inputEl: function ()
32524 return this.el.select('.roo-date-split-field-group-value', true).first();
32527 onRender : function(ct, position)
32531 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32533 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32535 this.dayField = new Roo.bootstrap.ComboBox({
32536 allowBlank : this.dayAllowBlank,
32537 alwaysQuery : true,
32538 displayField : 'value',
32541 forceSelection : true,
32543 placeholder : this.dayPlaceholder,
32544 selectOnFocus : true,
32545 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32546 triggerAction : 'all',
32548 valueField : 'value',
32549 store : new Roo.data.SimpleStore({
32550 data : (function() {
32552 _this.fireEvent('days', _this, days);
32555 fields : [ 'value' ]
32558 select : function (_self, record, index)
32560 _this.setValue(_this.getValue());
32565 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32567 this.monthField = new Roo.bootstrap.MonthField({
32568 after : '<i class=\"fa fa-calendar\"></i>',
32569 allowBlank : this.monthAllowBlank,
32570 placeholder : this.monthPlaceholder,
32573 render : function (_self)
32575 this.el.select('span.input-group-addon', true).first().on('click', function(e){
32576 e.preventDefault();
32580 select : function (_self, oldvalue, newvalue)
32582 _this.setValue(_this.getValue());
32587 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32589 this.yearField = new Roo.bootstrap.ComboBox({
32590 allowBlank : this.yearAllowBlank,
32591 alwaysQuery : true,
32592 displayField : 'value',
32595 forceSelection : true,
32597 placeholder : this.yearPlaceholder,
32598 selectOnFocus : true,
32599 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32600 triggerAction : 'all',
32602 valueField : 'value',
32603 store : new Roo.data.SimpleStore({
32604 data : (function() {
32606 _this.fireEvent('years', _this, years);
32609 fields : [ 'value' ]
32612 select : function (_self, record, index)
32614 _this.setValue(_this.getValue());
32619 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32622 setValue : function(v, format)
32624 this.inputEl.dom.value = v;
32626 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32628 var d = Date.parseDate(v, f);
32635 this.setDay(d.format(this.dayFormat));
32636 this.setMonth(d.format(this.monthFormat));
32637 this.setYear(d.format(this.yearFormat));
32644 setDay : function(v)
32646 this.dayField.setValue(v);
32647 this.inputEl.dom.value = this.getValue();
32652 setMonth : function(v)
32654 this.monthField.setValue(v, true);
32655 this.inputEl.dom.value = this.getValue();
32660 setYear : function(v)
32662 this.yearField.setValue(v);
32663 this.inputEl.dom.value = this.getValue();
32668 getDay : function()
32670 return this.dayField.getValue();
32673 getMonth : function()
32675 return this.monthField.getValue();
32678 getYear : function()
32680 return this.yearField.getValue();
32683 getValue : function()
32685 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32687 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32697 this.inputEl.dom.value = '';
32702 validate : function()
32704 var d = this.dayField.validate();
32705 var m = this.monthField.validate();
32706 var y = this.yearField.validate();
32711 (!this.dayAllowBlank && !d) ||
32712 (!this.monthAllowBlank && !m) ||
32713 (!this.yearAllowBlank && !y)
32718 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32727 this.markInvalid();
32732 markValid : function()
32735 var label = this.el.select('label', true).first();
32736 var icon = this.el.select('i.fa-star', true).first();
32742 this.fireEvent('valid', this);
32746 * Mark this field as invalid
32747 * @param {String} msg The validation message
32749 markInvalid : function(msg)
32752 var label = this.el.select('label', true).first();
32753 var icon = this.el.select('i.fa-star', true).first();
32755 if(label && !icon){
32756 this.el.select('.roo-date-split-field-label', true).createChild({
32758 cls : 'text-danger fa fa-lg fa-star',
32759 tooltip : 'This field is required',
32760 style : 'margin-right:5px;'
32764 this.fireEvent('invalid', this, msg);
32767 clearInvalid : function()
32769 var label = this.el.select('label', true).first();
32770 var icon = this.el.select('i.fa-star', true).first();
32776 this.fireEvent('valid', this);
32779 getName: function()
32789 * http://masonry.desandro.com
32791 * The idea is to render all the bricks based on vertical width...
32793 * The original code extends 'outlayer' - we might need to use that....
32799 * @class Roo.bootstrap.LayoutMasonry
32800 * @extends Roo.bootstrap.Component
32801 * Bootstrap Layout Masonry class
32804 * Create a new Element
32805 * @param {Object} config The config object
32808 Roo.bootstrap.LayoutMasonry = function(config){
32810 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32814 Roo.bootstrap.LayoutMasonry.register(this);
32820 * Fire after layout the items
32821 * @param {Roo.bootstrap.LayoutMasonry} this
32822 * @param {Roo.EventObject} e
32829 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
32832 * @cfg {Boolean} isLayoutInstant = no animation?
32834 isLayoutInstant : false, // needed?
32837 * @cfg {Number} boxWidth width of the columns
32842 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
32847 * @cfg {Number} padWidth padding below box..
32852 * @cfg {Number} gutter gutter width..
32857 * @cfg {Number} maxCols maximum number of columns
32863 * @cfg {Boolean} isAutoInitial defalut true
32865 isAutoInitial : true,
32870 * @cfg {Boolean} isHorizontal defalut false
32872 isHorizontal : false,
32874 currentSize : null,
32880 bricks: null, //CompositeElement
32884 _isLayoutInited : false,
32886 // isAlternative : false, // only use for vertical layout...
32889 * @cfg {Number} alternativePadWidth padding below box..
32891 alternativePadWidth : 50,
32893 selectedBrick : [],
32895 getAutoCreate : function(){
32897 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32901 cls: 'blog-masonary-wrapper ' + this.cls,
32903 cls : 'mas-boxes masonary'
32910 getChildContainer: function( )
32912 if (this.boxesEl) {
32913 return this.boxesEl;
32916 this.boxesEl = this.el.select('.mas-boxes').first();
32918 return this.boxesEl;
32922 initEvents : function()
32926 if(this.isAutoInitial){
32927 Roo.log('hook children rendered');
32928 this.on('childrenrendered', function() {
32929 Roo.log('children rendered');
32935 initial : function()
32937 this.selectedBrick = [];
32939 this.currentSize = this.el.getBox(true);
32941 Roo.EventManager.onWindowResize(this.resize, this);
32943 if(!this.isAutoInitial){
32951 //this.layout.defer(500,this);
32955 resize : function()
32957 var cs = this.el.getBox(true);
32960 this.currentSize.width == cs.width &&
32961 this.currentSize.x == cs.x &&
32962 this.currentSize.height == cs.height &&
32963 this.currentSize.y == cs.y
32965 Roo.log("no change in with or X or Y");
32969 this.currentSize = cs;
32975 layout : function()
32977 this._resetLayout();
32979 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32981 this.layoutItems( isInstant );
32983 this._isLayoutInited = true;
32985 this.fireEvent('layout', this);
32989 _resetLayout : function()
32991 if(this.isHorizontal){
32992 this.horizontalMeasureColumns();
32996 this.verticalMeasureColumns();
33000 verticalMeasureColumns : function()
33002 this.getContainerWidth();
33004 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33005 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33009 var boxWidth = this.boxWidth + this.padWidth;
33011 if(this.containerWidth < this.boxWidth){
33012 boxWidth = this.containerWidth
33015 var containerWidth = this.containerWidth;
33017 var cols = Math.floor(containerWidth / boxWidth);
33019 this.cols = Math.max( cols, 1 );
33021 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33023 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33025 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33027 this.colWidth = boxWidth + avail - this.padWidth;
33029 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33030 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33033 horizontalMeasureColumns : function()
33035 this.getContainerWidth();
33037 var boxWidth = this.boxWidth;
33039 if(this.containerWidth < boxWidth){
33040 boxWidth = this.containerWidth;
33043 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33045 this.el.setHeight(boxWidth);
33049 getContainerWidth : function()
33051 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33054 layoutItems : function( isInstant )
33056 Roo.log(this.bricks);
33058 var items = Roo.apply([], this.bricks);
33060 if(this.isHorizontal){
33061 this._horizontalLayoutItems( items , isInstant );
33065 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33066 // this._verticalAlternativeLayoutItems( items , isInstant );
33070 this._verticalLayoutItems( items , isInstant );
33074 _verticalLayoutItems : function ( items , isInstant)
33076 if ( !items || !items.length ) {
33081 ['xs', 'xs', 'xs', 'tall'],
33082 ['xs', 'xs', 'tall'],
33083 ['xs', 'xs', 'sm'],
33084 ['xs', 'xs', 'xs'],
33090 ['sm', 'xs', 'xs'],
33094 ['tall', 'xs', 'xs', 'xs'],
33095 ['tall', 'xs', 'xs'],
33107 Roo.each(items, function(item, k){
33109 switch (item.size) {
33110 // these layouts take up a full box,
33121 boxes.push([item]);
33144 var filterPattern = function(box, length)
33152 var pattern = box.slice(0, length);
33156 Roo.each(pattern, function(i){
33157 format.push(i.size);
33160 Roo.each(standard, function(s){
33162 if(String(s) != String(format)){
33171 if(!match && length == 1){
33176 filterPattern(box, length - 1);
33180 queue.push(pattern);
33182 box = box.slice(length, box.length);
33184 filterPattern(box, 4);
33190 Roo.each(boxes, function(box, k){
33196 if(box.length == 1){
33201 filterPattern(box, 4);
33205 this._processVerticalLayoutQueue( queue, isInstant );
33209 // _verticalAlternativeLayoutItems : function( items , isInstant )
33211 // if ( !items || !items.length ) {
33215 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33219 _horizontalLayoutItems : function ( items , isInstant)
33221 if ( !items || !items.length || items.length < 3) {
33227 var eItems = items.slice(0, 3);
33229 items = items.slice(3, items.length);
33232 ['xs', 'xs', 'xs', 'wide'],
33233 ['xs', 'xs', 'wide'],
33234 ['xs', 'xs', 'sm'],
33235 ['xs', 'xs', 'xs'],
33241 ['sm', 'xs', 'xs'],
33245 ['wide', 'xs', 'xs', 'xs'],
33246 ['wide', 'xs', 'xs'],
33259 Roo.each(items, function(item, k){
33261 switch (item.size) {
33272 boxes.push([item]);
33296 var filterPattern = function(box, length)
33304 var pattern = box.slice(0, length);
33308 Roo.each(pattern, function(i){
33309 format.push(i.size);
33312 Roo.each(standard, function(s){
33314 if(String(s) != String(format)){
33323 if(!match && length == 1){
33328 filterPattern(box, length - 1);
33332 queue.push(pattern);
33334 box = box.slice(length, box.length);
33336 filterPattern(box, 4);
33342 Roo.each(boxes, function(box, k){
33348 if(box.length == 1){
33353 filterPattern(box, 4);
33360 var pos = this.el.getBox(true);
33364 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33366 var hit_end = false;
33368 Roo.each(queue, function(box){
33372 Roo.each(box, function(b){
33374 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33384 Roo.each(box, function(b){
33386 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33389 mx = Math.max(mx, b.x);
33393 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33397 Roo.each(box, function(b){
33399 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33413 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33416 /** Sets position of item in DOM
33417 * @param {Element} item
33418 * @param {Number} x - horizontal position
33419 * @param {Number} y - vertical position
33420 * @param {Boolean} isInstant - disables transitions
33422 _processVerticalLayoutQueue : function( queue, isInstant )
33424 var pos = this.el.getBox(true);
33429 for (var i = 0; i < this.cols; i++){
33433 Roo.each(queue, function(box, k){
33435 var col = k % this.cols;
33437 Roo.each(box, function(b,kk){
33439 b.el.position('absolute');
33441 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33442 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33444 if(b.size == 'md-left' || b.size == 'md-right'){
33445 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33446 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33449 b.el.setWidth(width);
33450 b.el.setHeight(height);
33452 b.el.select('iframe',true).setSize(width,height);
33456 for (var i = 0; i < this.cols; i++){
33458 if(maxY[i] < maxY[col]){
33463 col = Math.min(col, i);
33467 x = pos.x + col * (this.colWidth + this.padWidth);
33471 var positions = [];
33473 switch (box.length){
33475 positions = this.getVerticalOneBoxColPositions(x, y, box);
33478 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33481 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33484 positions = this.getVerticalFourBoxColPositions(x, y, box);
33490 Roo.each(box, function(b,kk){
33492 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33494 var sz = b.el.getSize();
33496 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33504 for (var i = 0; i < this.cols; i++){
33505 mY = Math.max(mY, maxY[i]);
33508 this.el.setHeight(mY - pos.y);
33512 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33514 // var pos = this.el.getBox(true);
33517 // var maxX = pos.right;
33519 // var maxHeight = 0;
33521 // Roo.each(items, function(item, k){
33525 // item.el.position('absolute');
33527 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33529 // item.el.setWidth(width);
33531 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33533 // item.el.setHeight(height);
33536 // item.el.setXY([x, y], isInstant ? false : true);
33538 // item.el.setXY([maxX - width, y], isInstant ? false : true);
33541 // y = y + height + this.alternativePadWidth;
33543 // maxHeight = maxHeight + height + this.alternativePadWidth;
33547 // this.el.setHeight(maxHeight);
33551 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33553 var pos = this.el.getBox(true);
33558 var maxX = pos.right;
33560 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33562 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33564 Roo.each(queue, function(box, k){
33566 Roo.each(box, function(b, kk){
33568 b.el.position('absolute');
33570 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33571 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33573 if(b.size == 'md-left' || b.size == 'md-right'){
33574 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33575 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33578 b.el.setWidth(width);
33579 b.el.setHeight(height);
33587 var positions = [];
33589 switch (box.length){
33591 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33594 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33597 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33600 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33606 Roo.each(box, function(b,kk){
33608 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33610 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33618 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33620 Roo.each(eItems, function(b,k){
33622 b.size = (k == 0) ? 'sm' : 'xs';
33623 b.x = (k == 0) ? 2 : 1;
33624 b.y = (k == 0) ? 2 : 1;
33626 b.el.position('absolute');
33628 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33630 b.el.setWidth(width);
33632 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33634 b.el.setHeight(height);
33638 var positions = [];
33641 x : maxX - this.unitWidth * 2 - this.gutter,
33646 x : maxX - this.unitWidth,
33647 y : minY + (this.unitWidth + this.gutter) * 2
33651 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33655 Roo.each(eItems, function(b,k){
33657 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33663 getVerticalOneBoxColPositions : function(x, y, box)
33667 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33669 if(box[0].size == 'md-left'){
33673 if(box[0].size == 'md-right'){
33678 x : x + (this.unitWidth + this.gutter) * rand,
33685 getVerticalTwoBoxColPositions : function(x, y, box)
33689 if(box[0].size == 'xs'){
33693 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33697 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33711 x : x + (this.unitWidth + this.gutter) * 2,
33712 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33719 getVerticalThreeBoxColPositions : function(x, y, box)
33723 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33731 x : x + (this.unitWidth + this.gutter) * 1,
33736 x : x + (this.unitWidth + this.gutter) * 2,
33744 if(box[0].size == 'xs' && box[1].size == 'xs'){
33753 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33757 x : x + (this.unitWidth + this.gutter) * 1,
33771 x : x + (this.unitWidth + this.gutter) * 2,
33776 x : x + (this.unitWidth + this.gutter) * 2,
33777 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33784 getVerticalFourBoxColPositions : function(x, y, box)
33788 if(box[0].size == 'xs'){
33797 y : y + (this.unitHeight + this.gutter) * 1
33802 y : y + (this.unitHeight + this.gutter) * 2
33806 x : x + (this.unitWidth + this.gutter) * 1,
33820 x : x + (this.unitWidth + this.gutter) * 2,
33825 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33826 y : y + (this.unitHeight + this.gutter) * 1
33830 x : x + (this.unitWidth + this.gutter) * 2,
33831 y : y + (this.unitWidth + this.gutter) * 2
33838 getHorizontalOneBoxColPositions : function(maxX, minY, box)
33842 if(box[0].size == 'md-left'){
33844 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33851 if(box[0].size == 'md-right'){
33853 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33854 y : minY + (this.unitWidth + this.gutter) * 1
33860 var rand = Math.floor(Math.random() * (4 - box[0].y));
33863 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33864 y : minY + (this.unitWidth + this.gutter) * rand
33871 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33875 if(box[0].size == 'xs'){
33878 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33883 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33884 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33892 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33897 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33898 y : minY + (this.unitWidth + this.gutter) * 2
33905 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33909 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
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) * 1
33922 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33923 y : minY + (this.unitWidth + this.gutter) * 2
33930 if(box[0].size == 'xs' && box[1].size == 'xs'){
33933 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33938 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33943 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33944 y : minY + (this.unitWidth + this.gutter) * 1
33952 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33957 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33958 y : minY + (this.unitWidth + this.gutter) * 2
33962 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33963 y : minY + (this.unitWidth + this.gutter) * 2
33970 getHorizontalFourBoxColPositions : function(maxX, minY, box)
33974 if(box[0].size == 'xs'){
33977 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33982 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33987 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),
33992 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33993 y : minY + (this.unitWidth + this.gutter) * 1
34001 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34006 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34007 y : minY + (this.unitWidth + this.gutter) * 2
34011 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34012 y : minY + (this.unitWidth + this.gutter) * 2
34016 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),
34017 y : minY + (this.unitWidth + this.gutter) * 2
34025 * remove a Masonry Brick
34026 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34028 removeBrick : function(brick_id)
34034 for (var i = 0; i<this.bricks.length; i++) {
34035 if (this.bricks[i].id == brick_id) {
34036 this.bricks.splice(i,1);
34037 this.el.dom.removeChild(Roo.get(brick_id).dom);
34044 * adds a Masonry Brick
34045 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34047 addBrick : function(cfg)
34049 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34050 //this.register(cn);
34051 cn.parentId = this.id;
34052 cn.render(this.el);
34057 * register a Masonry Brick
34058 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34061 register : function(brick)
34063 this.bricks.push(brick);
34064 brick.masonryId = this.id;
34068 * clear all the Masonry Brick
34070 clearAll : function()
34073 //this.getChildContainer().dom.innerHTML = "";
34074 this.el.dom.innerHTML = '';
34077 getSelected : function()
34079 if (!this.selectedBrick) {
34083 return this.selectedBrick;
34087 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34091 * register a Masonry Layout
34092 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34095 register : function(layout)
34097 this.groups[layout.id] = layout;
34100 * fetch a Masonry Layout based on the masonry layout ID
34101 * @param {string} the masonry layout to add
34102 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34105 get: function(layout_id) {
34106 if (typeof(this.groups[layout_id]) == 'undefined') {
34109 return this.groups[layout_id] ;
34121 * http://masonry.desandro.com
34123 * The idea is to render all the bricks based on vertical width...
34125 * The original code extends 'outlayer' - we might need to use that....
34131 * @class Roo.bootstrap.LayoutMasonryAuto
34132 * @extends Roo.bootstrap.Component
34133 * Bootstrap Layout Masonry class
34136 * Create a new Element
34137 * @param {Object} config The config object
34140 Roo.bootstrap.LayoutMasonryAuto = function(config){
34141 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34144 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34147 * @cfg {Boolean} isFitWidth - resize the width..
34149 isFitWidth : false, // options..
34151 * @cfg {Boolean} isOriginLeft = left align?
34153 isOriginLeft : true,
34155 * @cfg {Boolean} isOriginTop = top align?
34157 isOriginTop : false,
34159 * @cfg {Boolean} isLayoutInstant = no animation?
34161 isLayoutInstant : false, // needed?
34163 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34165 isResizingContainer : true,
34167 * @cfg {Number} columnWidth width of the columns
34173 * @cfg {Number} maxCols maximum number of columns
34178 * @cfg {Number} padHeight padding below box..
34184 * @cfg {Boolean} isAutoInitial defalut true
34187 isAutoInitial : true,
34193 initialColumnWidth : 0,
34194 currentSize : null,
34196 colYs : null, // array.
34203 bricks: null, //CompositeElement
34204 cols : 0, // array?
34205 // element : null, // wrapped now this.el
34206 _isLayoutInited : null,
34209 getAutoCreate : function(){
34213 cls: 'blog-masonary-wrapper ' + this.cls,
34215 cls : 'mas-boxes masonary'
34222 getChildContainer: function( )
34224 if (this.boxesEl) {
34225 return this.boxesEl;
34228 this.boxesEl = this.el.select('.mas-boxes').first();
34230 return this.boxesEl;
34234 initEvents : function()
34238 if(this.isAutoInitial){
34239 Roo.log('hook children rendered');
34240 this.on('childrenrendered', function() {
34241 Roo.log('children rendered');
34248 initial : function()
34250 this.reloadItems();
34252 this.currentSize = this.el.getBox(true);
34254 /// was window resize... - let's see if this works..
34255 Roo.EventManager.onWindowResize(this.resize, this);
34257 if(!this.isAutoInitial){
34262 this.layout.defer(500,this);
34265 reloadItems: function()
34267 this.bricks = this.el.select('.masonry-brick', true);
34269 this.bricks.each(function(b) {
34270 //Roo.log(b.getSize());
34271 if (!b.attr('originalwidth')) {
34272 b.attr('originalwidth', b.getSize().width);
34277 Roo.log(this.bricks.elements.length);
34280 resize : function()
34283 var cs = this.el.getBox(true);
34285 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34286 Roo.log("no change in with or X");
34289 this.currentSize = cs;
34293 layout : function()
34296 this._resetLayout();
34297 //this._manageStamps();
34299 // don't animate first layout
34300 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34301 this.layoutItems( isInstant );
34303 // flag for initalized
34304 this._isLayoutInited = true;
34307 layoutItems : function( isInstant )
34309 //var items = this._getItemsForLayout( this.items );
34310 // original code supports filtering layout items.. we just ignore it..
34312 this._layoutItems( this.bricks , isInstant );
34314 this._postLayout();
34316 _layoutItems : function ( items , isInstant)
34318 //this.fireEvent( 'layout', this, items );
34321 if ( !items || !items.elements.length ) {
34322 // no items, emit event with empty array
34327 items.each(function(item) {
34328 Roo.log("layout item");
34330 // get x/y object from method
34331 var position = this._getItemLayoutPosition( item );
34333 position.item = item;
34334 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34335 queue.push( position );
34338 this._processLayoutQueue( queue );
34340 /** Sets position of item in DOM
34341 * @param {Element} item
34342 * @param {Number} x - horizontal position
34343 * @param {Number} y - vertical position
34344 * @param {Boolean} isInstant - disables transitions
34346 _processLayoutQueue : function( queue )
34348 for ( var i=0, len = queue.length; i < len; i++ ) {
34349 var obj = queue[i];
34350 obj.item.position('absolute');
34351 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34357 * Any logic you want to do after each layout,
34358 * i.e. size the container
34360 _postLayout : function()
34362 this.resizeContainer();
34365 resizeContainer : function()
34367 if ( !this.isResizingContainer ) {
34370 var size = this._getContainerSize();
34372 this.el.setSize(size.width,size.height);
34373 this.boxesEl.setSize(size.width,size.height);
34379 _resetLayout : function()
34381 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34382 this.colWidth = this.el.getWidth();
34383 //this.gutter = this.el.getWidth();
34385 this.measureColumns();
34391 this.colYs.push( 0 );
34397 measureColumns : function()
34399 this.getContainerWidth();
34400 // if columnWidth is 0, default to outerWidth of first item
34401 if ( !this.columnWidth ) {
34402 var firstItem = this.bricks.first();
34403 Roo.log(firstItem);
34404 this.columnWidth = this.containerWidth;
34405 if (firstItem && firstItem.attr('originalwidth') ) {
34406 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34408 // columnWidth fall back to item of first element
34409 Roo.log("set column width?");
34410 this.initialColumnWidth = this.columnWidth ;
34412 // if first elem has no width, default to size of container
34417 if (this.initialColumnWidth) {
34418 this.columnWidth = this.initialColumnWidth;
34423 // column width is fixed at the top - however if container width get's smaller we should
34426 // this bit calcs how man columns..
34428 var columnWidth = this.columnWidth += this.gutter;
34430 // calculate columns
34431 var containerWidth = this.containerWidth + this.gutter;
34433 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34434 // fix rounding errors, typically with gutters
34435 var excess = columnWidth - containerWidth % columnWidth;
34438 // if overshoot is less than a pixel, round up, otherwise floor it
34439 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34440 cols = Math[ mathMethod ]( cols );
34441 this.cols = Math.max( cols, 1 );
34442 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34444 // padding positioning..
34445 var totalColWidth = this.cols * this.columnWidth;
34446 var padavail = this.containerWidth - totalColWidth;
34447 // so for 2 columns - we need 3 'pads'
34449 var padNeeded = (1+this.cols) * this.padWidth;
34451 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34453 this.columnWidth += padExtra
34454 //this.padWidth = Math.floor(padavail / ( this.cols));
34456 // adjust colum width so that padding is fixed??
34458 // we have 3 columns ... total = width * 3
34459 // we have X left over... that should be used by
34461 //if (this.expandC) {
34469 getContainerWidth : function()
34471 /* // container is parent if fit width
34472 var container = this.isFitWidth ? this.element.parentNode : this.element;
34473 // check that this.size and size are there
34474 // IE8 triggers resize on body size change, so they might not be
34476 var size = getSize( container ); //FIXME
34477 this.containerWidth = size && size.innerWidth; //FIXME
34480 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34484 _getItemLayoutPosition : function( item ) // what is item?
34486 // we resize the item to our columnWidth..
34488 item.setWidth(this.columnWidth);
34489 item.autoBoxAdjust = false;
34491 var sz = item.getSize();
34493 // how many columns does this brick span
34494 var remainder = this.containerWidth % this.columnWidth;
34496 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34497 // round if off by 1 pixel, otherwise use ceil
34498 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34499 colSpan = Math.min( colSpan, this.cols );
34501 // normally this should be '1' as we dont' currently allow multi width columns..
34503 var colGroup = this._getColGroup( colSpan );
34504 // get the minimum Y value from the columns
34505 var minimumY = Math.min.apply( Math, colGroup );
34506 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34508 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
34510 // position the brick
34512 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34513 y: this.currentSize.y + minimumY + this.padHeight
34517 // apply setHeight to necessary columns
34518 var setHeight = minimumY + sz.height + this.padHeight;
34519 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34521 var setSpan = this.cols + 1 - colGroup.length;
34522 for ( var i = 0; i < setSpan; i++ ) {
34523 this.colYs[ shortColIndex + i ] = setHeight ;
34530 * @param {Number} colSpan - number of columns the element spans
34531 * @returns {Array} colGroup
34533 _getColGroup : function( colSpan )
34535 if ( colSpan < 2 ) {
34536 // if brick spans only one column, use all the column Ys
34541 // how many different places could this brick fit horizontally
34542 var groupCount = this.cols + 1 - colSpan;
34543 // for each group potential horizontal position
34544 for ( var i = 0; i < groupCount; i++ ) {
34545 // make an array of colY values for that one group
34546 var groupColYs = this.colYs.slice( i, i + colSpan );
34547 // and get the max value of the array
34548 colGroup[i] = Math.max.apply( Math, groupColYs );
34553 _manageStamp : function( stamp )
34555 var stampSize = stamp.getSize();
34556 var offset = stamp.getBox();
34557 // get the columns that this stamp affects
34558 var firstX = this.isOriginLeft ? offset.x : offset.right;
34559 var lastX = firstX + stampSize.width;
34560 var firstCol = Math.floor( firstX / this.columnWidth );
34561 firstCol = Math.max( 0, firstCol );
34563 var lastCol = Math.floor( lastX / this.columnWidth );
34564 // lastCol should not go over if multiple of columnWidth #425
34565 lastCol -= lastX % this.columnWidth ? 0 : 1;
34566 lastCol = Math.min( this.cols - 1, lastCol );
34568 // set colYs to bottom of the stamp
34569 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34572 for ( var i = firstCol; i <= lastCol; i++ ) {
34573 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34578 _getContainerSize : function()
34580 this.maxY = Math.max.apply( Math, this.colYs );
34585 if ( this.isFitWidth ) {
34586 size.width = this._getContainerFitWidth();
34592 _getContainerFitWidth : function()
34594 var unusedCols = 0;
34595 // count unused columns
34598 if ( this.colYs[i] !== 0 ) {
34603 // fit container to columns that have been used
34604 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34607 needsResizeLayout : function()
34609 var previousWidth = this.containerWidth;
34610 this.getContainerWidth();
34611 return previousWidth !== this.containerWidth;
34626 * @class Roo.bootstrap.MasonryBrick
34627 * @extends Roo.bootstrap.Component
34628 * Bootstrap MasonryBrick class
34631 * Create a new MasonryBrick
34632 * @param {Object} config The config object
34635 Roo.bootstrap.MasonryBrick = function(config){
34637 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34639 Roo.bootstrap.MasonryBrick.register(this);
34645 * When a MasonryBrick is clcik
34646 * @param {Roo.bootstrap.MasonryBrick} this
34647 * @param {Roo.EventObject} e
34653 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34656 * @cfg {String} title
34660 * @cfg {String} html
34664 * @cfg {String} bgimage
34668 * @cfg {String} videourl
34672 * @cfg {String} cls
34676 * @cfg {String} href
34680 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34685 * @cfg {String} placetitle (center|bottom)
34690 * @cfg {Boolean} isFitContainer defalut true
34692 isFitContainer : true,
34695 * @cfg {Boolean} preventDefault defalut false
34697 preventDefault : false,
34700 * @cfg {Boolean} inverse defalut false
34702 maskInverse : false,
34704 getAutoCreate : function()
34706 if(!this.isFitContainer){
34707 return this.getSplitAutoCreate();
34710 var cls = 'masonry-brick masonry-brick-full';
34712 if(this.href.length){
34713 cls += ' masonry-brick-link';
34716 if(this.bgimage.length){
34717 cls += ' masonry-brick-image';
34720 if(this.maskInverse){
34721 cls += ' mask-inverse';
34724 if(!this.html.length && !this.maskInverse && !this.videourl.length){
34725 cls += ' enable-mask';
34729 cls += ' masonry-' + this.size + '-brick';
34732 if(this.placetitle.length){
34734 switch (this.placetitle) {
34736 cls += ' masonry-center-title';
34739 cls += ' masonry-bottom-title';
34746 if(!this.html.length && !this.bgimage.length){
34747 cls += ' masonry-center-title';
34750 if(!this.html.length && this.bgimage.length){
34751 cls += ' masonry-bottom-title';
34756 cls += ' ' + this.cls;
34760 tag: (this.href.length) ? 'a' : 'div',
34765 cls: 'masonry-brick-mask'
34769 cls: 'masonry-brick-paragraph',
34775 if(this.href.length){
34776 cfg.href = this.href;
34779 var cn = cfg.cn[1].cn;
34781 if(this.title.length){
34784 cls: 'masonry-brick-title',
34789 if(this.html.length){
34792 cls: 'masonry-brick-text',
34797 if (!this.title.length && !this.html.length) {
34798 cfg.cn[1].cls += ' hide';
34801 if(this.bgimage.length){
34804 cls: 'masonry-brick-image-view',
34809 if(this.videourl.length){
34810 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34811 // youtube support only?
34814 cls: 'masonry-brick-image-view',
34817 allowfullscreen : true
34825 getSplitAutoCreate : function()
34827 var cls = 'masonry-brick masonry-brick-split';
34829 if(this.href.length){
34830 cls += ' masonry-brick-link';
34833 if(this.bgimage.length){
34834 cls += ' masonry-brick-image';
34838 cls += ' masonry-' + this.size + '-brick';
34841 switch (this.placetitle) {
34843 cls += ' masonry-center-title';
34846 cls += ' masonry-bottom-title';
34849 if(!this.bgimage.length){
34850 cls += ' masonry-center-title';
34853 if(this.bgimage.length){
34854 cls += ' masonry-bottom-title';
34860 cls += ' ' + this.cls;
34864 tag: (this.href.length) ? 'a' : 'div',
34869 cls: 'masonry-brick-split-head',
34873 cls: 'masonry-brick-paragraph',
34880 cls: 'masonry-brick-split-body',
34886 if(this.href.length){
34887 cfg.href = this.href;
34890 if(this.title.length){
34891 cfg.cn[0].cn[0].cn.push({
34893 cls: 'masonry-brick-title',
34898 if(this.html.length){
34899 cfg.cn[1].cn.push({
34901 cls: 'masonry-brick-text',
34906 if(this.bgimage.length){
34907 cfg.cn[0].cn.push({
34909 cls: 'masonry-brick-image-view',
34914 if(this.videourl.length){
34915 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34916 // youtube support only?
34917 cfg.cn[0].cn.cn.push({
34919 cls: 'masonry-brick-image-view',
34922 allowfullscreen : true
34929 initEvents: function()
34931 switch (this.size) {
34964 this.el.on('touchstart', this.onTouchStart, this);
34965 this.el.on('touchmove', this.onTouchMove, this);
34966 this.el.on('touchend', this.onTouchEnd, this);
34967 this.el.on('contextmenu', this.onContextMenu, this);
34969 this.el.on('mouseenter' ,this.enter, this);
34970 this.el.on('mouseleave', this.leave, this);
34971 this.el.on('click', this.onClick, this);
34974 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34975 this.parent().bricks.push(this);
34980 onClick: function(e, el)
34982 var time = this.endTimer - this.startTimer;
34983 // Roo.log(e.preventDefault());
34986 e.preventDefault();
34991 if(!this.preventDefault){
34995 e.preventDefault();
34997 if (this.activeClass != '') {
34998 this.selectBrick();
35001 this.fireEvent('click', this, e);
35004 enter: function(e, el)
35006 e.preventDefault();
35008 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35012 if(this.bgimage.length && this.html.length){
35013 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35017 leave: function(e, el)
35019 e.preventDefault();
35021 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35025 if(this.bgimage.length && this.html.length){
35026 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35030 onTouchStart: function(e, el)
35032 // e.preventDefault();
35034 this.touchmoved = false;
35036 if(!this.isFitContainer){
35040 if(!this.bgimage.length || !this.html.length){
35044 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35046 this.timer = new Date().getTime();
35050 onTouchMove: function(e, el)
35052 this.touchmoved = true;
35055 onContextMenu : function(e,el)
35057 e.preventDefault();
35058 e.stopPropagation();
35062 onTouchEnd: function(e, el)
35064 // e.preventDefault();
35066 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35073 if(!this.bgimage.length || !this.html.length){
35075 if(this.href.length){
35076 window.location.href = this.href;
35082 if(!this.isFitContainer){
35086 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35088 window.location.href = this.href;
35091 //selection on single brick only
35092 selectBrick : function() {
35094 if (!this.parentId) {
35098 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35099 var index = m.selectedBrick.indexOf(this.id);
35102 m.selectedBrick.splice(index,1);
35103 this.el.removeClass(this.activeClass);
35107 for(var i = 0; i < m.selectedBrick.length; i++) {
35108 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35109 b.el.removeClass(b.activeClass);
35112 m.selectedBrick = [];
35114 m.selectedBrick.push(this.id);
35115 this.el.addClass(this.activeClass);
35119 isSelected : function(){
35120 return this.el.hasClass(this.activeClass);
35125 Roo.apply(Roo.bootstrap.MasonryBrick, {
35128 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35130 * register a Masonry Brick
35131 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35134 register : function(brick)
35136 //this.groups[brick.id] = brick;
35137 this.groups.add(brick.id, brick);
35140 * fetch a masonry brick based on the masonry brick ID
35141 * @param {string} the masonry brick to add
35142 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35145 get: function(brick_id)
35147 // if (typeof(this.groups[brick_id]) == 'undefined') {
35150 // return this.groups[brick_id] ;
35152 if(this.groups.key(brick_id)) {
35153 return this.groups.key(brick_id);
35171 * @class Roo.bootstrap.Brick
35172 * @extends Roo.bootstrap.Component
35173 * Bootstrap Brick class
35176 * Create a new Brick
35177 * @param {Object} config The config object
35180 Roo.bootstrap.Brick = function(config){
35181 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35187 * When a Brick is click
35188 * @param {Roo.bootstrap.Brick} this
35189 * @param {Roo.EventObject} e
35195 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35198 * @cfg {String} title
35202 * @cfg {String} html
35206 * @cfg {String} bgimage
35210 * @cfg {String} cls
35214 * @cfg {String} href
35218 * @cfg {String} video
35222 * @cfg {Boolean} square
35226 getAutoCreate : function()
35228 var cls = 'roo-brick';
35230 if(this.href.length){
35231 cls += ' roo-brick-link';
35234 if(this.bgimage.length){
35235 cls += ' roo-brick-image';
35238 if(!this.html.length && !this.bgimage.length){
35239 cls += ' roo-brick-center-title';
35242 if(!this.html.length && this.bgimage.length){
35243 cls += ' roo-brick-bottom-title';
35247 cls += ' ' + this.cls;
35251 tag: (this.href.length) ? 'a' : 'div',
35256 cls: 'roo-brick-paragraph',
35262 if(this.href.length){
35263 cfg.href = this.href;
35266 var cn = cfg.cn[0].cn;
35268 if(this.title.length){
35271 cls: 'roo-brick-title',
35276 if(this.html.length){
35279 cls: 'roo-brick-text',
35286 if(this.bgimage.length){
35289 cls: 'roo-brick-image-view',
35297 initEvents: function()
35299 if(this.title.length || this.html.length){
35300 this.el.on('mouseenter' ,this.enter, this);
35301 this.el.on('mouseleave', this.leave, this);
35304 Roo.EventManager.onWindowResize(this.resize, this);
35306 if(this.bgimage.length){
35307 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35308 this.imageEl.on('load', this.onImageLoad, this);
35315 onImageLoad : function()
35320 resize : function()
35322 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35324 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35326 if(this.bgimage.length){
35327 var image = this.el.select('.roo-brick-image-view', true).first();
35329 image.setWidth(paragraph.getWidth());
35332 image.setHeight(paragraph.getWidth());
35335 this.el.setHeight(image.getHeight());
35336 paragraph.setHeight(image.getHeight());
35342 enter: function(e, el)
35344 e.preventDefault();
35346 if(this.bgimage.length){
35347 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35348 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35352 leave: function(e, el)
35354 e.preventDefault();
35356 if(this.bgimage.length){
35357 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35358 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35373 * @class Roo.bootstrap.NumberField
35374 * @extends Roo.bootstrap.Input
35375 * Bootstrap NumberField class
35381 * Create a new NumberField
35382 * @param {Object} config The config object
35385 Roo.bootstrap.NumberField = function(config){
35386 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35389 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35392 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35394 allowDecimals : true,
35396 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35398 decimalSeparator : ".",
35400 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35402 decimalPrecision : 2,
35404 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35406 allowNegative : true,
35409 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35413 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35415 minValue : Number.NEGATIVE_INFINITY,
35417 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35419 maxValue : Number.MAX_VALUE,
35421 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35423 minText : "The minimum value for this field is {0}",
35425 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35427 maxText : "The maximum value for this field is {0}",
35429 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35430 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35432 nanText : "{0} is not a valid number",
35434 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35436 thousandsDelimiter : false,
35438 * @cfg {String} valueAlign alignment of value
35440 valueAlign : "left",
35442 getAutoCreate : function()
35444 var hiddenInput = {
35448 cls: 'hidden-number-input'
35452 hiddenInput.name = this.name;
35457 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35459 this.name = hiddenInput.name;
35461 if(cfg.cn.length > 0) {
35462 cfg.cn.push(hiddenInput);
35469 initEvents : function()
35471 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35473 var allowed = "0123456789";
35475 if(this.allowDecimals){
35476 allowed += this.decimalSeparator;
35479 if(this.allowNegative){
35483 if(this.thousandsDelimiter) {
35487 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35489 var keyPress = function(e){
35491 var k = e.getKey();
35493 var c = e.getCharCode();
35496 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35497 allowed.indexOf(String.fromCharCode(c)) === -1
35503 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35507 if(allowed.indexOf(String.fromCharCode(c)) === -1){
35512 this.el.on("keypress", keyPress, this);
35515 validateValue : function(value)
35518 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35522 var num = this.parseValue(value);
35525 this.markInvalid(String.format(this.nanText, value));
35529 if(num < this.minValue){
35530 this.markInvalid(String.format(this.minText, this.minValue));
35534 if(num > this.maxValue){
35535 this.markInvalid(String.format(this.maxText, this.maxValue));
35542 getValue : function()
35544 var v = this.hiddenEl().getValue();
35546 return this.fixPrecision(this.parseValue(v));
35549 parseValue : function(value)
35551 if(this.thousandsDelimiter) {
35553 r = new RegExp(",", "g");
35554 value = value.replace(r, "");
35557 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35558 return isNaN(value) ? '' : value;
35561 fixPrecision : function(value)
35563 if(this.thousandsDelimiter) {
35565 r = new RegExp(",", "g");
35566 value = value.replace(r, "");
35569 var nan = isNaN(value);
35571 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35572 return nan ? '' : value;
35574 return parseFloat(value).toFixed(this.decimalPrecision);
35577 setValue : function(v)
35579 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35585 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35587 this.inputEl().dom.value = (v == '') ? '' :
35588 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35590 if(!this.allowZero && v === '0') {
35591 this.hiddenEl().dom.value = '';
35592 this.inputEl().dom.value = '';
35599 decimalPrecisionFcn : function(v)
35601 return Math.floor(v);
35604 beforeBlur : function()
35606 var v = this.parseValue(this.getRawValue());
35608 if(v || v === 0 || v === ''){
35613 hiddenEl : function()
35615 return this.el.select('input.hidden-number-input',true).first();
35627 * @class Roo.bootstrap.DocumentSlider
35628 * @extends Roo.bootstrap.Component
35629 * Bootstrap DocumentSlider class
35632 * Create a new DocumentViewer
35633 * @param {Object} config The config object
35636 Roo.bootstrap.DocumentSlider = function(config){
35637 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35644 * Fire after initEvent
35645 * @param {Roo.bootstrap.DocumentSlider} this
35650 * Fire after update
35651 * @param {Roo.bootstrap.DocumentSlider} this
35657 * @param {Roo.bootstrap.DocumentSlider} this
35663 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35669 getAutoCreate : function()
35673 cls : 'roo-document-slider',
35677 cls : 'roo-document-slider-header',
35681 cls : 'roo-document-slider-header-title'
35687 cls : 'roo-document-slider-body',
35691 cls : 'roo-document-slider-prev',
35695 cls : 'fa fa-chevron-left'
35701 cls : 'roo-document-slider-thumb',
35705 cls : 'roo-document-slider-image'
35711 cls : 'roo-document-slider-next',
35715 cls : 'fa fa-chevron-right'
35727 initEvents : function()
35729 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35730 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35732 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35733 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35735 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35736 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35738 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35739 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35741 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35742 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35744 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35745 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35747 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35748 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35750 this.thumbEl.on('click', this.onClick, this);
35752 this.prevIndicator.on('click', this.prev, this);
35754 this.nextIndicator.on('click', this.next, this);
35758 initial : function()
35760 if(this.files.length){
35761 this.indicator = 1;
35765 this.fireEvent('initial', this);
35768 update : function()
35770 this.imageEl.attr('src', this.files[this.indicator - 1]);
35772 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35774 this.prevIndicator.show();
35776 if(this.indicator == 1){
35777 this.prevIndicator.hide();
35780 this.nextIndicator.show();
35782 if(this.indicator == this.files.length){
35783 this.nextIndicator.hide();
35786 this.thumbEl.scrollTo('top');
35788 this.fireEvent('update', this);
35791 onClick : function(e)
35793 e.preventDefault();
35795 this.fireEvent('click', this);
35800 e.preventDefault();
35802 this.indicator = Math.max(1, this.indicator - 1);
35809 e.preventDefault();
35811 this.indicator = Math.min(this.files.length, this.indicator + 1);
35825 * @class Roo.bootstrap.RadioSet
35826 * @extends Roo.bootstrap.Input
35827 * Bootstrap RadioSet class
35828 * @cfg {String} indicatorpos (left|right) default left
35829 * @cfg {Boolean} inline (true|false) inline the element (default true)
35830 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35832 * Create a new RadioSet
35833 * @param {Object} config The config object
35836 Roo.bootstrap.RadioSet = function(config){
35838 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35842 Roo.bootstrap.RadioSet.register(this);
35847 * Fires when the element is checked or unchecked.
35848 * @param {Roo.bootstrap.RadioSet} this This radio
35849 * @param {Roo.bootstrap.Radio} item The checked item
35854 * Fires when the element is click.
35855 * @param {Roo.bootstrap.RadioSet} this This radio set
35856 * @param {Roo.bootstrap.Radio} item The checked item
35857 * @param {Roo.EventObject} e The event object
35864 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
35872 indicatorpos : 'left',
35874 getAutoCreate : function()
35878 cls : 'roo-radio-set-label',
35882 html : this.fieldLabel
35886 if (Roo.bootstrap.version == 3) {
35889 if(this.indicatorpos == 'left'){
35892 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35893 tooltip : 'This field is required'
35898 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35899 tooltip : 'This field is required'
35905 cls : 'roo-radio-set-items'
35908 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35910 if (align === 'left' && this.fieldLabel.length) {
35913 cls : "roo-radio-set-right",
35919 if(this.labelWidth > 12){
35920 label.style = "width: " + this.labelWidth + 'px';
35923 if(this.labelWidth < 13 && this.labelmd == 0){
35924 this.labelmd = this.labelWidth;
35927 if(this.labellg > 0){
35928 label.cls += ' col-lg-' + this.labellg;
35929 items.cls += ' col-lg-' + (12 - this.labellg);
35932 if(this.labelmd > 0){
35933 label.cls += ' col-md-' + this.labelmd;
35934 items.cls += ' col-md-' + (12 - this.labelmd);
35937 if(this.labelsm > 0){
35938 label.cls += ' col-sm-' + this.labelsm;
35939 items.cls += ' col-sm-' + (12 - this.labelsm);
35942 if(this.labelxs > 0){
35943 label.cls += ' col-xs-' + this.labelxs;
35944 items.cls += ' col-xs-' + (12 - this.labelxs);
35950 cls : 'roo-radio-set',
35954 cls : 'roo-radio-set-input',
35957 value : this.value ? this.value : ''
35964 if(this.weight.length){
35965 cfg.cls += ' roo-radio-' + this.weight;
35969 cfg.cls += ' roo-radio-set-inline';
35973 ['xs','sm','md','lg'].map(function(size){
35974 if (settings[size]) {
35975 cfg.cls += ' col-' + size + '-' + settings[size];
35983 initEvents : function()
35985 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35986 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35988 if(!this.fieldLabel.length){
35989 this.labelEl.hide();
35992 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35993 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35995 this.indicator = this.indicatorEl();
35997 if(this.indicator){
35998 this.indicator.addClass('invisible');
36001 this.originalValue = this.getValue();
36005 inputEl: function ()
36007 return this.el.select('.roo-radio-set-input', true).first();
36010 getChildContainer : function()
36012 return this.itemsEl;
36015 register : function(item)
36017 this.radioes.push(item);
36021 validate : function()
36023 if(this.getVisibilityEl().hasClass('hidden')){
36029 Roo.each(this.radioes, function(i){
36038 if(this.allowBlank) {
36042 if(this.disabled || valid){
36047 this.markInvalid();
36052 markValid : function()
36054 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36055 this.indicatorEl().removeClass('visible');
36056 this.indicatorEl().addClass('invisible');
36060 if (Roo.bootstrap.version == 3) {
36061 this.el.removeClass([this.invalidClass, this.validClass]);
36062 this.el.addClass(this.validClass);
36064 this.el.removeClass(['is-invalid','is-valid']);
36065 this.el.addClass(['is-valid']);
36067 this.fireEvent('valid', this);
36070 markInvalid : function(msg)
36072 if(this.allowBlank || this.disabled){
36076 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36077 this.indicatorEl().removeClass('invisible');
36078 this.indicatorEl().addClass('visible');
36080 if (Roo.bootstrap.version == 3) {
36081 this.el.removeClass([this.invalidClass, this.validClass]);
36082 this.el.addClass(this.invalidClass);
36084 this.el.removeClass(['is-invalid','is-valid']);
36085 this.el.addClass(['is-invalid']);
36088 this.fireEvent('invalid', this, msg);
36092 setValue : function(v, suppressEvent)
36094 if(this.value === v){
36101 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36104 Roo.each(this.radioes, function(i){
36106 i.el.removeClass('checked');
36109 Roo.each(this.radioes, function(i){
36111 if(i.value === v || i.value.toString() === v.toString()){
36113 i.el.addClass('checked');
36115 if(suppressEvent !== true){
36116 this.fireEvent('check', this, i);
36127 clearInvalid : function(){
36129 if(!this.el || this.preventMark){
36133 this.el.removeClass([this.invalidClass]);
36135 this.fireEvent('valid', this);
36140 Roo.apply(Roo.bootstrap.RadioSet, {
36144 register : function(set)
36146 this.groups[set.name] = set;
36149 get: function(name)
36151 if (typeof(this.groups[name]) == 'undefined') {
36155 return this.groups[name] ;
36161 * Ext JS Library 1.1.1
36162 * Copyright(c) 2006-2007, Ext JS, LLC.
36164 * Originally Released Under LGPL - original licence link has changed is not relivant.
36167 * <script type="text/javascript">
36172 * @class Roo.bootstrap.SplitBar
36173 * @extends Roo.util.Observable
36174 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36178 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36179 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36180 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36181 split.minSize = 100;
36182 split.maxSize = 600;
36183 split.animate = true;
36184 split.on('moved', splitterMoved);
36187 * Create a new SplitBar
36188 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36189 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36190 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36191 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36192 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36193 position of the SplitBar).
36195 Roo.bootstrap.SplitBar = function(cfg){
36200 // dragElement : elm
36201 // resizingElement: el,
36203 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36204 // placement : Roo.bootstrap.SplitBar.LEFT ,
36205 // existingProxy ???
36208 this.el = Roo.get(cfg.dragElement, true);
36209 this.el.dom.unselectable = "on";
36211 this.resizingEl = Roo.get(cfg.resizingElement, true);
36215 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36216 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36219 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36222 * The minimum size of the resizing element. (Defaults to 0)
36228 * The maximum size of the resizing element. (Defaults to 2000)
36231 this.maxSize = 2000;
36234 * Whether to animate the transition to the new size
36237 this.animate = false;
36240 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36243 this.useShim = false;
36248 if(!cfg.existingProxy){
36250 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36252 this.proxy = Roo.get(cfg.existingProxy).dom;
36255 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36258 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36261 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36264 this.dragSpecs = {};
36267 * @private The adapter to use to positon and resize elements
36269 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36270 this.adapter.init(this);
36272 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36274 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36275 this.el.addClass("roo-splitbar-h");
36278 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36279 this.el.addClass("roo-splitbar-v");
36285 * Fires when the splitter is moved (alias for {@link #event-moved})
36286 * @param {Roo.bootstrap.SplitBar} this
36287 * @param {Number} newSize the new width or height
36292 * Fires when the splitter is moved
36293 * @param {Roo.bootstrap.SplitBar} this
36294 * @param {Number} newSize the new width or height
36298 * @event beforeresize
36299 * Fires before the splitter is dragged
36300 * @param {Roo.bootstrap.SplitBar} this
36302 "beforeresize" : true,
36304 "beforeapply" : true
36307 Roo.util.Observable.call(this);
36310 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36311 onStartProxyDrag : function(x, y){
36312 this.fireEvent("beforeresize", this);
36314 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36316 o.enableDisplayMode("block");
36317 // all splitbars share the same overlay
36318 Roo.bootstrap.SplitBar.prototype.overlay = o;
36320 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36321 this.overlay.show();
36322 Roo.get(this.proxy).setDisplayed("block");
36323 var size = this.adapter.getElementSize(this);
36324 this.activeMinSize = this.getMinimumSize();;
36325 this.activeMaxSize = this.getMaximumSize();;
36326 var c1 = size - this.activeMinSize;
36327 var c2 = Math.max(this.activeMaxSize - size, 0);
36328 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36329 this.dd.resetConstraints();
36330 this.dd.setXConstraint(
36331 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36332 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36334 this.dd.setYConstraint(0, 0);
36336 this.dd.resetConstraints();
36337 this.dd.setXConstraint(0, 0);
36338 this.dd.setYConstraint(
36339 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36340 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36343 this.dragSpecs.startSize = size;
36344 this.dragSpecs.startPoint = [x, y];
36345 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36349 * @private Called after the drag operation by the DDProxy
36351 onEndProxyDrag : function(e){
36352 Roo.get(this.proxy).setDisplayed(false);
36353 var endPoint = Roo.lib.Event.getXY(e);
36355 this.overlay.hide();
36358 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36359 newSize = this.dragSpecs.startSize +
36360 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36361 endPoint[0] - this.dragSpecs.startPoint[0] :
36362 this.dragSpecs.startPoint[0] - endPoint[0]
36365 newSize = this.dragSpecs.startSize +
36366 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36367 endPoint[1] - this.dragSpecs.startPoint[1] :
36368 this.dragSpecs.startPoint[1] - endPoint[1]
36371 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36372 if(newSize != this.dragSpecs.startSize){
36373 if(this.fireEvent('beforeapply', this, newSize) !== false){
36374 this.adapter.setElementSize(this, newSize);
36375 this.fireEvent("moved", this, newSize);
36376 this.fireEvent("resize", this, newSize);
36382 * Get the adapter this SplitBar uses
36383 * @return The adapter object
36385 getAdapter : function(){
36386 return this.adapter;
36390 * Set the adapter this SplitBar uses
36391 * @param {Object} adapter A SplitBar adapter object
36393 setAdapter : function(adapter){
36394 this.adapter = adapter;
36395 this.adapter.init(this);
36399 * Gets the minimum size for the resizing element
36400 * @return {Number} The minimum size
36402 getMinimumSize : function(){
36403 return this.minSize;
36407 * Sets the minimum size for the resizing element
36408 * @param {Number} minSize The minimum size
36410 setMinimumSize : function(minSize){
36411 this.minSize = minSize;
36415 * Gets the maximum size for the resizing element
36416 * @return {Number} The maximum size
36418 getMaximumSize : function(){
36419 return this.maxSize;
36423 * Sets the maximum size for the resizing element
36424 * @param {Number} maxSize The maximum size
36426 setMaximumSize : function(maxSize){
36427 this.maxSize = maxSize;
36431 * Sets the initialize size for the resizing element
36432 * @param {Number} size The initial size
36434 setCurrentSize : function(size){
36435 var oldAnimate = this.animate;
36436 this.animate = false;
36437 this.adapter.setElementSize(this, size);
36438 this.animate = oldAnimate;
36442 * Destroy this splitbar.
36443 * @param {Boolean} removeEl True to remove the element
36445 destroy : function(removeEl){
36447 this.shim.remove();
36450 this.proxy.parentNode.removeChild(this.proxy);
36458 * @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.
36460 Roo.bootstrap.SplitBar.createProxy = function(dir){
36461 var proxy = new Roo.Element(document.createElement("div"));
36462 proxy.unselectable();
36463 var cls = 'roo-splitbar-proxy';
36464 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36465 document.body.appendChild(proxy.dom);
36470 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36471 * Default Adapter. It assumes the splitter and resizing element are not positioned
36472 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36474 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36477 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36478 // do nothing for now
36479 init : function(s){
36483 * Called before drag operations to get the current size of the resizing element.
36484 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36486 getElementSize : function(s){
36487 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36488 return s.resizingEl.getWidth();
36490 return s.resizingEl.getHeight();
36495 * Called after drag operations to set the size of the resizing element.
36496 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36497 * @param {Number} newSize The new size to set
36498 * @param {Function} onComplete A function to be invoked when resizing is complete
36500 setElementSize : function(s, newSize, onComplete){
36501 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36503 s.resizingEl.setWidth(newSize);
36505 onComplete(s, newSize);
36508 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36513 s.resizingEl.setHeight(newSize);
36515 onComplete(s, newSize);
36518 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36525 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36526 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36527 * Adapter that moves the splitter element to align with the resized sizing element.
36528 * Used with an absolute positioned SplitBar.
36529 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36530 * document.body, make sure you assign an id to the body element.
36532 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36533 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36534 this.container = Roo.get(container);
36537 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36538 init : function(s){
36539 this.basic.init(s);
36542 getElementSize : function(s){
36543 return this.basic.getElementSize(s);
36546 setElementSize : function(s, newSize, onComplete){
36547 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36550 moveSplitter : function(s){
36551 var yes = Roo.bootstrap.SplitBar;
36552 switch(s.placement){
36554 s.el.setX(s.resizingEl.getRight());
36557 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36560 s.el.setY(s.resizingEl.getBottom());
36563 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36570 * Orientation constant - Create a vertical SplitBar
36574 Roo.bootstrap.SplitBar.VERTICAL = 1;
36577 * Orientation constant - Create a horizontal SplitBar
36581 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36584 * Placement constant - The resizing element is to the left of the splitter element
36588 Roo.bootstrap.SplitBar.LEFT = 1;
36591 * Placement constant - The resizing element is to the right of the splitter element
36595 Roo.bootstrap.SplitBar.RIGHT = 2;
36598 * Placement constant - The resizing element is positioned above the splitter element
36602 Roo.bootstrap.SplitBar.TOP = 3;
36605 * Placement constant - The resizing element is positioned under splitter element
36609 Roo.bootstrap.SplitBar.BOTTOM = 4;
36610 Roo.namespace("Roo.bootstrap.layout");/*
36612 * Ext JS Library 1.1.1
36613 * Copyright(c) 2006-2007, Ext JS, LLC.
36615 * Originally Released Under LGPL - original licence link has changed is not relivant.
36618 * <script type="text/javascript">
36622 * @class Roo.bootstrap.layout.Manager
36623 * @extends Roo.bootstrap.Component
36624 * Base class for layout managers.
36626 Roo.bootstrap.layout.Manager = function(config)
36628 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36634 /** false to disable window resize monitoring @type Boolean */
36635 this.monitorWindowResize = true;
36640 * Fires when a layout is performed.
36641 * @param {Roo.LayoutManager} this
36645 * @event regionresized
36646 * Fires when the user resizes a region.
36647 * @param {Roo.LayoutRegion} region The resized region
36648 * @param {Number} newSize The new size (width for east/west, height for north/south)
36650 "regionresized" : true,
36652 * @event regioncollapsed
36653 * Fires when a region is collapsed.
36654 * @param {Roo.LayoutRegion} region The collapsed region
36656 "regioncollapsed" : true,
36658 * @event regionexpanded
36659 * Fires when a region is expanded.
36660 * @param {Roo.LayoutRegion} region The expanded region
36662 "regionexpanded" : true
36664 this.updating = false;
36667 this.el = Roo.get(config.el);
36673 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36678 monitorWindowResize : true,
36684 onRender : function(ct, position)
36687 this.el = Roo.get(ct);
36690 //this.fireEvent('render',this);
36694 initEvents: function()
36698 // ie scrollbar fix
36699 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36700 document.body.scroll = "no";
36701 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36702 this.el.position('relative');
36704 this.id = this.el.id;
36705 this.el.addClass("roo-layout-container");
36706 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36707 if(this.el.dom != document.body ) {
36708 this.el.on('resize', this.layout,this);
36709 this.el.on('show', this.layout,this);
36715 * Returns true if this layout is currently being updated
36716 * @return {Boolean}
36718 isUpdating : function(){
36719 return this.updating;
36723 * Suspend the LayoutManager from doing auto-layouts while
36724 * making multiple add or remove calls
36726 beginUpdate : function(){
36727 this.updating = true;
36731 * Restore auto-layouts and optionally disable the manager from performing a layout
36732 * @param {Boolean} noLayout true to disable a layout update
36734 endUpdate : function(noLayout){
36735 this.updating = false;
36741 layout: function(){
36745 onRegionResized : function(region, newSize){
36746 this.fireEvent("regionresized", region, newSize);
36750 onRegionCollapsed : function(region){
36751 this.fireEvent("regioncollapsed", region);
36754 onRegionExpanded : function(region){
36755 this.fireEvent("regionexpanded", region);
36759 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36760 * performs box-model adjustments.
36761 * @return {Object} The size as an object {width: (the width), height: (the height)}
36763 getViewSize : function()
36766 if(this.el.dom != document.body){
36767 size = this.el.getSize();
36769 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36771 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36772 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36777 * Returns the Element this layout is bound to.
36778 * @return {Roo.Element}
36780 getEl : function(){
36785 * Returns the specified region.
36786 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36787 * @return {Roo.LayoutRegion}
36789 getRegion : function(target){
36790 return this.regions[target.toLowerCase()];
36793 onWindowResize : function(){
36794 if(this.monitorWindowResize){
36801 * Ext JS Library 1.1.1
36802 * Copyright(c) 2006-2007, Ext JS, LLC.
36804 * Originally Released Under LGPL - original licence link has changed is not relivant.
36807 * <script type="text/javascript">
36810 * @class Roo.bootstrap.layout.Border
36811 * @extends Roo.bootstrap.layout.Manager
36812 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36813 * please see: examples/bootstrap/nested.html<br><br>
36815 <b>The container the layout is rendered into can be either the body element or any other element.
36816 If it is not the body element, the container needs to either be an absolute positioned element,
36817 or you will need to add "position:relative" to the css of the container. You will also need to specify
36818 the container size if it is not the body element.</b>
36821 * Create a new Border
36822 * @param {Object} config Configuration options
36824 Roo.bootstrap.layout.Border = function(config){
36825 config = config || {};
36826 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36830 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36831 if(config[region]){
36832 config[region].region = region;
36833 this.addRegion(config[region]);
36839 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
36841 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36843 parent : false, // this might point to a 'nest' or a ???
36846 * Creates and adds a new region if it doesn't already exist.
36847 * @param {String} target The target region key (north, south, east, west or center).
36848 * @param {Object} config The regions config object
36849 * @return {BorderLayoutRegion} The new region
36851 addRegion : function(config)
36853 if(!this.regions[config.region]){
36854 var r = this.factory(config);
36855 this.bindRegion(r);
36857 return this.regions[config.region];
36861 bindRegion : function(r){
36862 this.regions[r.config.region] = r;
36864 r.on("visibilitychange", this.layout, this);
36865 r.on("paneladded", this.layout, this);
36866 r.on("panelremoved", this.layout, this);
36867 r.on("invalidated", this.layout, this);
36868 r.on("resized", this.onRegionResized, this);
36869 r.on("collapsed", this.onRegionCollapsed, this);
36870 r.on("expanded", this.onRegionExpanded, this);
36874 * Performs a layout update.
36876 layout : function()
36878 if(this.updating) {
36882 // render all the rebions if they have not been done alreayd?
36883 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36884 if(this.regions[region] && !this.regions[region].bodyEl){
36885 this.regions[region].onRender(this.el)
36889 var size = this.getViewSize();
36890 var w = size.width;
36891 var h = size.height;
36896 //var x = 0, y = 0;
36898 var rs = this.regions;
36899 var north = rs["north"];
36900 var south = rs["south"];
36901 var west = rs["west"];
36902 var east = rs["east"];
36903 var center = rs["center"];
36904 //if(this.hideOnLayout){ // not supported anymore
36905 //c.el.setStyle("display", "none");
36907 if(north && north.isVisible()){
36908 var b = north.getBox();
36909 var m = north.getMargins();
36910 b.width = w - (m.left+m.right);
36913 centerY = b.height + b.y + m.bottom;
36914 centerH -= centerY;
36915 north.updateBox(this.safeBox(b));
36917 if(south && south.isVisible()){
36918 var b = south.getBox();
36919 var m = south.getMargins();
36920 b.width = w - (m.left+m.right);
36922 var totalHeight = (b.height + m.top + m.bottom);
36923 b.y = h - totalHeight + m.top;
36924 centerH -= totalHeight;
36925 south.updateBox(this.safeBox(b));
36927 if(west && west.isVisible()){
36928 var b = west.getBox();
36929 var m = west.getMargins();
36930 b.height = centerH - (m.top+m.bottom);
36932 b.y = centerY + m.top;
36933 var totalWidth = (b.width + m.left + m.right);
36934 centerX += totalWidth;
36935 centerW -= totalWidth;
36936 west.updateBox(this.safeBox(b));
36938 if(east && east.isVisible()){
36939 var b = east.getBox();
36940 var m = east.getMargins();
36941 b.height = centerH - (m.top+m.bottom);
36942 var totalWidth = (b.width + m.left + m.right);
36943 b.x = w - totalWidth + m.left;
36944 b.y = centerY + m.top;
36945 centerW -= totalWidth;
36946 east.updateBox(this.safeBox(b));
36949 var m = center.getMargins();
36951 x: centerX + m.left,
36952 y: centerY + m.top,
36953 width: centerW - (m.left+m.right),
36954 height: centerH - (m.top+m.bottom)
36956 //if(this.hideOnLayout){
36957 //center.el.setStyle("display", "block");
36959 center.updateBox(this.safeBox(centerBox));
36962 this.fireEvent("layout", this);
36966 safeBox : function(box){
36967 box.width = Math.max(0, box.width);
36968 box.height = Math.max(0, box.height);
36973 * Adds a ContentPanel (or subclass) to this layout.
36974 * @param {String} target The target region key (north, south, east, west or center).
36975 * @param {Roo.ContentPanel} panel The panel to add
36976 * @return {Roo.ContentPanel} The added panel
36978 add : function(target, panel){
36980 target = target.toLowerCase();
36981 return this.regions[target].add(panel);
36985 * Remove a ContentPanel (or subclass) to this layout.
36986 * @param {String} target The target region key (north, south, east, west or center).
36987 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36988 * @return {Roo.ContentPanel} The removed panel
36990 remove : function(target, panel){
36991 target = target.toLowerCase();
36992 return this.regions[target].remove(panel);
36996 * Searches all regions for a panel with the specified id
36997 * @param {String} panelId
36998 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37000 findPanel : function(panelId){
37001 var rs = this.regions;
37002 for(var target in rs){
37003 if(typeof rs[target] != "function"){
37004 var p = rs[target].getPanel(panelId);
37014 * Searches all regions for a panel with the specified id and activates (shows) it.
37015 * @param {String/ContentPanel} panelId The panels id or the panel itself
37016 * @return {Roo.ContentPanel} The shown panel or null
37018 showPanel : function(panelId) {
37019 var rs = this.regions;
37020 for(var target in rs){
37021 var r = rs[target];
37022 if(typeof r != "function"){
37023 if(r.hasPanel(panelId)){
37024 return r.showPanel(panelId);
37032 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37033 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37036 restoreState : function(provider){
37038 provider = Roo.state.Manager;
37040 var sm = new Roo.LayoutStateManager();
37041 sm.init(this, provider);
37047 * Adds a xtype elements to the layout.
37051 xtype : 'ContentPanel',
37058 xtype : 'NestedLayoutPanel',
37064 items : [ ... list of content panels or nested layout panels.. ]
37068 * @param {Object} cfg Xtype definition of item to add.
37070 addxtype : function(cfg)
37072 // basically accepts a pannel...
37073 // can accept a layout region..!?!?
37074 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37077 // theory? children can only be panels??
37079 //if (!cfg.xtype.match(/Panel$/)) {
37084 if (typeof(cfg.region) == 'undefined') {
37085 Roo.log("Failed to add Panel, region was not set");
37089 var region = cfg.region;
37095 xitems = cfg.items;
37100 if ( region == 'center') {
37101 Roo.log("Center: " + cfg.title);
37107 case 'Content': // ContentPanel (el, cfg)
37108 case 'Scroll': // ContentPanel (el, cfg)
37110 cfg.autoCreate = cfg.autoCreate || true;
37111 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37113 // var el = this.el.createChild();
37114 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37117 this.add(region, ret);
37121 case 'TreePanel': // our new panel!
37122 cfg.el = this.el.createChild();
37123 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37124 this.add(region, ret);
37129 // create a new Layout (which is a Border Layout...
37131 var clayout = cfg.layout;
37132 clayout.el = this.el.createChild();
37133 clayout.items = clayout.items || [];
37137 // replace this exitems with the clayout ones..
37138 xitems = clayout.items;
37140 // force background off if it's in center...
37141 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37142 cfg.background = false;
37144 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37147 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37148 //console.log('adding nested layout panel ' + cfg.toSource());
37149 this.add(region, ret);
37150 nb = {}; /// find first...
37155 // needs grid and region
37157 //var el = this.getRegion(region).el.createChild();
37159 *var el = this.el.createChild();
37160 // create the grid first...
37161 cfg.grid.container = el;
37162 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37165 if (region == 'center' && this.active ) {
37166 cfg.background = false;
37169 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37171 this.add(region, ret);
37173 if (cfg.background) {
37174 // render grid on panel activation (if panel background)
37175 ret.on('activate', function(gp) {
37176 if (!gp.grid.rendered) {
37177 // gp.grid.render(el);
37181 // cfg.grid.render(el);
37187 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37188 // it was the old xcomponent building that caused this before.
37189 // espeically if border is the top element in the tree.
37199 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37201 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37202 this.add(region, ret);
37206 throw "Can not add '" + cfg.xtype + "' to Border";
37212 this.beginUpdate();
37216 Roo.each(xitems, function(i) {
37217 region = nb && i.region ? i.region : false;
37219 var add = ret.addxtype(i);
37222 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37223 if (!i.background) {
37224 abn[region] = nb[region] ;
37231 // make the last non-background panel active..
37232 //if (nb) { Roo.log(abn); }
37235 for(var r in abn) {
37236 region = this.getRegion(r);
37238 // tried using nb[r], but it does not work..
37240 region.showPanel(abn[r]);
37251 factory : function(cfg)
37254 var validRegions = Roo.bootstrap.layout.Border.regions;
37256 var target = cfg.region;
37259 var r = Roo.bootstrap.layout;
37263 return new r.North(cfg);
37265 return new r.South(cfg);
37267 return new r.East(cfg);
37269 return new r.West(cfg);
37271 return new r.Center(cfg);
37273 throw 'Layout region "'+target+'" not supported.';
37280 * Ext JS Library 1.1.1
37281 * Copyright(c) 2006-2007, Ext JS, LLC.
37283 * Originally Released Under LGPL - original licence link has changed is not relivant.
37286 * <script type="text/javascript">
37290 * @class Roo.bootstrap.layout.Basic
37291 * @extends Roo.util.Observable
37292 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37293 * and does not have a titlebar, tabs or any other features. All it does is size and position
37294 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37295 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37296 * @cfg {string} region the region that it inhabits..
37297 * @cfg {bool} skipConfig skip config?
37301 Roo.bootstrap.layout.Basic = function(config){
37303 this.mgr = config.mgr;
37305 this.position = config.region;
37307 var skipConfig = config.skipConfig;
37311 * @scope Roo.BasicLayoutRegion
37315 * @event beforeremove
37316 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37317 * @param {Roo.LayoutRegion} this
37318 * @param {Roo.ContentPanel} panel The panel
37319 * @param {Object} e The cancel event object
37321 "beforeremove" : true,
37323 * @event invalidated
37324 * Fires when the layout for this region is changed.
37325 * @param {Roo.LayoutRegion} this
37327 "invalidated" : true,
37329 * @event visibilitychange
37330 * Fires when this region is shown or hidden
37331 * @param {Roo.LayoutRegion} this
37332 * @param {Boolean} visibility true or false
37334 "visibilitychange" : true,
37336 * @event paneladded
37337 * Fires when a panel is added.
37338 * @param {Roo.LayoutRegion} this
37339 * @param {Roo.ContentPanel} panel The panel
37341 "paneladded" : true,
37343 * @event panelremoved
37344 * Fires when a panel is removed.
37345 * @param {Roo.LayoutRegion} this
37346 * @param {Roo.ContentPanel} panel The panel
37348 "panelremoved" : true,
37350 * @event beforecollapse
37351 * Fires when this region before collapse.
37352 * @param {Roo.LayoutRegion} this
37354 "beforecollapse" : true,
37357 * Fires when this region is collapsed.
37358 * @param {Roo.LayoutRegion} this
37360 "collapsed" : true,
37363 * Fires when this region is expanded.
37364 * @param {Roo.LayoutRegion} this
37369 * Fires when this region is slid into view.
37370 * @param {Roo.LayoutRegion} this
37372 "slideshow" : true,
37375 * Fires when this region slides out of view.
37376 * @param {Roo.LayoutRegion} this
37378 "slidehide" : true,
37380 * @event panelactivated
37381 * Fires when a panel is activated.
37382 * @param {Roo.LayoutRegion} this
37383 * @param {Roo.ContentPanel} panel The activated panel
37385 "panelactivated" : true,
37388 * Fires when the user resizes this region.
37389 * @param {Roo.LayoutRegion} this
37390 * @param {Number} newSize The new size (width for east/west, height for north/south)
37394 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37395 this.panels = new Roo.util.MixedCollection();
37396 this.panels.getKey = this.getPanelId.createDelegate(this);
37398 this.activePanel = null;
37399 // ensure listeners are added...
37401 if (config.listeners || config.events) {
37402 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37403 listeners : config.listeners || {},
37404 events : config.events || {}
37408 if(skipConfig !== true){
37409 this.applyConfig(config);
37413 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37415 getPanelId : function(p){
37419 applyConfig : function(config){
37420 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37421 this.config = config;
37426 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37427 * the width, for horizontal (north, south) the height.
37428 * @param {Number} newSize The new width or height
37430 resizeTo : function(newSize){
37431 var el = this.el ? this.el :
37432 (this.activePanel ? this.activePanel.getEl() : null);
37434 switch(this.position){
37437 el.setWidth(newSize);
37438 this.fireEvent("resized", this, newSize);
37442 el.setHeight(newSize);
37443 this.fireEvent("resized", this, newSize);
37449 getBox : function(){
37450 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37453 getMargins : function(){
37454 return this.margins;
37457 updateBox : function(box){
37459 var el = this.activePanel.getEl();
37460 el.dom.style.left = box.x + "px";
37461 el.dom.style.top = box.y + "px";
37462 this.activePanel.setSize(box.width, box.height);
37466 * Returns the container element for this region.
37467 * @return {Roo.Element}
37469 getEl : function(){
37470 return this.activePanel;
37474 * Returns true if this region is currently visible.
37475 * @return {Boolean}
37477 isVisible : function(){
37478 return this.activePanel ? true : false;
37481 setActivePanel : function(panel){
37482 panel = this.getPanel(panel);
37483 if(this.activePanel && this.activePanel != panel){
37484 this.activePanel.setActiveState(false);
37485 this.activePanel.getEl().setLeftTop(-10000,-10000);
37487 this.activePanel = panel;
37488 panel.setActiveState(true);
37490 panel.setSize(this.box.width, this.box.height);
37492 this.fireEvent("panelactivated", this, panel);
37493 this.fireEvent("invalidated");
37497 * Show the specified panel.
37498 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37499 * @return {Roo.ContentPanel} The shown panel or null
37501 showPanel : function(panel){
37502 panel = this.getPanel(panel);
37504 this.setActivePanel(panel);
37510 * Get the active panel for this region.
37511 * @return {Roo.ContentPanel} The active panel or null
37513 getActivePanel : function(){
37514 return this.activePanel;
37518 * Add the passed ContentPanel(s)
37519 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37520 * @return {Roo.ContentPanel} The panel added (if only one was added)
37522 add : function(panel){
37523 if(arguments.length > 1){
37524 for(var i = 0, len = arguments.length; i < len; i++) {
37525 this.add(arguments[i]);
37529 if(this.hasPanel(panel)){
37530 this.showPanel(panel);
37533 var el = panel.getEl();
37534 if(el.dom.parentNode != this.mgr.el.dom){
37535 this.mgr.el.dom.appendChild(el.dom);
37537 if(panel.setRegion){
37538 panel.setRegion(this);
37540 this.panels.add(panel);
37541 el.setStyle("position", "absolute");
37542 if(!panel.background){
37543 this.setActivePanel(panel);
37544 if(this.config.initialSize && this.panels.getCount()==1){
37545 this.resizeTo(this.config.initialSize);
37548 this.fireEvent("paneladded", this, panel);
37553 * Returns true if the panel is in this region.
37554 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37555 * @return {Boolean}
37557 hasPanel : function(panel){
37558 if(typeof panel == "object"){ // must be panel obj
37559 panel = panel.getId();
37561 return this.getPanel(panel) ? true : false;
37565 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37566 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37567 * @param {Boolean} preservePanel Overrides the config preservePanel option
37568 * @return {Roo.ContentPanel} The panel that was removed
37570 remove : function(panel, preservePanel){
37571 panel = this.getPanel(panel);
37576 this.fireEvent("beforeremove", this, panel, e);
37577 if(e.cancel === true){
37580 var panelId = panel.getId();
37581 this.panels.removeKey(panelId);
37586 * Returns the panel specified or null if it's not in this region.
37587 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37588 * @return {Roo.ContentPanel}
37590 getPanel : function(id){
37591 if(typeof id == "object"){ // must be panel obj
37594 return this.panels.get(id);
37598 * Returns this regions position (north/south/east/west/center).
37601 getPosition: function(){
37602 return this.position;
37606 * Ext JS Library 1.1.1
37607 * Copyright(c) 2006-2007, Ext JS, LLC.
37609 * Originally Released Under LGPL - original licence link has changed is not relivant.
37612 * <script type="text/javascript">
37616 * @class Roo.bootstrap.layout.Region
37617 * @extends Roo.bootstrap.layout.Basic
37618 * This class represents a region in a layout manager.
37620 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37621 * @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})
37622 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
37623 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
37624 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
37625 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
37626 * @cfg {String} title The title for the region (overrides panel titles)
37627 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
37628 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37629 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
37630 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37631 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37632 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37633 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37634 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37635 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37636 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37638 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37639 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37640 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37641 * @cfg {Number} width For East/West panels
37642 * @cfg {Number} height For North/South panels
37643 * @cfg {Boolean} split To show the splitter
37644 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37646 * @cfg {string} cls Extra CSS classes to add to region
37648 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37649 * @cfg {string} region the region that it inhabits..
37652 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37653 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37655 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37656 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37657 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37659 Roo.bootstrap.layout.Region = function(config)
37661 this.applyConfig(config);
37663 var mgr = config.mgr;
37664 var pos = config.region;
37665 config.skipConfig = true;
37666 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37669 this.onRender(mgr.el);
37672 this.visible = true;
37673 this.collapsed = false;
37674 this.unrendered_panels = [];
37677 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37679 position: '', // set by wrapper (eg. north/south etc..)
37680 unrendered_panels : null, // unrendered panels.
37682 tabPosition : false,
37684 mgr: false, // points to 'Border'
37687 createBody : function(){
37688 /** This region's body element
37689 * @type Roo.Element */
37690 this.bodyEl = this.el.createChild({
37692 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37696 onRender: function(ctr, pos)
37698 var dh = Roo.DomHelper;
37699 /** This region's container element
37700 * @type Roo.Element */
37701 this.el = dh.append(ctr.dom, {
37703 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37705 /** This region's title element
37706 * @type Roo.Element */
37708 this.titleEl = dh.append(this.el.dom, {
37710 unselectable: "on",
37711 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37713 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
37714 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37718 this.titleEl.enableDisplayMode();
37719 /** This region's title text element
37720 * @type HTMLElement */
37721 this.titleTextEl = this.titleEl.dom.firstChild;
37722 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37724 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37725 this.closeBtn.enableDisplayMode();
37726 this.closeBtn.on("click", this.closeClicked, this);
37727 this.closeBtn.hide();
37729 this.createBody(this.config);
37730 if(this.config.hideWhenEmpty){
37732 this.on("paneladded", this.validateVisibility, this);
37733 this.on("panelremoved", this.validateVisibility, this);
37735 if(this.autoScroll){
37736 this.bodyEl.setStyle("overflow", "auto");
37738 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37740 //if(c.titlebar !== false){
37741 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37742 this.titleEl.hide();
37744 this.titleEl.show();
37745 if(this.config.title){
37746 this.titleTextEl.innerHTML = this.config.title;
37750 if(this.config.collapsed){
37751 this.collapse(true);
37753 if(this.config.hidden){
37757 if (this.unrendered_panels && this.unrendered_panels.length) {
37758 for (var i =0;i< this.unrendered_panels.length; i++) {
37759 this.add(this.unrendered_panels[i]);
37761 this.unrendered_panels = null;
37767 applyConfig : function(c)
37770 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37771 var dh = Roo.DomHelper;
37772 if(c.titlebar !== false){
37773 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37774 this.collapseBtn.on("click", this.collapse, this);
37775 this.collapseBtn.enableDisplayMode();
37777 if(c.showPin === true || this.showPin){
37778 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37779 this.stickBtn.enableDisplayMode();
37780 this.stickBtn.on("click", this.expand, this);
37781 this.stickBtn.hide();
37786 /** This region's collapsed element
37787 * @type Roo.Element */
37790 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37791 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37794 if(c.floatable !== false){
37795 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37796 this.collapsedEl.on("click", this.collapseClick, this);
37799 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37800 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37801 id: "message", unselectable: "on", style:{"float":"left"}});
37802 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37804 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37805 this.expandBtn.on("click", this.expand, this);
37809 if(this.collapseBtn){
37810 this.collapseBtn.setVisible(c.collapsible == true);
37813 this.cmargins = c.cmargins || this.cmargins ||
37814 (this.position == "west" || this.position == "east" ?
37815 {top: 0, left: 2, right:2, bottom: 0} :
37816 {top: 2, left: 0, right:0, bottom: 2});
37818 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37821 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37823 this.autoScroll = c.autoScroll || false;
37828 this.duration = c.duration || .30;
37829 this.slideDuration = c.slideDuration || .45;
37834 * Returns true if this region is currently visible.
37835 * @return {Boolean}
37837 isVisible : function(){
37838 return this.visible;
37842 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37843 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
37845 //setCollapsedTitle : function(title){
37846 // title = title || " ";
37847 // if(this.collapsedTitleTextEl){
37848 // this.collapsedTitleTextEl.innerHTML = title;
37852 getBox : function(){
37854 // if(!this.collapsed){
37855 b = this.el.getBox(false, true);
37857 // b = this.collapsedEl.getBox(false, true);
37862 getMargins : function(){
37863 return this.margins;
37864 //return this.collapsed ? this.cmargins : this.margins;
37867 highlight : function(){
37868 this.el.addClass("x-layout-panel-dragover");
37871 unhighlight : function(){
37872 this.el.removeClass("x-layout-panel-dragover");
37875 updateBox : function(box)
37877 if (!this.bodyEl) {
37878 return; // not rendered yet..
37882 if(!this.collapsed){
37883 this.el.dom.style.left = box.x + "px";
37884 this.el.dom.style.top = box.y + "px";
37885 this.updateBody(box.width, box.height);
37887 this.collapsedEl.dom.style.left = box.x + "px";
37888 this.collapsedEl.dom.style.top = box.y + "px";
37889 this.collapsedEl.setSize(box.width, box.height);
37892 this.tabs.autoSizeTabs();
37896 updateBody : function(w, h)
37899 this.el.setWidth(w);
37900 w -= this.el.getBorderWidth("rl");
37901 if(this.config.adjustments){
37902 w += this.config.adjustments[0];
37905 if(h !== null && h > 0){
37906 this.el.setHeight(h);
37907 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37908 h -= this.el.getBorderWidth("tb");
37909 if(this.config.adjustments){
37910 h += this.config.adjustments[1];
37912 this.bodyEl.setHeight(h);
37914 h = this.tabs.syncHeight(h);
37917 if(this.panelSize){
37918 w = w !== null ? w : this.panelSize.width;
37919 h = h !== null ? h : this.panelSize.height;
37921 if(this.activePanel){
37922 var el = this.activePanel.getEl();
37923 w = w !== null ? w : el.getWidth();
37924 h = h !== null ? h : el.getHeight();
37925 this.panelSize = {width: w, height: h};
37926 this.activePanel.setSize(w, h);
37928 if(Roo.isIE && this.tabs){
37929 this.tabs.el.repaint();
37934 * Returns the container element for this region.
37935 * @return {Roo.Element}
37937 getEl : function(){
37942 * Hides this region.
37945 //if(!this.collapsed){
37946 this.el.dom.style.left = "-2000px";
37949 // this.collapsedEl.dom.style.left = "-2000px";
37950 // this.collapsedEl.hide();
37952 this.visible = false;
37953 this.fireEvent("visibilitychange", this, false);
37957 * Shows this region if it was previously hidden.
37960 //if(!this.collapsed){
37963 // this.collapsedEl.show();
37965 this.visible = true;
37966 this.fireEvent("visibilitychange", this, true);
37969 closeClicked : function(){
37970 if(this.activePanel){
37971 this.remove(this.activePanel);
37975 collapseClick : function(e){
37977 e.stopPropagation();
37980 e.stopPropagation();
37986 * Collapses this region.
37987 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37990 collapse : function(skipAnim, skipCheck = false){
37991 if(this.collapsed) {
37995 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37997 this.collapsed = true;
37999 this.split.el.hide();
38001 if(this.config.animate && skipAnim !== true){
38002 this.fireEvent("invalidated", this);
38003 this.animateCollapse();
38005 this.el.setLocation(-20000,-20000);
38007 this.collapsedEl.show();
38008 this.fireEvent("collapsed", this);
38009 this.fireEvent("invalidated", this);
38015 animateCollapse : function(){
38020 * Expands this region if it was previously collapsed.
38021 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38022 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38025 expand : function(e, skipAnim){
38027 e.stopPropagation();
38029 if(!this.collapsed || this.el.hasActiveFx()) {
38033 this.afterSlideIn();
38036 this.collapsed = false;
38037 if(this.config.animate && skipAnim !== true){
38038 this.animateExpand();
38042 this.split.el.show();
38044 this.collapsedEl.setLocation(-2000,-2000);
38045 this.collapsedEl.hide();
38046 this.fireEvent("invalidated", this);
38047 this.fireEvent("expanded", this);
38051 animateExpand : function(){
38055 initTabs : function()
38057 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38059 var ts = new Roo.bootstrap.panel.Tabs({
38060 el: this.bodyEl.dom,
38062 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38063 disableTooltips: this.config.disableTabTips,
38064 toolbar : this.config.toolbar
38067 if(this.config.hideTabs){
38068 ts.stripWrap.setDisplayed(false);
38071 ts.resizeTabs = this.config.resizeTabs === true;
38072 ts.minTabWidth = this.config.minTabWidth || 40;
38073 ts.maxTabWidth = this.config.maxTabWidth || 250;
38074 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38075 ts.monitorResize = false;
38076 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38077 ts.bodyEl.addClass('roo-layout-tabs-body');
38078 this.panels.each(this.initPanelAsTab, this);
38081 initPanelAsTab : function(panel){
38082 var ti = this.tabs.addTab(
38086 this.config.closeOnTab && panel.isClosable(),
38089 if(panel.tabTip !== undefined){
38090 ti.setTooltip(panel.tabTip);
38092 ti.on("activate", function(){
38093 this.setActivePanel(panel);
38096 if(this.config.closeOnTab){
38097 ti.on("beforeclose", function(t, e){
38099 this.remove(panel);
38103 panel.tabItem = ti;
38108 updatePanelTitle : function(panel, title)
38110 if(this.activePanel == panel){
38111 this.updateTitle(title);
38114 var ti = this.tabs.getTab(panel.getEl().id);
38116 if(panel.tabTip !== undefined){
38117 ti.setTooltip(panel.tabTip);
38122 updateTitle : function(title){
38123 if(this.titleTextEl && !this.config.title){
38124 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38128 setActivePanel : function(panel)
38130 panel = this.getPanel(panel);
38131 if(this.activePanel && this.activePanel != panel){
38132 if(this.activePanel.setActiveState(false) === false){
38136 this.activePanel = panel;
38137 panel.setActiveState(true);
38138 if(this.panelSize){
38139 panel.setSize(this.panelSize.width, this.panelSize.height);
38142 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38144 this.updateTitle(panel.getTitle());
38146 this.fireEvent("invalidated", this);
38148 this.fireEvent("panelactivated", this, panel);
38152 * Shows the specified panel.
38153 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38154 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38156 showPanel : function(panel)
38158 panel = this.getPanel(panel);
38161 var tab = this.tabs.getTab(panel.getEl().id);
38162 if(tab.isHidden()){
38163 this.tabs.unhideTab(tab.id);
38167 this.setActivePanel(panel);
38174 * Get the active panel for this region.
38175 * @return {Roo.ContentPanel} The active panel or null
38177 getActivePanel : function(){
38178 return this.activePanel;
38181 validateVisibility : function(){
38182 if(this.panels.getCount() < 1){
38183 this.updateTitle(" ");
38184 this.closeBtn.hide();
38187 if(!this.isVisible()){
38194 * Adds the passed ContentPanel(s) to this region.
38195 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38196 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38198 add : function(panel)
38200 if(arguments.length > 1){
38201 for(var i = 0, len = arguments.length; i < len; i++) {
38202 this.add(arguments[i]);
38207 // if we have not been rendered yet, then we can not really do much of this..
38208 if (!this.bodyEl) {
38209 this.unrendered_panels.push(panel);
38216 if(this.hasPanel(panel)){
38217 this.showPanel(panel);
38220 panel.setRegion(this);
38221 this.panels.add(panel);
38222 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38223 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38224 // and hide them... ???
38225 this.bodyEl.dom.appendChild(panel.getEl().dom);
38226 if(panel.background !== true){
38227 this.setActivePanel(panel);
38229 this.fireEvent("paneladded", this, panel);
38236 this.initPanelAsTab(panel);
38240 if(panel.background !== true){
38241 this.tabs.activate(panel.getEl().id);
38243 this.fireEvent("paneladded", this, panel);
38248 * Hides the tab for the specified panel.
38249 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38251 hidePanel : function(panel){
38252 if(this.tabs && (panel = this.getPanel(panel))){
38253 this.tabs.hideTab(panel.getEl().id);
38258 * Unhides the tab for a previously hidden panel.
38259 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38261 unhidePanel : function(panel){
38262 if(this.tabs && (panel = this.getPanel(panel))){
38263 this.tabs.unhideTab(panel.getEl().id);
38267 clearPanels : function(){
38268 while(this.panels.getCount() > 0){
38269 this.remove(this.panels.first());
38274 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38275 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38276 * @param {Boolean} preservePanel Overrides the config preservePanel option
38277 * @return {Roo.ContentPanel} The panel that was removed
38279 remove : function(panel, preservePanel)
38281 panel = this.getPanel(panel);
38286 this.fireEvent("beforeremove", this, panel, e);
38287 if(e.cancel === true){
38290 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38291 var panelId = panel.getId();
38292 this.panels.removeKey(panelId);
38294 document.body.appendChild(panel.getEl().dom);
38297 this.tabs.removeTab(panel.getEl().id);
38298 }else if (!preservePanel){
38299 this.bodyEl.dom.removeChild(panel.getEl().dom);
38301 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38302 var p = this.panels.first();
38303 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38304 tempEl.appendChild(p.getEl().dom);
38305 this.bodyEl.update("");
38306 this.bodyEl.dom.appendChild(p.getEl().dom);
38308 this.updateTitle(p.getTitle());
38310 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38311 this.setActivePanel(p);
38313 panel.setRegion(null);
38314 if(this.activePanel == panel){
38315 this.activePanel = null;
38317 if(this.config.autoDestroy !== false && preservePanel !== true){
38318 try{panel.destroy();}catch(e){}
38320 this.fireEvent("panelremoved", this, panel);
38325 * Returns the TabPanel component used by this region
38326 * @return {Roo.TabPanel}
38328 getTabs : function(){
38332 createTool : function(parentEl, className){
38333 var btn = Roo.DomHelper.append(parentEl, {
38335 cls: "x-layout-tools-button",
38338 cls: "roo-layout-tools-button-inner " + className,
38342 btn.addClassOnOver("roo-layout-tools-button-over");
38347 * Ext JS Library 1.1.1
38348 * Copyright(c) 2006-2007, Ext JS, LLC.
38350 * Originally Released Under LGPL - original licence link has changed is not relivant.
38353 * <script type="text/javascript">
38359 * @class Roo.SplitLayoutRegion
38360 * @extends Roo.LayoutRegion
38361 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38363 Roo.bootstrap.layout.Split = function(config){
38364 this.cursor = config.cursor;
38365 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38368 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38370 splitTip : "Drag to resize.",
38371 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38372 useSplitTips : false,
38374 applyConfig : function(config){
38375 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38378 onRender : function(ctr,pos) {
38380 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38381 if(!this.config.split){
38386 var splitEl = Roo.DomHelper.append(ctr.dom, {
38388 id: this.el.id + "-split",
38389 cls: "roo-layout-split roo-layout-split-"+this.position,
38392 /** The SplitBar for this region
38393 * @type Roo.SplitBar */
38394 // does not exist yet...
38395 Roo.log([this.position, this.orientation]);
38397 this.split = new Roo.bootstrap.SplitBar({
38398 dragElement : splitEl,
38399 resizingElement: this.el,
38400 orientation : this.orientation
38403 this.split.on("moved", this.onSplitMove, this);
38404 this.split.useShim = this.config.useShim === true;
38405 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38406 if(this.useSplitTips){
38407 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38409 //if(config.collapsible){
38410 // this.split.el.on("dblclick", this.collapse, this);
38413 if(typeof this.config.minSize != "undefined"){
38414 this.split.minSize = this.config.minSize;
38416 if(typeof this.config.maxSize != "undefined"){
38417 this.split.maxSize = this.config.maxSize;
38419 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38420 this.hideSplitter();
38425 getHMaxSize : function(){
38426 var cmax = this.config.maxSize || 10000;
38427 var center = this.mgr.getRegion("center");
38428 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38431 getVMaxSize : function(){
38432 var cmax = this.config.maxSize || 10000;
38433 var center = this.mgr.getRegion("center");
38434 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38437 onSplitMove : function(split, newSize){
38438 this.fireEvent("resized", this, newSize);
38442 * Returns the {@link Roo.SplitBar} for this region.
38443 * @return {Roo.SplitBar}
38445 getSplitBar : function(){
38450 this.hideSplitter();
38451 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38454 hideSplitter : function(){
38456 this.split.el.setLocation(-2000,-2000);
38457 this.split.el.hide();
38463 this.split.el.show();
38465 Roo.bootstrap.layout.Split.superclass.show.call(this);
38468 beforeSlide: function(){
38469 if(Roo.isGecko){// firefox overflow auto bug workaround
38470 this.bodyEl.clip();
38472 this.tabs.bodyEl.clip();
38474 if(this.activePanel){
38475 this.activePanel.getEl().clip();
38477 if(this.activePanel.beforeSlide){
38478 this.activePanel.beforeSlide();
38484 afterSlide : function(){
38485 if(Roo.isGecko){// firefox overflow auto bug workaround
38486 this.bodyEl.unclip();
38488 this.tabs.bodyEl.unclip();
38490 if(this.activePanel){
38491 this.activePanel.getEl().unclip();
38492 if(this.activePanel.afterSlide){
38493 this.activePanel.afterSlide();
38499 initAutoHide : function(){
38500 if(this.autoHide !== false){
38501 if(!this.autoHideHd){
38502 var st = new Roo.util.DelayedTask(this.slideIn, this);
38503 this.autoHideHd = {
38504 "mouseout": function(e){
38505 if(!e.within(this.el, true)){
38509 "mouseover" : function(e){
38515 this.el.on(this.autoHideHd);
38519 clearAutoHide : function(){
38520 if(this.autoHide !== false){
38521 this.el.un("mouseout", this.autoHideHd.mouseout);
38522 this.el.un("mouseover", this.autoHideHd.mouseover);
38526 clearMonitor : function(){
38527 Roo.get(document).un("click", this.slideInIf, this);
38530 // these names are backwards but not changed for compat
38531 slideOut : function(){
38532 if(this.isSlid || this.el.hasActiveFx()){
38535 this.isSlid = true;
38536 if(this.collapseBtn){
38537 this.collapseBtn.hide();
38539 this.closeBtnState = this.closeBtn.getStyle('display');
38540 this.closeBtn.hide();
38542 this.stickBtn.show();
38545 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38546 this.beforeSlide();
38547 this.el.setStyle("z-index", 10001);
38548 this.el.slideIn(this.getSlideAnchor(), {
38549 callback: function(){
38551 this.initAutoHide();
38552 Roo.get(document).on("click", this.slideInIf, this);
38553 this.fireEvent("slideshow", this);
38560 afterSlideIn : function(){
38561 this.clearAutoHide();
38562 this.isSlid = false;
38563 this.clearMonitor();
38564 this.el.setStyle("z-index", "");
38565 if(this.collapseBtn){
38566 this.collapseBtn.show();
38568 this.closeBtn.setStyle('display', this.closeBtnState);
38570 this.stickBtn.hide();
38572 this.fireEvent("slidehide", this);
38575 slideIn : function(cb){
38576 if(!this.isSlid || this.el.hasActiveFx()){
38580 this.isSlid = false;
38581 this.beforeSlide();
38582 this.el.slideOut(this.getSlideAnchor(), {
38583 callback: function(){
38584 this.el.setLeftTop(-10000, -10000);
38586 this.afterSlideIn();
38594 slideInIf : function(e){
38595 if(!e.within(this.el)){
38600 animateCollapse : function(){
38601 this.beforeSlide();
38602 this.el.setStyle("z-index", 20000);
38603 var anchor = this.getSlideAnchor();
38604 this.el.slideOut(anchor, {
38605 callback : function(){
38606 this.el.setStyle("z-index", "");
38607 this.collapsedEl.slideIn(anchor, {duration:.3});
38609 this.el.setLocation(-10000,-10000);
38611 this.fireEvent("collapsed", this);
38618 animateExpand : function(){
38619 this.beforeSlide();
38620 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38621 this.el.setStyle("z-index", 20000);
38622 this.collapsedEl.hide({
38625 this.el.slideIn(this.getSlideAnchor(), {
38626 callback : function(){
38627 this.el.setStyle("z-index", "");
38630 this.split.el.show();
38632 this.fireEvent("invalidated", this);
38633 this.fireEvent("expanded", this);
38661 getAnchor : function(){
38662 return this.anchors[this.position];
38665 getCollapseAnchor : function(){
38666 return this.canchors[this.position];
38669 getSlideAnchor : function(){
38670 return this.sanchors[this.position];
38673 getAlignAdj : function(){
38674 var cm = this.cmargins;
38675 switch(this.position){
38691 getExpandAdj : function(){
38692 var c = this.collapsedEl, cm = this.cmargins;
38693 switch(this.position){
38695 return [-(cm.right+c.getWidth()+cm.left), 0];
38698 return [cm.right+c.getWidth()+cm.left, 0];
38701 return [0, -(cm.top+cm.bottom+c.getHeight())];
38704 return [0, cm.top+cm.bottom+c.getHeight()];
38710 * Ext JS Library 1.1.1
38711 * Copyright(c) 2006-2007, Ext JS, LLC.
38713 * Originally Released Under LGPL - original licence link has changed is not relivant.
38716 * <script type="text/javascript">
38719 * These classes are private internal classes
38721 Roo.bootstrap.layout.Center = function(config){
38722 config.region = "center";
38723 Roo.bootstrap.layout.Region.call(this, config);
38724 this.visible = true;
38725 this.minWidth = config.minWidth || 20;
38726 this.minHeight = config.minHeight || 20;
38729 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38731 // center panel can't be hidden
38735 // center panel can't be hidden
38738 getMinWidth: function(){
38739 return this.minWidth;
38742 getMinHeight: function(){
38743 return this.minHeight;
38757 Roo.bootstrap.layout.North = function(config)
38759 config.region = 'north';
38760 config.cursor = 'n-resize';
38762 Roo.bootstrap.layout.Split.call(this, config);
38766 this.split.placement = Roo.bootstrap.SplitBar.TOP;
38767 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38768 this.split.el.addClass("roo-layout-split-v");
38770 var size = config.initialSize || config.height;
38771 if(typeof size != "undefined"){
38772 this.el.setHeight(size);
38775 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38777 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38781 getBox : function(){
38782 if(this.collapsed){
38783 return this.collapsedEl.getBox();
38785 var box = this.el.getBox();
38787 box.height += this.split.el.getHeight();
38792 updateBox : function(box){
38793 if(this.split && !this.collapsed){
38794 box.height -= this.split.el.getHeight();
38795 this.split.el.setLeft(box.x);
38796 this.split.el.setTop(box.y+box.height);
38797 this.split.el.setWidth(box.width);
38799 if(this.collapsed){
38800 this.updateBody(box.width, null);
38802 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38810 Roo.bootstrap.layout.South = function(config){
38811 config.region = 'south';
38812 config.cursor = 's-resize';
38813 Roo.bootstrap.layout.Split.call(this, config);
38815 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38816 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38817 this.split.el.addClass("roo-layout-split-v");
38819 var size = config.initialSize || config.height;
38820 if(typeof size != "undefined"){
38821 this.el.setHeight(size);
38825 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38826 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38827 getBox : function(){
38828 if(this.collapsed){
38829 return this.collapsedEl.getBox();
38831 var box = this.el.getBox();
38833 var sh = this.split.el.getHeight();
38840 updateBox : function(box){
38841 if(this.split && !this.collapsed){
38842 var sh = this.split.el.getHeight();
38845 this.split.el.setLeft(box.x);
38846 this.split.el.setTop(box.y-sh);
38847 this.split.el.setWidth(box.width);
38849 if(this.collapsed){
38850 this.updateBody(box.width, null);
38852 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38856 Roo.bootstrap.layout.East = function(config){
38857 config.region = "east";
38858 config.cursor = "e-resize";
38859 Roo.bootstrap.layout.Split.call(this, config);
38861 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38862 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38863 this.split.el.addClass("roo-layout-split-h");
38865 var size = config.initialSize || config.width;
38866 if(typeof size != "undefined"){
38867 this.el.setWidth(size);
38870 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38871 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38872 getBox : function(){
38873 if(this.collapsed){
38874 return this.collapsedEl.getBox();
38876 var box = this.el.getBox();
38878 var sw = this.split.el.getWidth();
38885 updateBox : function(box){
38886 if(this.split && !this.collapsed){
38887 var sw = this.split.el.getWidth();
38889 this.split.el.setLeft(box.x);
38890 this.split.el.setTop(box.y);
38891 this.split.el.setHeight(box.height);
38894 if(this.collapsed){
38895 this.updateBody(null, box.height);
38897 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38901 Roo.bootstrap.layout.West = function(config){
38902 config.region = "west";
38903 config.cursor = "w-resize";
38905 Roo.bootstrap.layout.Split.call(this, config);
38907 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38908 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38909 this.split.el.addClass("roo-layout-split-h");
38913 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38914 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38916 onRender: function(ctr, pos)
38918 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38919 var size = this.config.initialSize || this.config.width;
38920 if(typeof size != "undefined"){
38921 this.el.setWidth(size);
38925 getBox : function(){
38926 if(this.collapsed){
38927 return this.collapsedEl.getBox();
38929 var box = this.el.getBox();
38931 box.width += this.split.el.getWidth();
38936 updateBox : function(box){
38937 if(this.split && !this.collapsed){
38938 var sw = this.split.el.getWidth();
38940 this.split.el.setLeft(box.x+box.width);
38941 this.split.el.setTop(box.y);
38942 this.split.el.setHeight(box.height);
38944 if(this.collapsed){
38945 this.updateBody(null, box.height);
38947 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38949 });Roo.namespace("Roo.bootstrap.panel");/*
38951 * Ext JS Library 1.1.1
38952 * Copyright(c) 2006-2007, Ext JS, LLC.
38954 * Originally Released Under LGPL - original licence link has changed is not relivant.
38957 * <script type="text/javascript">
38960 * @class Roo.ContentPanel
38961 * @extends Roo.util.Observable
38962 * A basic ContentPanel element.
38963 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
38964 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
38965 * @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
38966 * @cfg {Boolean} closable True if the panel can be closed/removed
38967 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
38968 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38969 * @cfg {Toolbar} toolbar A toolbar for this panel
38970 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
38971 * @cfg {String} title The title for this panel
38972 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38973 * @cfg {String} url Calls {@link #setUrl} with this value
38974 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38975 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
38976 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
38977 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
38978 * @cfg {Boolean} badges render the badges
38981 * Create a new ContentPanel.
38982 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38983 * @param {String/Object} config A string to set only the title or a config object
38984 * @param {String} content (optional) Set the HTML content for this panel
38985 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38987 Roo.bootstrap.panel.Content = function( config){
38989 this.tpl = config.tpl || false;
38991 var el = config.el;
38992 var content = config.content;
38994 if(config.autoCreate){ // xtype is available if this is called from factory
38997 this.el = Roo.get(el);
38998 if(!this.el && config && config.autoCreate){
38999 if(typeof config.autoCreate == "object"){
39000 if(!config.autoCreate.id){
39001 config.autoCreate.id = config.id||el;
39003 this.el = Roo.DomHelper.append(document.body,
39004 config.autoCreate, true);
39006 var elcfg = { tag: "div",
39007 cls: "roo-layout-inactive-content",
39011 elcfg.html = config.html;
39015 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39018 this.closable = false;
39019 this.loaded = false;
39020 this.active = false;
39023 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39025 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39027 this.wrapEl = this.el; //this.el.wrap();
39029 if (config.toolbar.items) {
39030 ti = config.toolbar.items ;
39031 delete config.toolbar.items ;
39035 this.toolbar.render(this.wrapEl, 'before');
39036 for(var i =0;i < ti.length;i++) {
39037 // Roo.log(['add child', items[i]]);
39038 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39040 this.toolbar.items = nitems;
39041 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39042 delete config.toolbar;
39046 // xtype created footer. - not sure if will work as we normally have to render first..
39047 if (this.footer && !this.footer.el && this.footer.xtype) {
39048 if (!this.wrapEl) {
39049 this.wrapEl = this.el.wrap();
39052 this.footer.container = this.wrapEl.createChild();
39054 this.footer = Roo.factory(this.footer, Roo);
39059 if(typeof config == "string"){
39060 this.title = config;
39062 Roo.apply(this, config);
39066 this.resizeEl = Roo.get(this.resizeEl, true);
39068 this.resizeEl = this.el;
39070 // handle view.xtype
39078 * Fires when this panel is activated.
39079 * @param {Roo.ContentPanel} this
39083 * @event deactivate
39084 * Fires when this panel is activated.
39085 * @param {Roo.ContentPanel} this
39087 "deactivate" : true,
39091 * Fires when this panel is resized if fitToFrame is true.
39092 * @param {Roo.ContentPanel} this
39093 * @param {Number} width The width after any component adjustments
39094 * @param {Number} height The height after any component adjustments
39100 * Fires when this tab is created
39101 * @param {Roo.ContentPanel} this
39112 if(this.autoScroll){
39113 this.resizeEl.setStyle("overflow", "auto");
39115 // fix randome scrolling
39116 //this.el.on('scroll', function() {
39117 // Roo.log('fix random scolling');
39118 // this.scrollTo('top',0);
39121 content = content || this.content;
39123 this.setContent(content);
39125 if(config && config.url){
39126 this.setUrl(this.url, this.params, this.loadOnce);
39131 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39133 if (this.view && typeof(this.view.xtype) != 'undefined') {
39134 this.view.el = this.el.appendChild(document.createElement("div"));
39135 this.view = Roo.factory(this.view);
39136 this.view.render && this.view.render(false, '');
39140 this.fireEvent('render', this);
39143 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39147 setRegion : function(region){
39148 this.region = region;
39149 this.setActiveClass(region && !this.background);
39153 setActiveClass: function(state)
39156 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39157 this.el.setStyle('position','relative');
39159 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39160 this.el.setStyle('position', 'absolute');
39165 * Returns the toolbar for this Panel if one was configured.
39166 * @return {Roo.Toolbar}
39168 getToolbar : function(){
39169 return this.toolbar;
39172 setActiveState : function(active)
39174 this.active = active;
39175 this.setActiveClass(active);
39177 if(this.fireEvent("deactivate", this) === false){
39182 this.fireEvent("activate", this);
39186 * Updates this panel's element
39187 * @param {String} content The new content
39188 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39190 setContent : function(content, loadScripts){
39191 this.el.update(content, loadScripts);
39194 ignoreResize : function(w, h){
39195 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39198 this.lastSize = {width: w, height: h};
39203 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39204 * @return {Roo.UpdateManager} The UpdateManager
39206 getUpdateManager : function(){
39207 return this.el.getUpdateManager();
39210 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39211 * @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:
39214 url: "your-url.php",
39215 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39216 callback: yourFunction,
39217 scope: yourObject, //(optional scope)
39220 text: "Loading...",
39225 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39226 * 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.
39227 * @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}
39228 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39229 * @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.
39230 * @return {Roo.ContentPanel} this
39233 var um = this.el.getUpdateManager();
39234 um.update.apply(um, arguments);
39240 * 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.
39241 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39242 * @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)
39243 * @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)
39244 * @return {Roo.UpdateManager} The UpdateManager
39246 setUrl : function(url, params, loadOnce){
39247 if(this.refreshDelegate){
39248 this.removeListener("activate", this.refreshDelegate);
39250 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39251 this.on("activate", this.refreshDelegate);
39252 return this.el.getUpdateManager();
39255 _handleRefresh : function(url, params, loadOnce){
39256 if(!loadOnce || !this.loaded){
39257 var updater = this.el.getUpdateManager();
39258 updater.update(url, params, this._setLoaded.createDelegate(this));
39262 _setLoaded : function(){
39263 this.loaded = true;
39267 * Returns this panel's id
39270 getId : function(){
39275 * Returns this panel's element - used by regiosn to add.
39276 * @return {Roo.Element}
39278 getEl : function(){
39279 return this.wrapEl || this.el;
39284 adjustForComponents : function(width, height)
39286 //Roo.log('adjustForComponents ');
39287 if(this.resizeEl != this.el){
39288 width -= this.el.getFrameWidth('lr');
39289 height -= this.el.getFrameWidth('tb');
39292 var te = this.toolbar.getEl();
39293 te.setWidth(width);
39294 height -= te.getHeight();
39297 var te = this.footer.getEl();
39298 te.setWidth(width);
39299 height -= te.getHeight();
39303 if(this.adjustments){
39304 width += this.adjustments[0];
39305 height += this.adjustments[1];
39307 return {"width": width, "height": height};
39310 setSize : function(width, height){
39311 if(this.fitToFrame && !this.ignoreResize(width, height)){
39312 if(this.fitContainer && this.resizeEl != this.el){
39313 this.el.setSize(width, height);
39315 var size = this.adjustForComponents(width, height);
39316 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39317 this.fireEvent('resize', this, size.width, size.height);
39322 * Returns this panel's title
39325 getTitle : function(){
39327 if (typeof(this.title) != 'object') {
39332 for (var k in this.title) {
39333 if (!this.title.hasOwnProperty(k)) {
39337 if (k.indexOf('-') >= 0) {
39338 var s = k.split('-');
39339 for (var i = 0; i<s.length; i++) {
39340 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39343 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39350 * Set this panel's title
39351 * @param {String} title
39353 setTitle : function(title){
39354 this.title = title;
39356 this.region.updatePanelTitle(this, title);
39361 * Returns true is this panel was configured to be closable
39362 * @return {Boolean}
39364 isClosable : function(){
39365 return this.closable;
39368 beforeSlide : function(){
39370 this.resizeEl.clip();
39373 afterSlide : function(){
39375 this.resizeEl.unclip();
39379 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39380 * Will fail silently if the {@link #setUrl} method has not been called.
39381 * This does not activate the panel, just updates its content.
39383 refresh : function(){
39384 if(this.refreshDelegate){
39385 this.loaded = false;
39386 this.refreshDelegate();
39391 * Destroys this panel
39393 destroy : function(){
39394 this.el.removeAllListeners();
39395 var tempEl = document.createElement("span");
39396 tempEl.appendChild(this.el.dom);
39397 tempEl.innerHTML = "";
39403 * form - if the content panel contains a form - this is a reference to it.
39404 * @type {Roo.form.Form}
39408 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39409 * This contains a reference to it.
39415 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39425 * @param {Object} cfg Xtype definition of item to add.
39429 getChildContainer: function () {
39430 return this.getEl();
39435 var ret = new Roo.factory(cfg);
39440 if (cfg.xtype.match(/^Form$/)) {
39443 //if (this.footer) {
39444 // el = this.footer.container.insertSibling(false, 'before');
39446 el = this.el.createChild();
39449 this.form = new Roo.form.Form(cfg);
39452 if ( this.form.allItems.length) {
39453 this.form.render(el.dom);
39457 // should only have one of theses..
39458 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39459 // views.. should not be just added - used named prop 'view''
39461 cfg.el = this.el.appendChild(document.createElement("div"));
39464 var ret = new Roo.factory(cfg);
39466 ret.render && ret.render(false, ''); // render blank..
39476 * @class Roo.bootstrap.panel.Grid
39477 * @extends Roo.bootstrap.panel.Content
39479 * Create a new GridPanel.
39480 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39481 * @param {Object} config A the config object
39487 Roo.bootstrap.panel.Grid = function(config)
39491 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39492 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39494 config.el = this.wrapper;
39495 //this.el = this.wrapper;
39497 if (config.container) {
39498 // ctor'ed from a Border/panel.grid
39501 this.wrapper.setStyle("overflow", "hidden");
39502 this.wrapper.addClass('roo-grid-container');
39507 if(config.toolbar){
39508 var tool_el = this.wrapper.createChild();
39509 this.toolbar = Roo.factory(config.toolbar);
39511 if (config.toolbar.items) {
39512 ti = config.toolbar.items ;
39513 delete config.toolbar.items ;
39517 this.toolbar.render(tool_el);
39518 for(var i =0;i < ti.length;i++) {
39519 // Roo.log(['add child', items[i]]);
39520 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39522 this.toolbar.items = nitems;
39524 delete config.toolbar;
39527 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39528 config.grid.scrollBody = true;;
39529 config.grid.monitorWindowResize = false; // turn off autosizing
39530 config.grid.autoHeight = false;
39531 config.grid.autoWidth = false;
39533 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39535 if (config.background) {
39536 // render grid on panel activation (if panel background)
39537 this.on('activate', function(gp) {
39538 if (!gp.grid.rendered) {
39539 gp.grid.render(this.wrapper);
39540 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39545 this.grid.render(this.wrapper);
39546 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39549 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39550 // ??? needed ??? config.el = this.wrapper;
39555 // xtype created footer. - not sure if will work as we normally have to render first..
39556 if (this.footer && !this.footer.el && this.footer.xtype) {
39558 var ctr = this.grid.getView().getFooterPanel(true);
39559 this.footer.dataSource = this.grid.dataSource;
39560 this.footer = Roo.factory(this.footer, Roo);
39561 this.footer.render(ctr);
39571 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39572 getId : function(){
39573 return this.grid.id;
39577 * Returns the grid for this panel
39578 * @return {Roo.bootstrap.Table}
39580 getGrid : function(){
39584 setSize : function(width, height){
39585 if(!this.ignoreResize(width, height)){
39586 var grid = this.grid;
39587 var size = this.adjustForComponents(width, height);
39588 // tfoot is not a footer?
39591 var gridel = grid.getGridEl();
39592 gridel.setSize(size.width, size.height);
39594 var tbd = grid.getGridEl().select('tbody', true).first();
39595 var thd = grid.getGridEl().select('thead',true).first();
39596 var tbf= grid.getGridEl().select('tfoot', true).first();
39599 size.height -= thd.getHeight();
39602 size.height -= thd.getHeight();
39605 tbd.setSize(size.width, size.height );
39606 // this is for the account management tab -seems to work there.
39607 var thd = grid.getGridEl().select('thead',true).first();
39609 // tbd.setSize(size.width, size.height - thd.getHeight());
39618 beforeSlide : function(){
39619 this.grid.getView().scroller.clip();
39622 afterSlide : function(){
39623 this.grid.getView().scroller.unclip();
39626 destroy : function(){
39627 this.grid.destroy();
39629 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
39634 * @class Roo.bootstrap.panel.Nest
39635 * @extends Roo.bootstrap.panel.Content
39637 * Create a new Panel, that can contain a layout.Border.
39640 * @param {Roo.BorderLayout} layout The layout for this panel
39641 * @param {String/Object} config A string to set only the title or a config object
39643 Roo.bootstrap.panel.Nest = function(config)
39645 // construct with only one argument..
39646 /* FIXME - implement nicer consturctors
39647 if (layout.layout) {
39649 layout = config.layout;
39650 delete config.layout;
39652 if (layout.xtype && !layout.getEl) {
39653 // then layout needs constructing..
39654 layout = Roo.factory(layout, Roo);
39658 config.el = config.layout.getEl();
39660 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39662 config.layout.monitorWindowResize = false; // turn off autosizing
39663 this.layout = config.layout;
39664 this.layout.getEl().addClass("roo-layout-nested-layout");
39665 this.layout.parent = this;
39672 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39674 setSize : function(width, height){
39675 if(!this.ignoreResize(width, height)){
39676 var size = this.adjustForComponents(width, height);
39677 var el = this.layout.getEl();
39678 if (size.height < 1) {
39679 el.setWidth(size.width);
39681 el.setSize(size.width, size.height);
39683 var touch = el.dom.offsetWidth;
39684 this.layout.layout();
39685 // ie requires a double layout on the first pass
39686 if(Roo.isIE && !this.initialized){
39687 this.initialized = true;
39688 this.layout.layout();
39693 // activate all subpanels if not currently active..
39695 setActiveState : function(active){
39696 this.active = active;
39697 this.setActiveClass(active);
39700 this.fireEvent("deactivate", this);
39704 this.fireEvent("activate", this);
39705 // not sure if this should happen before or after..
39706 if (!this.layout) {
39707 return; // should not happen..
39710 for (var r in this.layout.regions) {
39711 reg = this.layout.getRegion(r);
39712 if (reg.getActivePanel()) {
39713 //reg.showPanel(reg.getActivePanel()); // force it to activate..
39714 reg.setActivePanel(reg.getActivePanel());
39717 if (!reg.panels.length) {
39720 reg.showPanel(reg.getPanel(0));
39729 * Returns the nested BorderLayout for this panel
39730 * @return {Roo.BorderLayout}
39732 getLayout : function(){
39733 return this.layout;
39737 * Adds a xtype elements to the layout of the nested panel
39741 xtype : 'ContentPanel',
39748 xtype : 'NestedLayoutPanel',
39754 items : [ ... list of content panels or nested layout panels.. ]
39758 * @param {Object} cfg Xtype definition of item to add.
39760 addxtype : function(cfg) {
39761 return this.layout.addxtype(cfg);
39766 * Ext JS Library 1.1.1
39767 * Copyright(c) 2006-2007, Ext JS, LLC.
39769 * Originally Released Under LGPL - original licence link has changed is not relivant.
39772 * <script type="text/javascript">
39775 * @class Roo.TabPanel
39776 * @extends Roo.util.Observable
39777 * A lightweight tab container.
39781 // basic tabs 1, built from existing content
39782 var tabs = new Roo.TabPanel("tabs1");
39783 tabs.addTab("script", "View Script");
39784 tabs.addTab("markup", "View Markup");
39785 tabs.activate("script");
39787 // more advanced tabs, built from javascript
39788 var jtabs = new Roo.TabPanel("jtabs");
39789 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39791 // set up the UpdateManager
39792 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39793 var updater = tab2.getUpdateManager();
39794 updater.setDefaultUrl("ajax1.htm");
39795 tab2.on('activate', updater.refresh, updater, true);
39797 // Use setUrl for Ajax loading
39798 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39799 tab3.setUrl("ajax2.htm", null, true);
39802 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39805 jtabs.activate("jtabs-1");
39808 * Create a new TabPanel.
39809 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39810 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39812 Roo.bootstrap.panel.Tabs = function(config){
39814 * The container element for this TabPanel.
39815 * @type Roo.Element
39817 this.el = Roo.get(config.el);
39820 if(typeof config == "boolean"){
39821 this.tabPosition = config ? "bottom" : "top";
39823 Roo.apply(this, config);
39827 if(this.tabPosition == "bottom"){
39828 // if tabs are at the bottom = create the body first.
39829 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39830 this.el.addClass("roo-tabs-bottom");
39832 // next create the tabs holders
39834 if (this.tabPosition == "west"){
39836 var reg = this.region; // fake it..
39838 if (!reg.mgr.parent) {
39841 reg = reg.mgr.parent.region;
39843 Roo.log("got nest?");
39845 if (reg.mgr.getRegion('west')) {
39846 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39847 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39848 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39849 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39850 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39858 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39859 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39860 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39861 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39866 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39869 // finally - if tabs are at the top, then create the body last..
39870 if(this.tabPosition != "bottom"){
39871 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39872 * @type Roo.Element
39874 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39875 this.el.addClass("roo-tabs-top");
39879 this.bodyEl.setStyle("position", "relative");
39881 this.active = null;
39882 this.activateDelegate = this.activate.createDelegate(this);
39887 * Fires when the active tab changes
39888 * @param {Roo.TabPanel} this
39889 * @param {Roo.TabPanelItem} activePanel The new active tab
39893 * @event beforetabchange
39894 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39895 * @param {Roo.TabPanel} this
39896 * @param {Object} e Set cancel to true on this object to cancel the tab change
39897 * @param {Roo.TabPanelItem} tab The tab being changed to
39899 "beforetabchange" : true
39902 Roo.EventManager.onWindowResize(this.onResize, this);
39903 this.cpad = this.el.getPadding("lr");
39904 this.hiddenCount = 0;
39907 // toolbar on the tabbar support...
39908 if (this.toolbar) {
39909 alert("no toolbar support yet");
39910 this.toolbar = false;
39912 var tcfg = this.toolbar;
39913 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
39914 this.toolbar = new Roo.Toolbar(tcfg);
39915 if (Roo.isSafari) {
39916 var tbl = tcfg.container.child('table', true);
39917 tbl.setAttribute('width', '100%');
39925 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39928 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39930 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39932 tabPosition : "top",
39934 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39936 currentTabWidth : 0,
39938 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39942 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39946 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39948 preferredTabWidth : 175,
39950 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39952 resizeTabs : false,
39954 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39956 monitorResize : true,
39958 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
39960 toolbar : false, // set by caller..
39962 region : false, /// set by caller
39964 disableTooltips : true, // not used yet...
39967 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39968 * @param {String} id The id of the div to use <b>or create</b>
39969 * @param {String} text The text for the tab
39970 * @param {String} content (optional) Content to put in the TabPanelItem body
39971 * @param {Boolean} closable (optional) True to create a close icon on the tab
39972 * @return {Roo.TabPanelItem} The created TabPanelItem
39974 addTab : function(id, text, content, closable, tpl)
39976 var item = new Roo.bootstrap.panel.TabItem({
39980 closable : closable,
39983 this.addTabItem(item);
39985 item.setContent(content);
39991 * Returns the {@link Roo.TabPanelItem} with the specified id/index
39992 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39993 * @return {Roo.TabPanelItem}
39995 getTab : function(id){
39996 return this.items[id];
40000 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40001 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40003 hideTab : function(id){
40004 var t = this.items[id];
40007 this.hiddenCount++;
40008 this.autoSizeTabs();
40013 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40014 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40016 unhideTab : function(id){
40017 var t = this.items[id];
40019 t.setHidden(false);
40020 this.hiddenCount--;
40021 this.autoSizeTabs();
40026 * Adds an existing {@link Roo.TabPanelItem}.
40027 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40029 addTabItem : function(item)
40031 this.items[item.id] = item;
40032 this.items.push(item);
40033 this.autoSizeTabs();
40034 // if(this.resizeTabs){
40035 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40036 // this.autoSizeTabs();
40038 // item.autoSize();
40043 * Removes a {@link Roo.TabPanelItem}.
40044 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40046 removeTab : function(id){
40047 var items = this.items;
40048 var tab = items[id];
40049 if(!tab) { return; }
40050 var index = items.indexOf(tab);
40051 if(this.active == tab && items.length > 1){
40052 var newTab = this.getNextAvailable(index);
40057 this.stripEl.dom.removeChild(tab.pnode.dom);
40058 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40059 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40061 items.splice(index, 1);
40062 delete this.items[tab.id];
40063 tab.fireEvent("close", tab);
40064 tab.purgeListeners();
40065 this.autoSizeTabs();
40068 getNextAvailable : function(start){
40069 var items = this.items;
40071 // look for a next tab that will slide over to
40072 // replace the one being removed
40073 while(index < items.length){
40074 var item = items[++index];
40075 if(item && !item.isHidden()){
40079 // if one isn't found select the previous tab (on the left)
40082 var item = items[--index];
40083 if(item && !item.isHidden()){
40091 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40092 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40094 disableTab : function(id){
40095 var tab = this.items[id];
40096 if(tab && this.active != tab){
40102 * Enables a {@link Roo.TabPanelItem} that is disabled.
40103 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40105 enableTab : function(id){
40106 var tab = this.items[id];
40111 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40112 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40113 * @return {Roo.TabPanelItem} The TabPanelItem.
40115 activate : function(id)
40117 //Roo.log('activite:' + id);
40119 var tab = this.items[id];
40123 if(tab == this.active || tab.disabled){
40127 this.fireEvent("beforetabchange", this, e, tab);
40128 if(e.cancel !== true && !tab.disabled){
40130 this.active.hide();
40132 this.active = this.items[id];
40133 this.active.show();
40134 this.fireEvent("tabchange", this, this.active);
40140 * Gets the active {@link Roo.TabPanelItem}.
40141 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40143 getActiveTab : function(){
40144 return this.active;
40148 * Updates the tab body element to fit the height of the container element
40149 * for overflow scrolling
40150 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40152 syncHeight : function(targetHeight){
40153 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40154 var bm = this.bodyEl.getMargins();
40155 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40156 this.bodyEl.setHeight(newHeight);
40160 onResize : function(){
40161 if(this.monitorResize){
40162 this.autoSizeTabs();
40167 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40169 beginUpdate : function(){
40170 this.updating = true;
40174 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40176 endUpdate : function(){
40177 this.updating = false;
40178 this.autoSizeTabs();
40182 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40184 autoSizeTabs : function()
40186 var count = this.items.length;
40187 var vcount = count - this.hiddenCount;
40190 this.stripEl.hide();
40192 this.stripEl.show();
40195 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40200 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40201 var availWidth = Math.floor(w / vcount);
40202 var b = this.stripBody;
40203 if(b.getWidth() > w){
40204 var tabs = this.items;
40205 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40206 if(availWidth < this.minTabWidth){
40207 /*if(!this.sleft){ // incomplete scrolling code
40208 this.createScrollButtons();
40211 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40214 if(this.currentTabWidth < this.preferredTabWidth){
40215 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40221 * Returns the number of tabs in this TabPanel.
40224 getCount : function(){
40225 return this.items.length;
40229 * Resizes all the tabs to the passed width
40230 * @param {Number} The new width
40232 setTabWidth : function(width){
40233 this.currentTabWidth = width;
40234 for(var i = 0, len = this.items.length; i < len; i++) {
40235 if(!this.items[i].isHidden()) {
40236 this.items[i].setWidth(width);
40242 * Destroys this TabPanel
40243 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40245 destroy : function(removeEl){
40246 Roo.EventManager.removeResizeListener(this.onResize, this);
40247 for(var i = 0, len = this.items.length; i < len; i++){
40248 this.items[i].purgeListeners();
40250 if(removeEl === true){
40251 this.el.update("");
40256 createStrip : function(container)
40258 var strip = document.createElement("nav");
40259 strip.className = Roo.bootstrap.version == 4 ?
40260 "navbar-light bg-light" :
40261 "navbar navbar-default"; //"x-tabs-wrap";
40262 container.appendChild(strip);
40266 createStripList : function(strip)
40268 // div wrapper for retard IE
40269 // returns the "tr" element.
40270 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40271 //'<div class="x-tabs-strip-wrap">'+
40272 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40273 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40274 return strip.firstChild; //.firstChild.firstChild.firstChild;
40276 createBody : function(container)
40278 var body = document.createElement("div");
40279 Roo.id(body, "tab-body");
40280 //Roo.fly(body).addClass("x-tabs-body");
40281 Roo.fly(body).addClass("tab-content");
40282 container.appendChild(body);
40285 createItemBody :function(bodyEl, id){
40286 var body = Roo.getDom(id);
40288 body = document.createElement("div");
40291 //Roo.fly(body).addClass("x-tabs-item-body");
40292 Roo.fly(body).addClass("tab-pane");
40293 bodyEl.insertBefore(body, bodyEl.firstChild);
40297 createStripElements : function(stripEl, text, closable, tpl)
40299 var td = document.createElement("li"); // was td..
40300 td.className = 'nav-item';
40302 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40305 stripEl.appendChild(td);
40307 td.className = "x-tabs-closable";
40308 if(!this.closeTpl){
40309 this.closeTpl = new Roo.Template(
40310 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40311 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40312 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40315 var el = this.closeTpl.overwrite(td, {"text": text});
40316 var close = el.getElementsByTagName("div")[0];
40317 var inner = el.getElementsByTagName("em")[0];
40318 return {"el": el, "close": close, "inner": inner};
40321 // not sure what this is..
40322 // if(!this.tabTpl){
40323 //this.tabTpl = new Roo.Template(
40324 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40325 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40327 // this.tabTpl = new Roo.Template(
40328 // '<a href="#">' +
40329 // '<span unselectable="on"' +
40330 // (this.disableTooltips ? '' : ' title="{text}"') +
40331 // ' >{text}</span></a>'
40337 var template = tpl || this.tabTpl || false;
40340 template = new Roo.Template(
40341 Roo.bootstrap.version == 4 ?
40343 '<a class="nav-link" href="#" unselectable="on"' +
40344 (this.disableTooltips ? '' : ' title="{text}"') +
40347 '<a class="nav-link" href="#">' +
40348 '<span unselectable="on"' +
40349 (this.disableTooltips ? '' : ' title="{text}"') +
40350 ' >{text}</span></a>'
40355 switch (typeof(template)) {
40359 template = new Roo.Template(template);
40365 var el = template.overwrite(td, {"text": text});
40367 var inner = el.getElementsByTagName("span")[0];
40369 return {"el": el, "inner": inner};
40377 * @class Roo.TabPanelItem
40378 * @extends Roo.util.Observable
40379 * Represents an individual item (tab plus body) in a TabPanel.
40380 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40381 * @param {String} id The id of this TabPanelItem
40382 * @param {String} text The text for the tab of this TabPanelItem
40383 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40385 Roo.bootstrap.panel.TabItem = function(config){
40387 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40388 * @type Roo.TabPanel
40390 this.tabPanel = config.panel;
40392 * The id for this TabPanelItem
40395 this.id = config.id;
40397 this.disabled = false;
40399 this.text = config.text;
40401 this.loaded = false;
40402 this.closable = config.closable;
40405 * The body element for this TabPanelItem.
40406 * @type Roo.Element
40408 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40409 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40410 this.bodyEl.setStyle("display", "block");
40411 this.bodyEl.setStyle("zoom", "1");
40412 //this.hideAction();
40414 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40416 this.el = Roo.get(els.el);
40417 this.inner = Roo.get(els.inner, true);
40418 this.textEl = Roo.bootstrap.version == 4 ?
40419 this.el : Roo.get(this.el.dom.firstChild, true);
40421 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40422 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40425 // this.el.on("mousedown", this.onTabMouseDown, this);
40426 this.el.on("click", this.onTabClick, this);
40428 if(config.closable){
40429 var c = Roo.get(els.close, true);
40430 c.dom.title = this.closeText;
40431 c.addClassOnOver("close-over");
40432 c.on("click", this.closeClick, this);
40438 * Fires when this tab becomes the active tab.
40439 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40440 * @param {Roo.TabPanelItem} this
40444 * @event beforeclose
40445 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40446 * @param {Roo.TabPanelItem} this
40447 * @param {Object} e Set cancel to true on this object to cancel the close.
40449 "beforeclose": true,
40452 * Fires when this tab is closed.
40453 * @param {Roo.TabPanelItem} this
40457 * @event deactivate
40458 * Fires when this tab is no longer the active tab.
40459 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40460 * @param {Roo.TabPanelItem} this
40462 "deactivate" : true
40464 this.hidden = false;
40466 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40469 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40471 purgeListeners : function(){
40472 Roo.util.Observable.prototype.purgeListeners.call(this);
40473 this.el.removeAllListeners();
40476 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40479 this.status_node.addClass("active");
40482 this.tabPanel.stripWrap.repaint();
40484 this.fireEvent("activate", this.tabPanel, this);
40488 * Returns true if this tab is the active tab.
40489 * @return {Boolean}
40491 isActive : function(){
40492 return this.tabPanel.getActiveTab() == this;
40496 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40499 this.status_node.removeClass("active");
40501 this.fireEvent("deactivate", this.tabPanel, this);
40504 hideAction : function(){
40505 this.bodyEl.hide();
40506 this.bodyEl.setStyle("position", "absolute");
40507 this.bodyEl.setLeft("-20000px");
40508 this.bodyEl.setTop("-20000px");
40511 showAction : function(){
40512 this.bodyEl.setStyle("position", "relative");
40513 this.bodyEl.setTop("");
40514 this.bodyEl.setLeft("");
40515 this.bodyEl.show();
40519 * Set the tooltip for the tab.
40520 * @param {String} tooltip The tab's tooltip
40522 setTooltip : function(text){
40523 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40524 this.textEl.dom.qtip = text;
40525 this.textEl.dom.removeAttribute('title');
40527 this.textEl.dom.title = text;
40531 onTabClick : function(e){
40532 e.preventDefault();
40533 this.tabPanel.activate(this.id);
40536 onTabMouseDown : function(e){
40537 e.preventDefault();
40538 this.tabPanel.activate(this.id);
40541 getWidth : function(){
40542 return this.inner.getWidth();
40545 setWidth : function(width){
40546 var iwidth = width - this.linode.getPadding("lr");
40547 this.inner.setWidth(iwidth);
40548 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40549 this.linode.setWidth(width);
40553 * Show or hide the tab
40554 * @param {Boolean} hidden True to hide or false to show.
40556 setHidden : function(hidden){
40557 this.hidden = hidden;
40558 this.linode.setStyle("display", hidden ? "none" : "");
40562 * Returns true if this tab is "hidden"
40563 * @return {Boolean}
40565 isHidden : function(){
40566 return this.hidden;
40570 * Returns the text for this tab
40573 getText : function(){
40577 autoSize : function(){
40578 //this.el.beginMeasure();
40579 this.textEl.setWidth(1);
40581 * #2804 [new] Tabs in Roojs
40582 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40584 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40585 //this.el.endMeasure();
40589 * Sets the text for the tab (Note: this also sets the tooltip text)
40590 * @param {String} text The tab's text and tooltip
40592 setText : function(text){
40594 this.textEl.update(text);
40595 this.setTooltip(text);
40596 //if(!this.tabPanel.resizeTabs){
40597 // this.autoSize();
40601 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40603 activate : function(){
40604 this.tabPanel.activate(this.id);
40608 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40610 disable : function(){
40611 if(this.tabPanel.active != this){
40612 this.disabled = true;
40613 this.status_node.addClass("disabled");
40618 * Enables this TabPanelItem if it was previously disabled.
40620 enable : function(){
40621 this.disabled = false;
40622 this.status_node.removeClass("disabled");
40626 * Sets the content for this TabPanelItem.
40627 * @param {String} content The content
40628 * @param {Boolean} loadScripts true to look for and load scripts
40630 setContent : function(content, loadScripts){
40631 this.bodyEl.update(content, loadScripts);
40635 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40636 * @return {Roo.UpdateManager} The UpdateManager
40638 getUpdateManager : function(){
40639 return this.bodyEl.getUpdateManager();
40643 * Set a URL to be used to load the content for this TabPanelItem.
40644 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40645 * @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)
40646 * @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)
40647 * @return {Roo.UpdateManager} The UpdateManager
40649 setUrl : function(url, params, loadOnce){
40650 if(this.refreshDelegate){
40651 this.un('activate', this.refreshDelegate);
40653 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40654 this.on("activate", this.refreshDelegate);
40655 return this.bodyEl.getUpdateManager();
40659 _handleRefresh : function(url, params, loadOnce){
40660 if(!loadOnce || !this.loaded){
40661 var updater = this.bodyEl.getUpdateManager();
40662 updater.update(url, params, this._setLoaded.createDelegate(this));
40667 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40668 * Will fail silently if the setUrl method has not been called.
40669 * This does not activate the panel, just updates its content.
40671 refresh : function(){
40672 if(this.refreshDelegate){
40673 this.loaded = false;
40674 this.refreshDelegate();
40679 _setLoaded : function(){
40680 this.loaded = true;
40684 closeClick : function(e){
40687 this.fireEvent("beforeclose", this, o);
40688 if(o.cancel !== true){
40689 this.tabPanel.removeTab(this.id);
40693 * The text displayed in the tooltip for the close icon.
40696 closeText : "Close this tab"
40699 * This script refer to:
40700 * Title: International Telephone Input
40701 * Author: Jack O'Connor
40702 * Code version: v12.1.12
40703 * Availability: https://github.com/jackocnr/intl-tel-input.git
40706 Roo.bootstrap.PhoneInputData = function() {
40709 "Afghanistan (افغانستان)",
40714 "Albania (Shqipëri)",
40719 "Algeria (الجزائر)",
40744 "Antigua and Barbuda",
40754 "Armenia (Հայաստան)",
40770 "Austria (Österreich)",
40775 "Azerbaijan (Azərbaycan)",
40785 "Bahrain (البحرين)",
40790 "Bangladesh (বাংলাদেশ)",
40800 "Belarus (Беларусь)",
40805 "Belgium (België)",
40835 "Bosnia and Herzegovina (Босна и Херцеговина)",
40850 "British Indian Ocean Territory",
40855 "British Virgin Islands",
40865 "Bulgaria (България)",
40875 "Burundi (Uburundi)",
40880 "Cambodia (កម្ពុជា)",
40885 "Cameroon (Cameroun)",
40894 ["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"]
40897 "Cape Verde (Kabu Verdi)",
40902 "Caribbean Netherlands",
40913 "Central African Republic (République centrafricaine)",
40933 "Christmas Island",
40939 "Cocos (Keeling) Islands",
40950 "Comoros (جزر القمر)",
40955 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40960 "Congo (Republic) (Congo-Brazzaville)",
40980 "Croatia (Hrvatska)",
41001 "Czech Republic (Česká republika)",
41006 "Denmark (Danmark)",
41021 "Dominican Republic (República Dominicana)",
41025 ["809", "829", "849"]
41043 "Equatorial Guinea (Guinea Ecuatorial)",
41063 "Falkland Islands (Islas Malvinas)",
41068 "Faroe Islands (Føroyar)",
41089 "French Guiana (Guyane française)",
41094 "French Polynesia (Polynésie française)",
41109 "Georgia (საქართველო)",
41114 "Germany (Deutschland)",
41134 "Greenland (Kalaallit Nunaat)",
41171 "Guinea-Bissau (Guiné Bissau)",
41196 "Hungary (Magyarország)",
41201 "Iceland (Ísland)",
41221 "Iraq (العراق)",
41237 "Israel (ישראל)",
41264 "Jordan (الأردن)",
41269 "Kazakhstan (Казахстан)",
41290 "Kuwait (الكويت)",
41295 "Kyrgyzstan (Кыргызстан)",
41305 "Latvia (Latvija)",
41310 "Lebanon (لبنان)",
41325 "Libya (ليبيا)",
41335 "Lithuania (Lietuva)",
41350 "Macedonia (FYROM) (Македонија)",
41355 "Madagascar (Madagasikara)",
41385 "Marshall Islands",
41395 "Mauritania (موريتانيا)",
41400 "Mauritius (Moris)",
41421 "Moldova (Republica Moldova)",
41431 "Mongolia (Монгол)",
41436 "Montenegro (Crna Gora)",
41446 "Morocco (المغرب)",
41452 "Mozambique (Moçambique)",
41457 "Myanmar (Burma) (မြန်မာ)",
41462 "Namibia (Namibië)",
41477 "Netherlands (Nederland)",
41482 "New Caledonia (Nouvelle-Calédonie)",
41517 "North Korea (조선 민주주의 인민 공화국)",
41522 "Northern Mariana Islands",
41538 "Pakistan (پاکستان)",
41548 "Palestine (فلسطين)",
41558 "Papua New Guinea",
41600 "Réunion (La Réunion)",
41606 "Romania (România)",
41622 "Saint Barthélemy",
41633 "Saint Kitts and Nevis",
41643 "Saint Martin (Saint-Martin (partie française))",
41649 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41654 "Saint Vincent and the Grenadines",
41669 "São Tomé and Príncipe (São Tomé e Príncipe)",
41674 "Saudi Arabia (المملكة العربية السعودية)",
41679 "Senegal (Sénégal)",
41709 "Slovakia (Slovensko)",
41714 "Slovenia (Slovenija)",
41724 "Somalia (Soomaaliya)",
41734 "South Korea (대한민국)",
41739 "South Sudan (جنوب السودان)",
41749 "Sri Lanka (ශ්රී ලංකාව)",
41754 "Sudan (السودان)",
41764 "Svalbard and Jan Mayen",
41775 "Sweden (Sverige)",
41780 "Switzerland (Schweiz)",
41785 "Syria (سوريا)",
41830 "Trinidad and Tobago",
41835 "Tunisia (تونس)",
41840 "Turkey (Türkiye)",
41850 "Turks and Caicos Islands",
41860 "U.S. Virgin Islands",
41870 "Ukraine (Україна)",
41875 "United Arab Emirates (الإمارات العربية المتحدة)",
41897 "Uzbekistan (Oʻzbekiston)",
41907 "Vatican City (Città del Vaticano)",
41918 "Vietnam (Việt Nam)",
41923 "Wallis and Futuna (Wallis-et-Futuna)",
41928 "Western Sahara (الصحراء الغربية)",
41934 "Yemen (اليمن)",
41958 * This script refer to:
41959 * Title: International Telephone Input
41960 * Author: Jack O'Connor
41961 * Code version: v12.1.12
41962 * Availability: https://github.com/jackocnr/intl-tel-input.git
41966 * @class Roo.bootstrap.PhoneInput
41967 * @extends Roo.bootstrap.TriggerField
41968 * An input with International dial-code selection
41970 * @cfg {String} defaultDialCode default '+852'
41971 * @cfg {Array} preferedCountries default []
41974 * Create a new PhoneInput.
41975 * @param {Object} config Configuration options
41978 Roo.bootstrap.PhoneInput = function(config) {
41979 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41982 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41984 listWidth: undefined,
41986 selectedClass: 'active',
41988 invalidClass : "has-warning",
41990 validClass: 'has-success',
41992 allowed: '0123456789',
41997 * @cfg {String} defaultDialCode The default dial code when initializing the input
41999 defaultDialCode: '+852',
42002 * @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
42004 preferedCountries: false,
42006 getAutoCreate : function()
42008 var data = Roo.bootstrap.PhoneInputData();
42009 var align = this.labelAlign || this.parentLabelAlign();
42012 this.allCountries = [];
42013 this.dialCodeMapping = [];
42015 for (var i = 0; i < data.length; i++) {
42017 this.allCountries[i] = {
42021 priority: c[3] || 0,
42022 areaCodes: c[4] || null
42024 this.dialCodeMapping[c[2]] = {
42027 priority: c[3] || 0,
42028 areaCodes: c[4] || null
42040 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42041 maxlength: this.max_length,
42042 cls : 'form-control tel-input',
42043 autocomplete: 'new-password'
42046 var hiddenInput = {
42049 cls: 'hidden-tel-input'
42053 hiddenInput.name = this.name;
42056 if (this.disabled) {
42057 input.disabled = true;
42060 var flag_container = {
42077 cls: this.hasFeedback ? 'has-feedback' : '',
42083 cls: 'dial-code-holder',
42090 cls: 'roo-select2-container input-group',
42097 if (this.fieldLabel.length) {
42100 tooltip: 'This field is required'
42106 cls: 'control-label',
42112 html: this.fieldLabel
42115 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42121 if(this.indicatorpos == 'right') {
42122 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42129 if(align == 'left') {
42137 if(this.labelWidth > 12){
42138 label.style = "width: " + this.labelWidth + 'px';
42140 if(this.labelWidth < 13 && this.labelmd == 0){
42141 this.labelmd = this.labelWidth;
42143 if(this.labellg > 0){
42144 label.cls += ' col-lg-' + this.labellg;
42145 input.cls += ' col-lg-' + (12 - this.labellg);
42147 if(this.labelmd > 0){
42148 label.cls += ' col-md-' + this.labelmd;
42149 container.cls += ' col-md-' + (12 - this.labelmd);
42151 if(this.labelsm > 0){
42152 label.cls += ' col-sm-' + this.labelsm;
42153 container.cls += ' col-sm-' + (12 - this.labelsm);
42155 if(this.labelxs > 0){
42156 label.cls += ' col-xs-' + this.labelxs;
42157 container.cls += ' col-xs-' + (12 - this.labelxs);
42167 var settings = this;
42169 ['xs','sm','md','lg'].map(function(size){
42170 if (settings[size]) {
42171 cfg.cls += ' col-' + size + '-' + settings[size];
42175 this.store = new Roo.data.Store({
42176 proxy : new Roo.data.MemoryProxy({}),
42177 reader : new Roo.data.JsonReader({
42188 'name' : 'dialCode',
42192 'name' : 'priority',
42196 'name' : 'areaCodes',
42203 if(!this.preferedCountries) {
42204 this.preferedCountries = [
42211 var p = this.preferedCountries.reverse();
42214 for (var i = 0; i < p.length; i++) {
42215 for (var j = 0; j < this.allCountries.length; j++) {
42216 if(this.allCountries[j].iso2 == p[i]) {
42217 var t = this.allCountries[j];
42218 this.allCountries.splice(j,1);
42219 this.allCountries.unshift(t);
42225 this.store.proxy.data = {
42227 data: this.allCountries
42233 initEvents : function()
42236 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42238 this.indicator = this.indicatorEl();
42239 this.flag = this.flagEl();
42240 this.dialCodeHolder = this.dialCodeHolderEl();
42242 this.trigger = this.el.select('div.flag-box',true).first();
42243 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42248 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42249 _this.list.setWidth(lw);
42252 this.list.on('mouseover', this.onViewOver, this);
42253 this.list.on('mousemove', this.onViewMove, this);
42254 this.inputEl().on("keyup", this.onKeyUp, this);
42255 this.inputEl().on("keypress", this.onKeyPress, this);
42257 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42259 this.view = new Roo.View(this.list, this.tpl, {
42260 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42263 this.view.on('click', this.onViewClick, this);
42264 this.setValue(this.defaultDialCode);
42267 onTriggerClick : function(e)
42269 Roo.log('trigger click');
42274 if(this.isExpanded()){
42276 this.hasFocus = false;
42278 this.store.load({});
42279 this.hasFocus = true;
42284 isExpanded : function()
42286 return this.list.isVisible();
42289 collapse : function()
42291 if(!this.isExpanded()){
42295 Roo.get(document).un('mousedown', this.collapseIf, this);
42296 Roo.get(document).un('mousewheel', this.collapseIf, this);
42297 this.fireEvent('collapse', this);
42301 expand : function()
42305 if(this.isExpanded() || !this.hasFocus){
42309 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42310 this.list.setWidth(lw);
42313 this.restrictHeight();
42315 Roo.get(document).on('mousedown', this.collapseIf, this);
42316 Roo.get(document).on('mousewheel', this.collapseIf, this);
42318 this.fireEvent('expand', this);
42321 restrictHeight : function()
42323 this.list.alignTo(this.inputEl(), this.listAlign);
42324 this.list.alignTo(this.inputEl(), this.listAlign);
42327 onViewOver : function(e, t)
42329 if(this.inKeyMode){
42332 var item = this.view.findItemFromChild(t);
42335 var index = this.view.indexOf(item);
42336 this.select(index, false);
42341 onViewClick : function(view, doFocus, el, e)
42343 var index = this.view.getSelectedIndexes()[0];
42345 var r = this.store.getAt(index);
42348 this.onSelect(r, index);
42350 if(doFocus !== false && !this.blockFocus){
42351 this.inputEl().focus();
42355 onViewMove : function(e, t)
42357 this.inKeyMode = false;
42360 select : function(index, scrollIntoView)
42362 this.selectedIndex = index;
42363 this.view.select(index);
42364 if(scrollIntoView !== false){
42365 var el = this.view.getNode(index);
42367 this.list.scrollChildIntoView(el, false);
42372 createList : function()
42374 this.list = Roo.get(document.body).createChild({
42376 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42377 style: 'display:none'
42380 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42383 collapseIf : function(e)
42385 var in_combo = e.within(this.el);
42386 var in_list = e.within(this.list);
42387 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42389 if (in_combo || in_list || is_list) {
42395 onSelect : function(record, index)
42397 if(this.fireEvent('beforeselect', this, record, index) !== false){
42399 this.setFlagClass(record.data.iso2);
42400 this.setDialCode(record.data.dialCode);
42401 this.hasFocus = false;
42403 this.fireEvent('select', this, record, index);
42407 flagEl : function()
42409 var flag = this.el.select('div.flag',true).first();
42416 dialCodeHolderEl : function()
42418 var d = this.el.select('input.dial-code-holder',true).first();
42425 setDialCode : function(v)
42427 this.dialCodeHolder.dom.value = '+'+v;
42430 setFlagClass : function(n)
42432 this.flag.dom.className = 'flag '+n;
42435 getValue : function()
42437 var v = this.inputEl().getValue();
42438 if(this.dialCodeHolder) {
42439 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42444 setValue : function(v)
42446 var d = this.getDialCode(v);
42448 //invalid dial code
42449 if(v.length == 0 || !d || d.length == 0) {
42451 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42452 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42458 this.setFlagClass(this.dialCodeMapping[d].iso2);
42459 this.setDialCode(d);
42460 this.inputEl().dom.value = v.replace('+'+d,'');
42461 this.hiddenEl().dom.value = this.getValue();
42466 getDialCode : function(v)
42470 if (v.length == 0) {
42471 return this.dialCodeHolder.dom.value;
42475 if (v.charAt(0) != "+") {
42478 var numericChars = "";
42479 for (var i = 1; i < v.length; i++) {
42480 var c = v.charAt(i);
42483 if (this.dialCodeMapping[numericChars]) {
42484 dialCode = v.substr(1, i);
42486 if (numericChars.length == 4) {
42496 this.setValue(this.defaultDialCode);
42500 hiddenEl : function()
42502 return this.el.select('input.hidden-tel-input',true).first();
42505 // after setting val
42506 onKeyUp : function(e){
42507 this.setValue(this.getValue());
42510 onKeyPress : function(e){
42511 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42518 * @class Roo.bootstrap.MoneyField
42519 * @extends Roo.bootstrap.ComboBox
42520 * Bootstrap MoneyField class
42523 * Create a new MoneyField.
42524 * @param {Object} config Configuration options
42527 Roo.bootstrap.MoneyField = function(config) {
42529 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42533 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42536 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42538 allowDecimals : true,
42540 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42542 decimalSeparator : ".",
42544 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42546 decimalPrecision : 0,
42548 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42550 allowNegative : true,
42552 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42556 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42558 minValue : Number.NEGATIVE_INFINITY,
42560 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42562 maxValue : Number.MAX_VALUE,
42564 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42566 minText : "The minimum value for this field is {0}",
42568 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42570 maxText : "The maximum value for this field is {0}",
42572 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
42573 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42575 nanText : "{0} is not a valid number",
42577 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42581 * @cfg {String} defaults currency of the MoneyField
42582 * value should be in lkey
42584 defaultCurrency : false,
42586 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42588 thousandsDelimiter : false,
42590 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42601 getAutoCreate : function()
42603 var align = this.labelAlign || this.parentLabelAlign();
42615 cls : 'form-control roo-money-amount-input',
42616 autocomplete: 'new-password'
42619 var hiddenInput = {
42623 cls: 'hidden-number-input'
42626 if(this.max_length) {
42627 input.maxlength = this.max_length;
42631 hiddenInput.name = this.name;
42634 if (this.disabled) {
42635 input.disabled = true;
42638 var clg = 12 - this.inputlg;
42639 var cmd = 12 - this.inputmd;
42640 var csm = 12 - this.inputsm;
42641 var cxs = 12 - this.inputxs;
42645 cls : 'row roo-money-field',
42649 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42653 cls: 'roo-select2-container input-group',
42657 cls : 'form-control roo-money-currency-input',
42658 autocomplete: 'new-password',
42660 name : this.currencyName
42664 cls : 'input-group-addon',
42678 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42682 cls: this.hasFeedback ? 'has-feedback' : '',
42693 if (this.fieldLabel.length) {
42696 tooltip: 'This field is required'
42702 cls: 'control-label',
42708 html: this.fieldLabel
42711 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42717 if(this.indicatorpos == 'right') {
42718 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42725 if(align == 'left') {
42733 if(this.labelWidth > 12){
42734 label.style = "width: " + this.labelWidth + 'px';
42736 if(this.labelWidth < 13 && this.labelmd == 0){
42737 this.labelmd = this.labelWidth;
42739 if(this.labellg > 0){
42740 label.cls += ' col-lg-' + this.labellg;
42741 input.cls += ' col-lg-' + (12 - this.labellg);
42743 if(this.labelmd > 0){
42744 label.cls += ' col-md-' + this.labelmd;
42745 container.cls += ' col-md-' + (12 - this.labelmd);
42747 if(this.labelsm > 0){
42748 label.cls += ' col-sm-' + this.labelsm;
42749 container.cls += ' col-sm-' + (12 - this.labelsm);
42751 if(this.labelxs > 0){
42752 label.cls += ' col-xs-' + this.labelxs;
42753 container.cls += ' col-xs-' + (12 - this.labelxs);
42764 var settings = this;
42766 ['xs','sm','md','lg'].map(function(size){
42767 if (settings[size]) {
42768 cfg.cls += ' col-' + size + '-' + settings[size];
42775 initEvents : function()
42777 this.indicator = this.indicatorEl();
42779 this.initCurrencyEvent();
42781 this.initNumberEvent();
42784 initCurrencyEvent : function()
42787 throw "can not find store for combo";
42790 this.store = Roo.factory(this.store, Roo.data);
42791 this.store.parent = this;
42795 this.triggerEl = this.el.select('.input-group-addon', true).first();
42797 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42802 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42803 _this.list.setWidth(lw);
42806 this.list.on('mouseover', this.onViewOver, this);
42807 this.list.on('mousemove', this.onViewMove, this);
42808 this.list.on('scroll', this.onViewScroll, this);
42811 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42814 this.view = new Roo.View(this.list, this.tpl, {
42815 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42818 this.view.on('click', this.onViewClick, this);
42820 this.store.on('beforeload', this.onBeforeLoad, this);
42821 this.store.on('load', this.onLoad, this);
42822 this.store.on('loadexception', this.onLoadException, this);
42824 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42825 "up" : function(e){
42826 this.inKeyMode = true;
42830 "down" : function(e){
42831 if(!this.isExpanded()){
42832 this.onTriggerClick();
42834 this.inKeyMode = true;
42839 "enter" : function(e){
42842 if(this.fireEvent("specialkey", this, e)){
42843 this.onViewClick(false);
42849 "esc" : function(e){
42853 "tab" : function(e){
42856 if(this.fireEvent("specialkey", this, e)){
42857 this.onViewClick(false);
42865 doRelay : function(foo, bar, hname){
42866 if(hname == 'down' || this.scope.isExpanded()){
42867 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42875 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42879 initNumberEvent : function(e)
42881 this.inputEl().on("keydown" , this.fireKey, this);
42882 this.inputEl().on("focus", this.onFocus, this);
42883 this.inputEl().on("blur", this.onBlur, this);
42885 this.inputEl().relayEvent('keyup', this);
42887 if(this.indicator){
42888 this.indicator.addClass('invisible');
42891 this.originalValue = this.getValue();
42893 if(this.validationEvent == 'keyup'){
42894 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42895 this.inputEl().on('keyup', this.filterValidation, this);
42897 else if(this.validationEvent !== false){
42898 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42901 if(this.selectOnFocus){
42902 this.on("focus", this.preFocus, this);
42905 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42906 this.inputEl().on("keypress", this.filterKeys, this);
42908 this.inputEl().relayEvent('keypress', this);
42911 var allowed = "0123456789";
42913 if(this.allowDecimals){
42914 allowed += this.decimalSeparator;
42917 if(this.allowNegative){
42921 if(this.thousandsDelimiter) {
42925 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42927 var keyPress = function(e){
42929 var k = e.getKey();
42931 var c = e.getCharCode();
42934 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42935 allowed.indexOf(String.fromCharCode(c)) === -1
42941 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42945 if(allowed.indexOf(String.fromCharCode(c)) === -1){
42950 this.inputEl().on("keypress", keyPress, this);
42954 onTriggerClick : function(e)
42961 this.loadNext = false;
42963 if(this.isExpanded()){
42968 this.hasFocus = true;
42970 if(this.triggerAction == 'all') {
42971 this.doQuery(this.allQuery, true);
42975 this.doQuery(this.getRawValue());
42978 getCurrency : function()
42980 var v = this.currencyEl().getValue();
42985 restrictHeight : function()
42987 this.list.alignTo(this.currencyEl(), this.listAlign);
42988 this.list.alignTo(this.currencyEl(), this.listAlign);
42991 onViewClick : function(view, doFocus, el, e)
42993 var index = this.view.getSelectedIndexes()[0];
42995 var r = this.store.getAt(index);
42998 this.onSelect(r, index);
43002 onSelect : function(record, index){
43004 if(this.fireEvent('beforeselect', this, record, index) !== false){
43006 this.setFromCurrencyData(index > -1 ? record.data : false);
43010 this.fireEvent('select', this, record, index);
43014 setFromCurrencyData : function(o)
43018 this.lastCurrency = o;
43020 if (this.currencyField) {
43021 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43023 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43026 this.lastSelectionText = currency;
43028 //setting default currency
43029 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43030 this.setCurrency(this.defaultCurrency);
43034 this.setCurrency(currency);
43037 setFromData : function(o)
43041 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43043 this.setFromCurrencyData(c);
43048 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43050 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43053 this.setValue(value);
43057 setCurrency : function(v)
43059 this.currencyValue = v;
43062 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43067 setValue : function(v)
43069 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43075 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43077 this.inputEl().dom.value = (v == '') ? '' :
43078 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43080 if(!this.allowZero && v === '0') {
43081 this.hiddenEl().dom.value = '';
43082 this.inputEl().dom.value = '';
43089 getRawValue : function()
43091 var v = this.inputEl().getValue();
43096 getValue : function()
43098 return this.fixPrecision(this.parseValue(this.getRawValue()));
43101 parseValue : function(value)
43103 if(this.thousandsDelimiter) {
43105 r = new RegExp(",", "g");
43106 value = value.replace(r, "");
43109 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43110 return isNaN(value) ? '' : value;
43114 fixPrecision : function(value)
43116 if(this.thousandsDelimiter) {
43118 r = new RegExp(",", "g");
43119 value = value.replace(r, "");
43122 var nan = isNaN(value);
43124 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43125 return nan ? '' : value;
43127 return parseFloat(value).toFixed(this.decimalPrecision);
43130 decimalPrecisionFcn : function(v)
43132 return Math.floor(v);
43135 validateValue : function(value)
43137 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43141 var num = this.parseValue(value);
43144 this.markInvalid(String.format(this.nanText, value));
43148 if(num < this.minValue){
43149 this.markInvalid(String.format(this.minText, this.minValue));
43153 if(num > this.maxValue){
43154 this.markInvalid(String.format(this.maxText, this.maxValue));
43161 validate : function()
43163 if(this.disabled || this.allowBlank){
43168 var currency = this.getCurrency();
43170 if(this.validateValue(this.getRawValue()) && currency.length){
43175 this.markInvalid();
43179 getName: function()
43184 beforeBlur : function()
43190 var v = this.parseValue(this.getRawValue());
43197 onBlur : function()
43201 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43202 //this.el.removeClass(this.focusClass);
43205 this.hasFocus = false;
43207 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43211 var v = this.getValue();
43213 if(String(v) !== String(this.startValue)){
43214 this.fireEvent('change', this, v, this.startValue);
43217 this.fireEvent("blur", this);
43220 inputEl : function()
43222 return this.el.select('.roo-money-amount-input', true).first();
43225 currencyEl : function()
43227 return this.el.select('.roo-money-currency-input', true).first();
43230 hiddenEl : function()
43232 return this.el.select('input.hidden-number-input',true).first();
43236 * @class Roo.bootstrap.BezierSignature
43237 * @extends Roo.bootstrap.Component
43238 * Bootstrap BezierSignature class
43239 * This script refer to:
43240 * Title: Signature Pad
43242 * Availability: https://github.com/szimek/signature_pad
43245 * Create a new BezierSignature
43246 * @param {Object} config The config object
43249 Roo.bootstrap.BezierSignature = function(config){
43250 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43256 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43263 mouse_btn_down: true,
43266 * @cfg {int} canvas height
43268 canvas_height: '200px',
43271 * @cfg {float|function} Radius of a single dot.
43276 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43281 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43286 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43291 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43296 * @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.
43298 bg_color: 'rgba(0, 0, 0, 0)',
43301 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43303 dot_color: 'black',
43306 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43308 velocity_filter_weight: 0.7,
43311 * @cfg {function} Callback when stroke begin.
43316 * @cfg {function} Callback when stroke end.
43320 getAutoCreate : function()
43322 var cls = 'roo-signature column';
43325 cls += ' ' + this.cls;
43335 for(var i = 0; i < col_sizes.length; i++) {
43336 if(this[col_sizes[i]]) {
43337 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43347 cls: 'roo-signature-body',
43351 cls: 'roo-signature-body-canvas',
43352 height: this.canvas_height,
43353 width: this.canvas_width
43360 style: 'display: none'
43368 initEvents: function()
43370 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43372 var canvas = this.canvasEl();
43374 // mouse && touch event swapping...
43375 canvas.dom.style.touchAction = 'none';
43376 canvas.dom.style.msTouchAction = 'none';
43378 this.mouse_btn_down = false;
43379 canvas.on('mousedown', this._handleMouseDown, this);
43380 canvas.on('mousemove', this._handleMouseMove, this);
43381 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43383 if (window.PointerEvent) {
43384 canvas.on('pointerdown', this._handleMouseDown, this);
43385 canvas.on('pointermove', this._handleMouseMove, this);
43386 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43389 if ('ontouchstart' in window) {
43390 canvas.on('touchstart', this._handleTouchStart, this);
43391 canvas.on('touchmove', this._handleTouchMove, this);
43392 canvas.on('touchend', this._handleTouchEnd, this);
43395 Roo.EventManager.onWindowResize(this.resize, this, true);
43397 // file input event
43398 this.fileEl().on('change', this.uploadImage, this);
43405 resize: function(){
43407 var canvas = this.canvasEl().dom;
43408 var ctx = this.canvasElCtx();
43409 var img_data = false;
43411 if(canvas.width > 0) {
43412 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43414 // setting canvas width will clean img data
43417 var style = window.getComputedStyle ?
43418 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43420 var padding_left = parseInt(style.paddingLeft) || 0;
43421 var padding_right = parseInt(style.paddingRight) || 0;
43423 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43426 ctx.putImageData(img_data, 0, 0);
43430 _handleMouseDown: function(e)
43432 if (e.browserEvent.which === 1) {
43433 this.mouse_btn_down = true;
43434 this.strokeBegin(e);
43438 _handleMouseMove: function (e)
43440 if (this.mouse_btn_down) {
43441 this.strokeMoveUpdate(e);
43445 _handleMouseUp: function (e)
43447 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43448 this.mouse_btn_down = false;
43453 _handleTouchStart: function (e) {
43455 e.preventDefault();
43456 if (e.browserEvent.targetTouches.length === 1) {
43457 // var touch = e.browserEvent.changedTouches[0];
43458 // this.strokeBegin(touch);
43460 this.strokeBegin(e); // assume e catching the correct xy...
43464 _handleTouchMove: function (e) {
43465 e.preventDefault();
43466 // var touch = event.targetTouches[0];
43467 // _this._strokeMoveUpdate(touch);
43468 this.strokeMoveUpdate(e);
43471 _handleTouchEnd: function (e) {
43472 var wasCanvasTouched = e.target === this.canvasEl().dom;
43473 if (wasCanvasTouched) {
43474 e.preventDefault();
43475 // var touch = event.changedTouches[0];
43476 // _this._strokeEnd(touch);
43481 reset: function () {
43482 this._lastPoints = [];
43483 this._lastVelocity = 0;
43484 this._lastWidth = (this.min_width + this.max_width) / 2;
43485 this.canvasElCtx().fillStyle = this.dot_color;
43488 strokeMoveUpdate: function(e)
43490 this.strokeUpdate(e);
43492 if (this.throttle) {
43493 this.throttleStroke(this.strokeUpdate, this.throttle);
43496 this.strokeUpdate(e);
43500 strokeBegin: function(e)
43502 var newPointGroup = {
43503 color: this.dot_color,
43507 if (typeof this.onBegin === 'function') {
43511 this.curve_data.push(newPointGroup);
43513 this.strokeUpdate(e);
43516 strokeUpdate: function(e)
43518 var rect = this.canvasEl().dom.getBoundingClientRect();
43519 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43520 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43521 var lastPoints = lastPointGroup.points;
43522 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43523 var isLastPointTooClose = lastPoint
43524 ? point.distanceTo(lastPoint) <= this.min_distance
43526 var color = lastPointGroup.color;
43527 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43528 var curve = this.addPoint(point);
43530 this.drawDot({color: color, point: point});
43533 this.drawCurve({color: color, curve: curve});
43543 strokeEnd: function(e)
43545 this.strokeUpdate(e);
43546 if (typeof this.onEnd === 'function') {
43551 addPoint: function (point) {
43552 var _lastPoints = this._lastPoints;
43553 _lastPoints.push(point);
43554 if (_lastPoints.length > 2) {
43555 if (_lastPoints.length === 3) {
43556 _lastPoints.unshift(_lastPoints[0]);
43558 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43559 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43560 _lastPoints.shift();
43566 calculateCurveWidths: function (startPoint, endPoint) {
43567 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43568 (1 - this.velocity_filter_weight) * this._lastVelocity;
43570 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43573 start: this._lastWidth
43576 this._lastVelocity = velocity;
43577 this._lastWidth = newWidth;
43581 drawDot: function (_a) {
43582 var color = _a.color, point = _a.point;
43583 var ctx = this.canvasElCtx();
43584 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43586 this.drawCurveSegment(point.x, point.y, width);
43588 ctx.fillStyle = color;
43592 drawCurve: function (_a) {
43593 var color = _a.color, curve = _a.curve;
43594 var ctx = this.canvasElCtx();
43595 var widthDelta = curve.endWidth - curve.startWidth;
43596 var drawSteps = Math.floor(curve.length()) * 2;
43598 ctx.fillStyle = color;
43599 for (var i = 0; i < drawSteps; i += 1) {
43600 var t = i / drawSteps;
43606 var x = uuu * curve.startPoint.x;
43607 x += 3 * uu * t * curve.control1.x;
43608 x += 3 * u * tt * curve.control2.x;
43609 x += ttt * curve.endPoint.x;
43610 var y = uuu * curve.startPoint.y;
43611 y += 3 * uu * t * curve.control1.y;
43612 y += 3 * u * tt * curve.control2.y;
43613 y += ttt * curve.endPoint.y;
43614 var width = curve.startWidth + ttt * widthDelta;
43615 this.drawCurveSegment(x, y, width);
43621 drawCurveSegment: function (x, y, width) {
43622 var ctx = this.canvasElCtx();
43624 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43625 this.is_empty = false;
43630 var ctx = this.canvasElCtx();
43631 var canvas = this.canvasEl().dom;
43632 ctx.fillStyle = this.bg_color;
43633 ctx.clearRect(0, 0, canvas.width, canvas.height);
43634 ctx.fillRect(0, 0, canvas.width, canvas.height);
43635 this.curve_data = [];
43637 this.is_empty = true;
43642 return this.el.select('input',true).first();
43645 canvasEl: function()
43647 return this.el.select('canvas',true).first();
43650 canvasElCtx: function()
43652 return this.el.select('canvas',true).first().dom.getContext('2d');
43655 getImage: function(type)
43657 if(this.is_empty) {
43662 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43665 drawFromImage: function(img_src)
43667 var img = new Image();
43669 img.onload = function(){
43670 this.canvasElCtx().drawImage(img, 0, 0);
43675 this.is_empty = false;
43678 selectImage: function()
43680 this.fileEl().dom.click();
43683 uploadImage: function(e)
43685 var reader = new FileReader();
43687 reader.onload = function(e){
43688 var img = new Image();
43689 img.onload = function(){
43691 this.canvasElCtx().drawImage(img, 0, 0);
43693 img.src = e.target.result;
43696 reader.readAsDataURL(e.target.files[0]);
43699 // Bezier Point Constructor
43700 Point: (function () {
43701 function Point(x, y, time) {
43704 this.time = time || Date.now();
43706 Point.prototype.distanceTo = function (start) {
43707 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43709 Point.prototype.equals = function (other) {
43710 return this.x === other.x && this.y === other.y && this.time === other.time;
43712 Point.prototype.velocityFrom = function (start) {
43713 return this.time !== start.time
43714 ? this.distanceTo(start) / (this.time - start.time)
43721 // Bezier Constructor
43722 Bezier: (function () {
43723 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43724 this.startPoint = startPoint;
43725 this.control2 = control2;
43726 this.control1 = control1;
43727 this.endPoint = endPoint;
43728 this.startWidth = startWidth;
43729 this.endWidth = endWidth;
43731 Bezier.fromPoints = function (points, widths, scope) {
43732 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43733 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43734 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43736 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43737 var dx1 = s1.x - s2.x;
43738 var dy1 = s1.y - s2.y;
43739 var dx2 = s2.x - s3.x;
43740 var dy2 = s2.y - s3.y;
43741 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43742 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43743 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43744 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43745 var dxm = m1.x - m2.x;
43746 var dym = m1.y - m2.y;
43747 var k = l2 / (l1 + l2);
43748 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43749 var tx = s2.x - cm.x;
43750 var ty = s2.y - cm.y;
43752 c1: new scope.Point(m1.x + tx, m1.y + ty),
43753 c2: new scope.Point(m2.x + tx, m2.y + ty)
43756 Bezier.prototype.length = function () {
43761 for (var i = 0; i <= steps; i += 1) {
43763 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43764 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43766 var xdiff = cx - px;
43767 var ydiff = cy - py;
43768 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43775 Bezier.prototype.point = function (t, start, c1, c2, end) {
43776 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43777 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43778 + (3.0 * c2 * (1.0 - t) * t * t)
43779 + (end * t * t * t);
43784 throttleStroke: function(fn, wait) {
43785 if (wait === void 0) { wait = 250; }
43787 var timeout = null;
43791 var later = function () {
43792 previous = Date.now();
43794 result = fn.apply(storedContext, storedArgs);
43796 storedContext = null;
43800 return function wrapper() {
43802 for (var _i = 0; _i < arguments.length; _i++) {
43803 args[_i] = arguments[_i];
43805 var now = Date.now();
43806 var remaining = wait - (now - previous);
43807 storedContext = this;
43809 if (remaining <= 0 || remaining > wait) {
43811 clearTimeout(timeout);
43815 result = fn.apply(storedContext, storedArgs);
43817 storedContext = null;
43821 else if (!timeout) {
43822 timeout = window.setTimeout(later, remaining);