2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets, function(s) {
10 if ( s.href && s.href.match(/css-bootstrap4/)) {
17 * Ext JS Library 1.1.1
18 * Copyright(c) 2006-2007, Ext JS, LLC.
20 * Originally Released Under LGPL - original licence link has changed is not relivant.
23 * <script type="text/javascript">
29 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
30 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
31 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34 * @param {Object} config The config object
36 Roo.Shadow = function(config){
37 Roo.apply(this, config);
38 if(typeof this.mode != "string"){
39 this.mode = this.defaultMode;
41 var o = this.offset, a = {h: 0};
42 var rad = Math.floor(this.offset/2);
43 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49 a.l -= this.offset + rad;
50 a.t -= this.offset + rad;
61 a.l -= (this.offset - rad);
62 a.t -= this.offset + rad;
64 a.w -= (this.offset - rad)*2;
75 a.l -= (this.offset - rad);
76 a.t -= (this.offset - rad);
78 a.w -= (this.offset + rad + 1);
79 a.h -= (this.offset + rad);
88 Roo.Shadow.prototype = {
91 * The shadow display mode. Supports the following options:<br />
92 * sides: Shadow displays on both sides and bottom only<br />
93 * frame: Shadow displays equally on all four sides<br />
94 * drop: Traditional bottom-right drop shadow (default)
97 * @cfg {String} offset
98 * The number of pixels to offset the shadow from the element (defaults to 4)
106 * Displays the shadow under the target element
107 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
109 show : function(target){
110 target = Roo.get(target);
112 this.el = Roo.Shadow.Pool.pull();
113 if(this.el.dom.nextSibling != target.dom){
114 this.el.insertBefore(target);
117 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
119 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122 target.getLeft(true),
127 this.el.dom.style.display = "block";
131 * Returns true if the shadow is visible, else false
133 isVisible : function(){
134 return this.el ? true : false;
138 * Direct alignment when values are already available. Show must be called at least once before
139 * calling this method to ensure it is initialized.
140 * @param {Number} left The target element left position
141 * @param {Number} top The target element top position
142 * @param {Number} width The target element width
143 * @param {Number} height The target element height
145 realign : function(l, t, w, h){
149 var a = this.adjusts, d = this.el.dom, s = d.style;
151 s.left = (l+a.l)+"px";
152 s.top = (t+a.t)+"px";
153 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
155 if(s.width != sws || s.height != shs){
159 var cn = d.childNodes;
160 var sww = Math.max(0, (sw-12))+"px";
161 cn[0].childNodes[1].style.width = sww;
162 cn[1].childNodes[1].style.width = sww;
163 cn[2].childNodes[1].style.width = sww;
164 cn[1].style.height = Math.max(0, (sh-12))+"px";
174 this.el.dom.style.display = "none";
175 Roo.Shadow.Pool.push(this.el);
181 * Adjust the z-index of this shadow
182 * @param {Number} zindex The new z-index
184 setZIndex : function(z){
187 this.el.setStyle("z-index", z);
192 // Private utility class that manages the internal Shadow cache
193 Roo.Shadow.Pool = function(){
195 var markup = Roo.isIE ?
196 '<div class="x-ie-shadow"></div>' :
197 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
202 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
203 sh.autoBoxAdjust = false;
215 * base class for bootstrap elements.
219 Roo.bootstrap = Roo.bootstrap || {};
221 * @class Roo.bootstrap.Component
222 * @extends Roo.Component
223 * Bootstrap Component base class
224 * @cfg {String} cls css class
225 * @cfg {String} style any extra css
226 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
227 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
228 * @cfg {string} dataId cutomer id
229 * @cfg {string} name Specifies name attribute
230 * @cfg {string} tooltip Text for the tooltip
231 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
232 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235 * Do not use directly - it does not do anything..
236 * @param {Object} config The config object
241 Roo.bootstrap.Component = function(config){
242 Roo.bootstrap.Component.superclass.constructor.call(this, config);
246 * @event childrenrendered
247 * Fires when the children have been rendered..
248 * @param {Roo.bootstrap.Component} this
250 "childrenrendered" : true
259 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
262 allowDomMove : false, // to stop relocations in parent onRender...
272 * Initialize Events for the element
274 initEvents : function() { },
280 can_build_overlaid : true,
282 container_method : false,
289 // returns the parent component..
290 return Roo.ComponentMgr.get(this.parentId)
296 onRender : function(ct, position)
298 // Roo.log("Call onRender: " + this.xtype);
300 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303 if (this.el.attr('xtype')) {
304 this.el.attr('xtypex', this.el.attr('xtype'));
305 this.el.dom.removeAttribute('xtype');
315 var cfg = Roo.apply({}, this.getAutoCreate());
317 cfg.id = this.id || Roo.id();
319 // fill in the extra attributes
320 if (this.xattr && typeof(this.xattr) =='object') {
321 for (var i in this.xattr) {
322 cfg[i] = this.xattr[i];
327 cfg.dataId = this.dataId;
331 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334 if (this.style) { // fixme needs to support more complex style data.
335 cfg.style = this.style;
339 cfg.name = this.name;
342 this.el = ct.createChild(cfg, position);
345 this.tooltipEl().attr('tooltip', this.tooltip);
348 if(this.tabIndex !== undefined){
349 this.el.dom.setAttribute('tabIndex', this.tabIndex);
356 * Fetch the element to add children to
357 * @return {Roo.Element} defaults to this.el
359 getChildContainer : function()
364 * Fetch the element to display the tooltip on.
365 * @return {Roo.Element} defaults to this.el
367 tooltipEl : function()
372 addxtype : function(tree,cntr)
376 cn = Roo.factory(tree);
377 //Roo.log(['addxtype', cn]);
379 cn.parentType = this.xtype; //??
380 cn.parentId = this.id;
382 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
383 if (typeof(cn.container_method) == 'string') {
384 cntr = cn.container_method;
388 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
390 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
392 var build_from_html = Roo.XComponent.build_from_html;
394 var is_body = (tree.xtype == 'Body') ;
396 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
398 var self_cntr_el = Roo.get(this[cntr](false));
400 // do not try and build conditional elements
401 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
405 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
406 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
407 return this.addxtypeChild(tree,cntr, is_body);
410 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416 Roo.log('skipping render');
422 if (!build_from_html) {
426 // this i think handles overlaying multiple children of the same type
427 // with the sam eelement.. - which might be buggy..
429 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
435 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
439 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
446 addxtypeChild : function (tree, cntr, is_body)
448 Roo.debug && Roo.log('addxtypeChild:' + cntr);
450 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
454 (typeof(tree['flexy:foreach']) != 'undefined');
458 skip_children = false;
459 // render the element if it's not BODY.
462 // if parent was disabled, then do not try and create the children..
463 if(!this[cntr](true)){
468 cn = Roo.factory(tree);
470 cn.parentType = this.xtype; //??
471 cn.parentId = this.id;
473 var build_from_html = Roo.XComponent.build_from_html;
476 // does the container contain child eleemnts with 'xtype' attributes.
477 // that match this xtype..
478 // note - when we render we create these as well..
479 // so we should check to see if body has xtype set.
480 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
482 var self_cntr_el = Roo.get(this[cntr](false));
483 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
485 //Roo.log(Roo.XComponent.build_from_html);
486 //Roo.log("got echild:");
489 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
490 // and are not displayed -this causes this to use up the wrong element when matching.
491 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
495 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
501 //echild.dom.removeAttribute('xtype');
503 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
504 Roo.debug && Roo.log(self_cntr_el);
505 Roo.debug && Roo.log(echild);
506 Roo.debug && Roo.log(cn);
512 // if object has flexy:if - then it may or may not be rendered.
513 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
514 // skip a flexy if element.
515 Roo.debug && Roo.log('skipping render');
516 Roo.debug && Roo.log(tree);
518 Roo.debug && Roo.log('skipping all children');
519 skip_children = true;
524 // actually if flexy:foreach is found, we really want to create
525 // multiple copies here...
527 //Roo.log(this[cntr]());
528 // some elements do not have render methods.. like the layouts...
530 if(this[cntr](true) === false){
535 cn.render && cn.render(this[cntr](true));
538 // then add the element..
545 if (typeof (tree.menu) != 'undefined') {
546 tree.menu.parentType = cn.xtype;
547 tree.menu.triggerEl = cn.el;
548 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
552 if (!tree.items || !tree.items.length) {
554 //Roo.log(["no children", this]);
559 var items = tree.items;
562 //Roo.log(items.length);
564 if (!skip_children) {
565 for(var i =0;i < items.length;i++) {
566 // Roo.log(['add child', items[i]]);
567 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
573 //Roo.log("fire childrenrendered");
575 cn.fireEvent('childrenrendered', this);
581 * Set the element that will be used to show or hide
583 setVisibilityEl : function(el)
585 this.visibilityEl = el;
589 * Get the element that will be used to show or hide
591 getVisibilityEl : function()
593 if (typeof(this.visibilityEl) == 'object') {
594 return this.visibilityEl;
597 if (typeof(this.visibilityEl) == 'string') {
598 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
605 * Show a component - removes 'hidden' class
609 if(!this.getVisibilityEl()){
613 this.getVisibilityEl().removeClass(['hidden','d-none']);
615 this.fireEvent('show', this);
620 * Hide a component - adds 'hidden' class
624 if(!this.getVisibilityEl()){
628 this.getVisibilityEl().addClass(['hidden','d-none']);
630 this.fireEvent('hide', this);
643 * @class Roo.bootstrap.Element
644 * @extends Roo.bootstrap.Component
645 * Bootstrap Element class
646 * @cfg {String} html contents of the element
647 * @cfg {String} tag tag of the element
648 * @cfg {String} cls class of the element
649 * @cfg {Boolean} preventDefault (true|false) default false
650 * @cfg {Boolean} clickable (true|false) default false
653 * Create a new Element
654 * @param {Object} config The config object
657 Roo.bootstrap.Element = function(config){
658 Roo.bootstrap.Element.superclass.constructor.call(this, config);
664 * When a element is chick
665 * @param {Roo.bootstrap.Element} this
666 * @param {Roo.EventObject} e
672 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
677 preventDefault: false,
680 getAutoCreate : function(){
684 // cls: this.cls, double assign in parent class Component.js :: onRender
691 initEvents: function()
693 Roo.bootstrap.Element.superclass.initEvents.call(this);
696 this.el.on('click', this.onClick, this);
701 onClick : function(e)
703 if(this.preventDefault){
707 this.fireEvent('click', this, e);
710 getValue : function()
712 return this.el.dom.innerHTML;
715 setValue : function(value)
717 this.el.dom.innerHTML = value;
732 * @class Roo.bootstrap.DropTarget
733 * @extends Roo.bootstrap.Element
734 * Bootstrap DropTarget class
736 * @cfg {string} name dropable name
739 * Create a new Dropable Area
740 * @param {Object} config The config object
743 Roo.bootstrap.DropTarget = function(config){
744 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
750 * When a element is chick
751 * @param {Roo.bootstrap.Element} this
752 * @param {Roo.EventObject} e
758 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
761 getAutoCreate : function(){
766 initEvents: function()
768 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
769 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772 drop : this.dragDrop.createDelegate(this),
773 enter : this.dragEnter.createDelegate(this),
774 out : this.dragOut.createDelegate(this),
775 over : this.dragOver.createDelegate(this)
779 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782 dragDrop : function(source,e,data)
784 // user has to decide how to impliment this.
787 //this.fireEvent('drop', this, source, e ,data);
791 dragEnter : function(n, dd, e, data)
793 // probably want to resize the element to match the dropped element..
795 this.originalSize = this.el.getSize();
796 this.el.setSize( n.el.getSize());
797 this.dropZone.DDM.refreshCache(this.name);
798 Roo.log([n, dd, e, data]);
801 dragOut : function(value)
803 // resize back to normal
805 this.el.setSize(this.originalSize);
806 this.dropZone.resetConstraints();
809 dragOver : function()
826 * @class Roo.bootstrap.Body
827 * @extends Roo.bootstrap.Component
828 * Bootstrap Body class
832 * @param {Object} config The config object
835 Roo.bootstrap.Body = function(config){
837 config = config || {};
839 Roo.bootstrap.Body.superclass.constructor.call(this, config);
840 this.el = Roo.get(config.el ? config.el : document.body );
841 if (this.cls && this.cls.length) {
842 Roo.get(document.body).addClass(this.cls);
846 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
848 is_body : true,// just to make sure it's constructed?
853 onRender : function(ct, position)
855 /* Roo.log("Roo.bootstrap.Body - onRender");
856 if (this.cls && this.cls.length) {
857 Roo.get(document.body).addClass(this.cls);
876 * @class Roo.bootstrap.ButtonGroup
877 * @extends Roo.bootstrap.Component
878 * Bootstrap ButtonGroup class
879 * @cfg {String} size lg | sm | xs (default empty normal)
880 * @cfg {String} align vertical | justified (default none)
881 * @cfg {String} direction up | down (default down)
882 * @cfg {Boolean} toolbar false | true
883 * @cfg {Boolean} btn true | false
888 * @param {Object} config The config object
891 Roo.bootstrap.ButtonGroup = function(config){
892 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
903 getAutoCreate : function(){
909 cfg.html = this.html || cfg.html;
920 if (['vertical','justified'].indexOf(this.align)!==-1) {
921 cfg.cls = 'btn-group-' + this.align;
923 if (this.align == 'justified') {
924 console.log(this.items);
928 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
929 cfg.cls += ' btn-group-' + this.size;
932 if (this.direction == 'up') {
933 cfg.cls += ' dropup' ;
939 * Add a button to the group (similar to NavItem API.)
941 addItem : function(cfg)
943 var cn = new Roo.bootstrap.Button(cfg);
945 cn.parentId = this.id;
946 cn.onRender(this.el, null);
960 * @class Roo.bootstrap.Button
961 * @extends Roo.bootstrap.Component
962 * Bootstrap Button class
963 * @cfg {String} html The button content
964 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
965 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
966 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
967 * @cfg {String} size (lg|sm|xs)
968 * @cfg {String} tag (a|input|submit)
969 * @cfg {String} href empty or href
970 * @cfg {Boolean} disabled default false;
971 * @cfg {Boolean} isClose default false;
972 * @cfg {String} glyphicon depricated - use fa
973 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
974 * @cfg {String} badge text for badge
975 * @cfg {String} theme (default|glow)
976 * @cfg {Boolean} inverse dark themed version
977 * @cfg {Boolean} toggle is it a slidy toggle button
978 * @cfg {Boolean} pressed default null - if the button ahs active state
979 * @cfg {String} ontext text for on slidy toggle state
980 * @cfg {String} offtext text for off slidy toggle state
981 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
982 * @cfg {Boolean} removeClass remove the standard class..
983 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
986 * Create a new button
987 * @param {Object} config The config object
991 Roo.bootstrap.Button = function(config){
992 Roo.bootstrap.Button.superclass.constructor.call(this, config);
998 * When a butotn is pressed
999 * @param {Roo.bootstrap.Button} btn
1000 * @param {Roo.EventObject} e
1005 * After the button has been toggles
1006 * @param {Roo.bootstrap.Button} btn
1007 * @param {Roo.EventObject} e
1008 * @param {boolean} pressed (also available as button.pressed)
1014 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1035 preventDefault: true,
1043 getAutoCreate : function(){
1051 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1052 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1053 this.tag = 'button';
1057 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1059 if (this.toggle == true) {
1062 cls: 'slider-frame roo-button',
1066 'data-on-text':'ON',
1067 'data-off-text':'OFF',
1068 cls: 'slider-button',
1074 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1075 cfg.cls += ' '+this.weight;
1082 cfg.cls += ' close';
1084 cfg["aria-hidden"] = true;
1086 cfg.html = "×";
1092 if (this.theme==='default') {
1093 cfg.cls = 'btn roo-button';
1095 //if (this.parentType != 'Navbar') {
1096 this.weight = this.weight.length ? this.weight : 'default';
1098 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1100 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1101 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1102 cfg.cls += ' btn-' + outline + weight;
1103 if (this.weight == 'default') {
1105 cfg.cls += ' btn-' + this.weight;
1108 } else if (this.theme==='glow') {
1111 cfg.cls = 'btn-glow roo-button';
1113 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1115 cfg.cls += ' ' + this.weight;
1121 this.cls += ' inverse';
1125 if (this.active || this.pressed === true) {
1126 cfg.cls += ' active';
1129 if (this.disabled) {
1130 cfg.disabled = 'disabled';
1134 Roo.log('changing to ul' );
1136 this.glyphicon = 'caret';
1137 if (Roo.bootstrap.version == 4) {
1138 this.fa = 'caret-down';
1143 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1145 //gsRoo.log(this.parentType);
1146 if (this.parentType === 'Navbar' && !this.parent().bar) {
1147 Roo.log('changing to li?');
1156 href : this.href || '#'
1159 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1160 cfg.cls += ' dropdown';
1167 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1169 if (this.glyphicon) {
1170 cfg.html = ' ' + cfg.html;
1175 cls: 'glyphicon glyphicon-' + this.glyphicon
1180 cfg.html = ' ' + cfg.html;
1185 cls: 'fa fas fa-' + this.fa
1195 // cfg.cls='btn roo-button';
1199 var value = cfg.html;
1204 cls: 'glyphicon glyphicon-' + this.glyphicon,
1211 cls: 'fa fas fa-' + this.fa,
1216 var bw = this.badge_weight.length ? this.badge_weight :
1217 (this.weight.length ? this.weight : 'secondary');
1218 bw = bw == 'default' ? 'secondary' : bw;
1224 cls: 'badge badge-' + bw,
1233 cfg.cls += ' dropdown';
1234 cfg.html = typeof(cfg.html) != 'undefined' ?
1235 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1238 if (cfg.tag !== 'a' && this.href !== '') {
1239 throw "Tag must be a to set href.";
1240 } else if (this.href.length > 0) {
1241 cfg.href = this.href;
1244 if(this.removeClass){
1249 cfg.target = this.target;
1254 initEvents: function() {
1255 // Roo.log('init events?');
1256 // Roo.log(this.el.dom);
1259 if (typeof (this.menu) != 'undefined') {
1260 this.menu.parentType = this.xtype;
1261 this.menu.triggerEl = this.el;
1262 this.addxtype(Roo.apply({}, this.menu));
1266 if (this.el.hasClass('roo-button')) {
1267 this.el.on('click', this.onClick, this);
1269 this.el.select('.roo-button').on('click', this.onClick, this);
1272 if(this.removeClass){
1273 this.el.on('click', this.onClick, this);
1276 this.el.enableDisplayMode();
1279 onClick : function(e)
1281 if (this.disabled) {
1285 Roo.log('button on click ');
1286 if(this.preventDefault){
1290 if (this.pressed === true || this.pressed === false) {
1291 this.toggleActive(e);
1295 this.fireEvent('click', this, e);
1299 * Enables this button
1303 this.disabled = false;
1304 this.el.removeClass('disabled');
1308 * Disable this button
1310 disable : function()
1312 this.disabled = true;
1313 this.el.addClass('disabled');
1316 * sets the active state on/off,
1317 * @param {Boolean} state (optional) Force a particular state
1319 setActive : function(v) {
1321 this.el[v ? 'addClass' : 'removeClass']('active');
1325 * toggles the current active state
1327 toggleActive : function(e)
1329 this.setActive(!this.pressed);
1330 this.fireEvent('toggle', this, e, !this.pressed);
1333 * get the current active state
1334 * @return {boolean} true if it's active
1336 isActive : function()
1338 return this.el.hasClass('active');
1341 * set the text of the first selected button
1343 setText : function(str)
1345 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1348 * get the text of the first selected button
1350 getText : function()
1352 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1355 setWeight : function(str)
1357 this.el.removeClass(Roo.bootstrap.Button.weightClass );
1359 var outline = this.outline ? 'outline-' : '';
1360 if (str == 'default') {
1361 this.el.addClass('btn-default btn-outline-secondary');
1364 this.el.addClass('btn-' + outline + str);
1369 // fixme - should include btn-outline-*
1370 Roo.bootstrap.Button.weightClass = [
1373 "btn-outline-secondary",
1391 * @class Roo.bootstrap.Column
1392 * @extends Roo.bootstrap.Component
1393 * Bootstrap Column class
1394 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1395 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1396 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1397 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1398 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1399 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1400 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1401 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1404 * @cfg {Boolean} hidden (true|false) hide the element
1405 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1406 * @cfg {String} fa (ban|check|...) font awesome icon
1407 * @cfg {Number} fasize (1|2|....) font awsome size
1409 * @cfg {String} icon (info-sign|check|...) glyphicon name
1411 * @cfg {String} html content of column.
1414 * Create a new Column
1415 * @param {Object} config The config object
1418 Roo.bootstrap.Column = function(config){
1419 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1422 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1440 getAutoCreate : function(){
1441 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1449 var sizes = ['xs','sm','md','lg'];
1450 sizes.map(function(size ,ix){
1451 //Roo.log( size + ':' + settings[size]);
1453 if (settings[size+'off'] !== false) {
1454 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1457 if (settings[size] === false) {
1461 if (!settings[size]) { // 0 = hidden
1462 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1464 for (var i = ix; i > -1; i--) {
1465 cfg.cls += ' d-' + sizes[i] + '-none';
1471 cfg.cls += ' col-' + size + '-' + settings[size] + (
1472 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1478 cfg.cls += ' hidden';
1481 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1482 cfg.cls +=' alert alert-' + this.alert;
1486 if (this.html.length) {
1487 cfg.html = this.html;
1491 if (this.fasize > 1) {
1492 fasize = ' fa-' + this.fasize + 'x';
1494 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1499 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1518 * @class Roo.bootstrap.Container
1519 * @extends Roo.bootstrap.Component
1520 * Bootstrap Container class
1521 * @cfg {Boolean} jumbotron is it a jumbotron element
1522 * @cfg {String} html content of element
1523 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1524 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1525 * @cfg {String} header content of header (for panel)
1526 * @cfg {String} footer content of footer (for panel)
1527 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1528 * @cfg {String} tag (header|aside|section) type of HTML tag.
1529 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1530 * @cfg {String} fa font awesome icon
1531 * @cfg {String} icon (info-sign|check|...) glyphicon name
1532 * @cfg {Boolean} hidden (true|false) hide the element
1533 * @cfg {Boolean} expandable (true|false) default false
1534 * @cfg {Boolean} expanded (true|false) default true
1535 * @cfg {String} rheader contet on the right of header
1536 * @cfg {Boolean} clickable (true|false) default false
1540 * Create a new Container
1541 * @param {Object} config The config object
1544 Roo.bootstrap.Container = function(config){
1545 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1551 * After the panel has been expand
1553 * @param {Roo.bootstrap.Container} this
1558 * After the panel has been collapsed
1560 * @param {Roo.bootstrap.Container} this
1565 * When a element is chick
1566 * @param {Roo.bootstrap.Container} this
1567 * @param {Roo.EventObject} e
1573 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1591 getChildContainer : function() {
1597 if (this.panel.length) {
1598 return this.el.select('.panel-body',true).first();
1605 getAutoCreate : function(){
1608 tag : this.tag || 'div',
1612 if (this.jumbotron) {
1613 cfg.cls = 'jumbotron';
1618 // - this is applied by the parent..
1620 // cfg.cls = this.cls + '';
1623 if (this.sticky.length) {
1625 var bd = Roo.get(document.body);
1626 if (!bd.hasClass('bootstrap-sticky')) {
1627 bd.addClass('bootstrap-sticky');
1628 Roo.select('html',true).setStyle('height', '100%');
1631 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1635 if (this.well.length) {
1636 switch (this.well) {
1639 cfg.cls +=' well well-' +this.well;
1648 cfg.cls += ' hidden';
1652 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1653 cfg.cls +=' alert alert-' + this.alert;
1658 if (this.panel.length) {
1659 cfg.cls += ' panel panel-' + this.panel;
1661 if (this.header.length) {
1665 if(this.expandable){
1667 cfg.cls = cfg.cls + ' expandable';
1671 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1679 cls : 'panel-title',
1680 html : (this.expandable ? ' ' : '') + this.header
1684 cls: 'panel-header-right',
1690 cls : 'panel-heading',
1691 style : this.expandable ? 'cursor: pointer' : '',
1699 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1704 if (this.footer.length) {
1706 cls : 'panel-footer',
1715 body.html = this.html || cfg.html;
1716 // prefix with the icons..
1718 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1721 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1726 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1727 cfg.cls = 'container';
1733 initEvents: function()
1735 if(this.expandable){
1736 var headerEl = this.headerEl();
1739 headerEl.on('click', this.onToggleClick, this);
1744 this.el.on('click', this.onClick, this);
1749 onToggleClick : function()
1751 var headerEl = this.headerEl();
1767 if(this.fireEvent('expand', this)) {
1769 this.expanded = true;
1771 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1773 this.el.select('.panel-body',true).first().removeClass('hide');
1775 var toggleEl = this.toggleEl();
1781 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1786 collapse : function()
1788 if(this.fireEvent('collapse', this)) {
1790 this.expanded = false;
1792 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1793 this.el.select('.panel-body',true).first().addClass('hide');
1795 var toggleEl = this.toggleEl();
1801 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1805 toggleEl : function()
1807 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1811 return this.el.select('.panel-heading .fa',true).first();
1814 headerEl : function()
1816 if(!this.el || !this.panel.length || !this.header.length){
1820 return this.el.select('.panel-heading',true).first()
1825 if(!this.el || !this.panel.length){
1829 return this.el.select('.panel-body',true).first()
1832 titleEl : function()
1834 if(!this.el || !this.panel.length || !this.header.length){
1838 return this.el.select('.panel-title',true).first();
1841 setTitle : function(v)
1843 var titleEl = this.titleEl();
1849 titleEl.dom.innerHTML = v;
1852 getTitle : function()
1855 var titleEl = this.titleEl();
1861 return titleEl.dom.innerHTML;
1864 setRightTitle : function(v)
1866 var t = this.el.select('.panel-header-right',true).first();
1872 t.dom.innerHTML = v;
1875 onClick : function(e)
1879 this.fireEvent('click', this, e);
1886 * This is BS4's Card element.. - similar to our containers probably..
1890 * @class Roo.bootstrap.Card
1891 * @extends Roo.bootstrap.Component
1892 * Bootstrap Card class
1895 * possible... may not be implemented..
1896 * @cfg {String} header_image src url of image.
1897 * @cfg {String|Object} header
1898 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1900 * @cfg {String} title
1901 * @cfg {String} subtitle
1902 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1903 * @cfg {String} footer
1905 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1907 * @cfg {String} margin (0|1|2|3|4|5|auto)
1908 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1909 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1910 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1911 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1912 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1913 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1915 * @cfg {String} padding (0|1|2|3|4|5)
1916 * @cfg {String} padding_top (0|1|2|3|4|5)
1917 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1918 * @cfg {String} padding_left (0|1|2|3|4|5)
1919 * @cfg {String} padding_right (0|1|2|3|4|5)
1920 * @cfg {String} padding_x (0|1|2|3|4|5)
1921 * @cfg {String} padding_y (0|1|2|3|4|5)
1923 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1924 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1925 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1926 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1927 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1929 * @config {Boolean} dragable if this card can be dragged.
1930 * @config {String} drag_group group for drag
1931 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1932 * @config {String} drop_group group for drag
1934 * @config {Boolean} collapsable can the body be collapsed.
1935 * @config {Boolean} collapsed is the body collapsed when rendered...
1936 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1937 * @config {Boolean} rotated is the body rotated when rendered...
1940 * Create a new Container
1941 * @param {Object} config The config object
1944 Roo.bootstrap.Card = function(config){
1945 Roo.bootstrap.Card.superclass.constructor.call(this, config);
1951 * When a element a card is dropped
1952 * @param {Roo.bootstrap.Element} this
1953 * @param {Roo.Element} n the node being dropped?
1954 * @param {Object} dd Drag and drop data
1955 * @param {Roo.EventObject} e
1956 * @param {Roo.EventObject} data the data passed via getDragData
1961 * When a element a card is rotate
1962 * @param {Roo.bootstrap.Element} this
1963 * @param {Roo.Element} n the node being dropped?
1964 * @param {Boolean} rotate status
1972 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
1977 margin: '', /// may be better in component?
2007 collapsable : false,
2016 childContainer : false,
2017 dropEl : false, /// the dom placeholde element that indicates drop location.
2019 layoutCls : function()
2023 Roo.log(this.margin_bottom.length);
2024 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2025 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2027 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2028 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2030 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2031 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2035 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2036 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2037 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2041 // more generic support?
2049 // Roo.log("Call onRender: " + this.xtype);
2050 /* We are looking at something like this.
2052 <img src="..." class="card-img-top" alt="...">
2053 <div class="card-body">
2054 <h5 class="card-title">Card title</h5>
2055 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2057 >> this bit is really the body...
2058 <div> << we will ad dthis in hopefully it will not break shit.
2060 ** card text does not actually have any styling...
2062 <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>
2065 <a href="#" class="card-link">Card link</a>
2068 <div class="card-footer">
2069 <small class="text-muted">Last updated 3 mins ago</small>
2073 getAutoCreate : function(){
2081 if (this.weight.length && this.weight != 'light') {
2082 cfg.cls += ' text-white';
2084 cfg.cls += ' text-dark'; // need as it's nested..
2086 if (this.weight.length) {
2087 cfg.cls += ' bg-' + this.weight;
2090 cfg.cls += this.layoutCls();
2093 if (this.header.length) {
2095 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2096 cls : 'card-header',
2104 cls : 'card-header d-none',
2109 if (this.collapsable) {
2112 cls : 'd-block user-select-none',
2116 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2121 hdr.cn.push(hdr_ctr);
2126 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2131 if (this.header_image.length) {
2134 cls : 'card-img-top',
2135 src: this.header_image // escape?
2140 cls : 'card-img-top d-none'
2146 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2150 if (this.collapsable || this.rotateable) {
2153 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2160 if (this.title.length) {
2164 src: this.title // escape?
2168 if (this.subtitle.length) {
2172 src: this.subtitle // escape?
2178 cls : 'roo-card-body-ctr'
2181 if (this.html.length) {
2187 // fixme ? handle objects?
2189 if (this.footer.length) {
2192 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2197 cfg.cn.push({cls : 'card-footer d-none'});
2206 getCardHeader : function()
2208 var ret = this.el.select('.card-header',true).first();
2209 if (ret.hasClass('d-none')) {
2210 ret.removeClass('d-none');
2215 getCardFooter : function()
2217 var ret = this.el.select('.card-footer',true).first();
2218 if (ret.hasClass('d-none')) {
2219 ret.removeClass('d-none');
2224 getCardImageTop : function()
2226 var ret = this.el.select('.card-img-top',true).first();
2227 if (ret.hasClass('d-none')) {
2228 ret.removeClass('d-none');
2234 getChildContainer : function()
2240 return this.el.select('.roo-card-body-ctr',true).first();
2243 initEvents: function()
2246 this.bodyEl = this.getChildContainer();
2248 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2249 containerScroll: true,
2250 ddGroup: this.drag_group || 'default_card_drag_group'
2252 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2254 if (this.dropable) {
2255 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2256 containerScroll: true,
2257 ddGroup: this.drop_group || 'default_card_drag_group'
2259 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2260 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2261 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2262 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2263 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2266 if (this.collapsable) {
2267 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2269 if (this.rotateable) {
2270 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2272 this.collapsableEl = this.el.select('.roo-collapsable').first();
2274 this.footerEl = this.el.select('.card-footer').first();
2275 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2276 this.headerEl = this.el.select('.roo-card-header-ctr').first();
2279 this.el.addClass('roo-card-rotated');
2280 this.fireEvent('rotate', this, true);
2284 getDragData : function(e)
2286 var target = this.getEl();
2288 //this.handleSelection(e);
2293 nodes: this.getEl(),
2298 dragData.ddel = target.dom ; // the div element
2299 Roo.log(target.getWidth( ));
2300 dragData.ddel.style.width = target.getWidth() + 'px';
2307 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2308 * whole Element becomes the target, and this causes the drop gesture to append.
2310 getTargetFromEvent : function(e, dragged_card_el)
2312 var target = e.getTarget();
2313 while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2314 target = target.parentNode;
2325 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2326 // see if target is one of the 'cards'...
2329 //Roo.log(this.items.length);
2332 var last_card_n = 0;
2334 for (var i = 0;i< this.items.length;i++) {
2336 if (!this.items[i].el.hasClass('card')) {
2339 pos = this.getDropPoint(e, this.items[i].el.dom);
2341 cards_len = ret.cards.length;
2342 //Roo.log(this.items[i].el.dom.id);
2343 ret.cards.push(this.items[i]);
2345 if (ret.card_n < 0 && pos == 'above') {
2346 ret.position = cards_len > 0 ? 'below' : pos;
2347 ret.items_n = i > 0 ? i - 1 : 0;
2348 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2349 ret.card = ret.cards[ret.card_n];
2352 if (!ret.cards.length) {
2354 ret.position = 'below';
2358 // could not find a card.. stick it at the end..
2359 if (ret.card_n < 0) {
2360 ret.card_n = last_card_n;
2361 ret.card = ret.cards[last_card_n];
2362 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2363 ret.position = 'below';
2366 if (this.items[ret.items_n].el == dragged_card_el) {
2370 if (ret.position == 'below') {
2371 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2373 if (card_after && card_after.el == dragged_card_el) {
2380 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2382 if (card_before && card_before.el == dragged_card_el) {
2389 onNodeEnter : function(n, dd, e, data){
2392 onNodeOver : function(n, dd, e, data)
2395 var target_info = this.getTargetFromEvent(e,data.source.el);
2396 if (target_info === false) {
2397 this.dropPlaceHolder('hide');
2400 Roo.log(['getTargetFromEvent', target_info ]);
2403 this.dropPlaceHolder('show', target_info,data);
2407 onNodeOut : function(n, dd, e, data){
2408 this.dropPlaceHolder('hide');
2411 onNodeDrop : function(n, dd, e, data)
2414 // call drop - return false if
2416 // this could actually fail - if the Network drops..
2417 // we will ignore this at present..- client should probably reload
2418 // the whole set of cards if stuff like that fails.
2421 var info = this.getTargetFromEvent(e,data.source.el);
2422 if (info === false) {
2426 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2430 this.dropPlaceHolder('hide');
2432 // do the dom manipulation first..
2433 var dom = data.source.el.dom;
2434 dom.parentNode.removeChild(dom);
2437 if (info.card !== true) {
2438 var cardel = info.card.el.dom;
2440 if (info.position == 'above') {
2441 cardel.parentNode.insertBefore(dom, cardel);
2442 } else if (cardel.nextSibling) {
2443 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2445 cardel.parentNode.append(dom);
2448 // card container???
2449 this.bodyEl.dom.append(dom);
2452 //FIXME HANDLE card = true
2454 // add this to the correct place in items.
2458 // remove Card from items.
2460 var old_parent = data.source.parent();
2462 old_parent.items = old_parent.items.filter(function(e) { return e != data.source });
2464 if (this.items.length) {
2466 //Roo.log([info.items_n, info.position, this.items.length]);
2467 for (var i =0; i < this.items.length; i++) {
2468 if (i == info.items_n && info.position == 'above') {
2469 nitems.push(data.source);
2471 nitems.push(this.items[i]);
2472 if (i == info.items_n && info.position == 'below') {
2473 nitems.push(data.source);
2476 this.items = nitems;
2477 Roo.log(this.items);
2479 this.items.push(data.source);
2482 data.source.parentId = this.id;
2487 /** Decide whether to drop above or below a View node. */
2488 getDropPoint : function(e, n, dd)
2493 if (n == this.bodyEl.dom) {
2496 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2497 var c = t + (b - t) / 2;
2498 var y = Roo.lib.Event.getPageY(e);
2505 onToggleCollapse : function(e)
2507 if (this.collapsed) {
2508 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2509 this.collapsableEl.addClass('show');
2510 this.collapsed = false;
2513 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2514 this.collapsableEl.removeClass('show');
2515 this.collapsed = true;
2520 onToggleRotate : function(e)
2522 this.collapsableEl.removeClass('show');
2523 this.footerEl.removeClass('d-none');
2524 this.el.removeClass('roo-card-rotated');
2525 this.el.removeClass('d-none');
2528 this.collapsableEl.addClass('show');
2529 this.rotated = false;
2530 this.fireEvent('rotate', this, this.rotated);
2533 this.el.addClass('roo-card-rotated');
2534 this.footerEl.addClass('d-none');
2535 this.el.select('.roo-collapsable').removeClass('show');
2537 this.rotated = true;
2538 this.fireEvent('rotate', this, this.rotated);
2542 dropPlaceHolder: function (action, info, data)
2544 if (this.dropEl === false) {
2545 this.dropEl = Roo.DomHelper.append(this.bodyEl, {
2549 this.dropEl.removeClass(['d-none', 'd-block']);
2550 if (action == 'hide') {
2552 this.dropEl.addClass('d-none');
2555 // FIXME - info.card == true!!!
2556 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2558 if (info.card !== true) {
2559 var cardel = info.card.el.dom;
2561 if (info.position == 'above') {
2562 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2563 } else if (cardel.nextSibling) {
2564 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2566 cardel.parentNode.append(this.dropEl.dom);
2569 // card container???
2570 this.bodyEl.dom.append(this.dropEl.dom);
2573 this.dropEl.addClass('d-block roo-card-dropzone');
2575 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2582 setHeaderText: function(html)
2584 this.headerEl.dom.innerHTML = html;
2593 * Card header - holder for the card header elements.
2598 * @class Roo.bootstrap.CardHeader
2599 * @extends Roo.bootstrap.Element
2600 * Bootstrap CardHeader class
2602 * Create a new Card Header - that you can embed children into
2603 * @param {Object} config The config object
2606 Roo.bootstrap.CardHeader = function(config){
2607 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2610 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2613 container_method : 'getCardHeader'
2626 * Card footer - holder for the card footer elements.
2631 * @class Roo.bootstrap.CardFooter
2632 * @extends Roo.bootstrap.Element
2633 * Bootstrap CardFooter class
2635 * Create a new Card Footer - that you can embed children into
2636 * @param {Object} config The config object
2639 Roo.bootstrap.CardFooter = function(config){
2640 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2643 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2646 container_method : 'getCardFooter'
2659 * Card header - holder for the card header elements.
2664 * @class Roo.bootstrap.CardImageTop
2665 * @extends Roo.bootstrap.Element
2666 * Bootstrap CardImageTop class
2668 * Create a new Card Image Top container
2669 * @param {Object} config The config object
2672 Roo.bootstrap.CardImageTop = function(config){
2673 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2676 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2679 container_method : 'getCardImageTop'
2697 * @class Roo.bootstrap.Img
2698 * @extends Roo.bootstrap.Component
2699 * Bootstrap Img class
2700 * @cfg {Boolean} imgResponsive false | true
2701 * @cfg {String} border rounded | circle | thumbnail
2702 * @cfg {String} src image source
2703 * @cfg {String} alt image alternative text
2704 * @cfg {String} href a tag href
2705 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2706 * @cfg {String} xsUrl xs image source
2707 * @cfg {String} smUrl sm image source
2708 * @cfg {String} mdUrl md image source
2709 * @cfg {String} lgUrl lg image source
2712 * Create a new Input
2713 * @param {Object} config The config object
2716 Roo.bootstrap.Img = function(config){
2717 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2723 * The img click event for the img.
2724 * @param {Roo.EventObject} e
2730 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2732 imgResponsive: true,
2742 getAutoCreate : function()
2744 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2745 return this.createSingleImg();
2750 cls: 'roo-image-responsive-group',
2755 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2757 if(!_this[size + 'Url']){
2763 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2764 html: _this.html || cfg.html,
2765 src: _this[size + 'Url']
2768 img.cls += ' roo-image-responsive-' + size;
2770 var s = ['xs', 'sm', 'md', 'lg'];
2772 s.splice(s.indexOf(size), 1);
2774 Roo.each(s, function(ss){
2775 img.cls += ' hidden-' + ss;
2778 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2779 cfg.cls += ' img-' + _this.border;
2783 cfg.alt = _this.alt;
2796 a.target = _this.target;
2800 cfg.cn.push((_this.href) ? a : img);
2807 createSingleImg : function()
2811 cls: (this.imgResponsive) ? 'img-responsive' : '',
2813 src : 'about:blank' // just incase src get's set to undefined?!?
2816 cfg.html = this.html || cfg.html;
2818 cfg.src = this.src || cfg.src;
2820 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2821 cfg.cls += ' img-' + this.border;
2838 a.target = this.target;
2843 return (this.href) ? a : cfg;
2846 initEvents: function()
2849 this.el.on('click', this.onClick, this);
2854 onClick : function(e)
2856 Roo.log('img onclick');
2857 this.fireEvent('click', this, e);
2860 * Sets the url of the image - used to update it
2861 * @param {String} url the url of the image
2864 setSrc : function(url)
2868 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2869 this.el.dom.src = url;
2873 this.el.select('img', true).first().dom.src = url;
2889 * @class Roo.bootstrap.Link
2890 * @extends Roo.bootstrap.Component
2891 * Bootstrap Link Class
2892 * @cfg {String} alt image alternative text
2893 * @cfg {String} href a tag href
2894 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2895 * @cfg {String} html the content of the link.
2896 * @cfg {String} anchor name for the anchor link
2897 * @cfg {String} fa - favicon
2899 * @cfg {Boolean} preventDefault (true | false) default false
2903 * Create a new Input
2904 * @param {Object} config The config object
2907 Roo.bootstrap.Link = function(config){
2908 Roo.bootstrap.Link.superclass.constructor.call(this, config);
2914 * The img click event for the img.
2915 * @param {Roo.EventObject} e
2921 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
2925 preventDefault: false,
2931 getAutoCreate : function()
2933 var html = this.html || '';
2935 if (this.fa !== false) {
2936 html = '<i class="fa fa-' + this.fa + '"></i>';
2941 // anchor's do not require html/href...
2942 if (this.anchor === false) {
2944 cfg.href = this.href || '#';
2946 cfg.name = this.anchor;
2947 if (this.html !== false || this.fa !== false) {
2950 if (this.href !== false) {
2951 cfg.href = this.href;
2955 if(this.alt !== false){
2960 if(this.target !== false) {
2961 cfg.target = this.target;
2967 initEvents: function() {
2969 if(!this.href || this.preventDefault){
2970 this.el.on('click', this.onClick, this);
2974 onClick : function(e)
2976 if(this.preventDefault){
2979 //Roo.log('img onclick');
2980 this.fireEvent('click', this, e);
2993 * @class Roo.bootstrap.Header
2994 * @extends Roo.bootstrap.Component
2995 * Bootstrap Header class
2996 * @cfg {String} html content of header
2997 * @cfg {Number} level (1|2|3|4|5|6) default 1
3000 * Create a new Header
3001 * @param {Object} config The config object
3005 Roo.bootstrap.Header = function(config){
3006 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3009 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3017 getAutoCreate : function(){
3022 tag: 'h' + (1 *this.level),
3023 html: this.html || ''
3035 * Ext JS Library 1.1.1
3036 * Copyright(c) 2006-2007, Ext JS, LLC.
3038 * Originally Released Under LGPL - original licence link has changed is not relivant.
3041 * <script type="text/javascript">
3045 * @class Roo.bootstrap.MenuMgr
3046 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3049 Roo.bootstrap.MenuMgr = function(){
3050 var menus, active, groups = {}, attached = false, lastShow = new Date();
3052 // private - called when first menu is created
3055 active = new Roo.util.MixedCollection();
3056 Roo.get(document).addKeyListener(27, function(){
3057 if(active.length > 0){
3065 if(active && active.length > 0){
3066 var c = active.clone();
3076 if(active.length < 1){
3077 Roo.get(document).un("mouseup", onMouseDown);
3085 var last = active.last();
3086 lastShow = new Date();
3089 Roo.get(document).on("mouseup", onMouseDown);
3094 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3095 m.parentMenu.activeChild = m;
3096 }else if(last && last.isVisible()){
3097 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3102 function onBeforeHide(m){
3104 m.activeChild.hide();
3106 if(m.autoHideTimer){
3107 clearTimeout(m.autoHideTimer);
3108 delete m.autoHideTimer;
3113 function onBeforeShow(m){
3114 var pm = m.parentMenu;
3115 if(!pm && !m.allowOtherMenus){
3117 }else if(pm && pm.activeChild && active != m){
3118 pm.activeChild.hide();
3122 // private this should really trigger on mouseup..
3123 function onMouseDown(e){
3124 Roo.log("on Mouse Up");
3126 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3127 Roo.log("MenuManager hideAll");
3136 function onBeforeCheck(mi, state){
3138 var g = groups[mi.group];
3139 for(var i = 0, l = g.length; i < l; i++){
3141 g[i].setChecked(false);
3150 * Hides all menus that are currently visible
3152 hideAll : function(){
3157 register : function(menu){
3161 menus[menu.id] = menu;
3162 menu.on("beforehide", onBeforeHide);
3163 menu.on("hide", onHide);
3164 menu.on("beforeshow", onBeforeShow);
3165 menu.on("show", onShow);
3167 if(g && menu.events["checkchange"]){
3171 groups[g].push(menu);
3172 menu.on("checkchange", onCheck);
3177 * Returns a {@link Roo.menu.Menu} object
3178 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3179 * be used to generate and return a new Menu instance.
3181 get : function(menu){
3182 if(typeof menu == "string"){ // menu id
3184 }else if(menu.events){ // menu instance
3187 /*else if(typeof menu.length == 'number'){ // array of menu items?
3188 return new Roo.bootstrap.Menu({items:menu});
3189 }else{ // otherwise, must be a config
3190 return new Roo.bootstrap.Menu(menu);
3197 unregister : function(menu){
3198 delete menus[menu.id];
3199 menu.un("beforehide", onBeforeHide);
3200 menu.un("hide", onHide);
3201 menu.un("beforeshow", onBeforeShow);
3202 menu.un("show", onShow);
3204 if(g && menu.events["checkchange"]){
3205 groups[g].remove(menu);
3206 menu.un("checkchange", onCheck);
3211 registerCheckable : function(menuItem){
3212 var g = menuItem.group;
3217 groups[g].push(menuItem);
3218 menuItem.on("beforecheckchange", onBeforeCheck);
3223 unregisterCheckable : function(menuItem){
3224 var g = menuItem.group;
3226 groups[g].remove(menuItem);
3227 menuItem.un("beforecheckchange", onBeforeCheck);
3239 * @class Roo.bootstrap.Menu
3240 * @extends Roo.bootstrap.Component
3241 * Bootstrap Menu class - container for MenuItems
3242 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3243 * @cfg {bool} hidden if the menu should be hidden when rendered.
3244 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3245 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3249 * @param {Object} config The config object
3253 Roo.bootstrap.Menu = function(config){
3254 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3255 if (this.registerMenu && this.type != 'treeview') {
3256 Roo.bootstrap.MenuMgr.register(this);
3263 * Fires before this menu is displayed (return false to block)
3264 * @param {Roo.menu.Menu} this
3269 * Fires before this menu is hidden (return false to block)
3270 * @param {Roo.menu.Menu} this
3275 * Fires after this menu is displayed
3276 * @param {Roo.menu.Menu} this
3281 * Fires after this menu is hidden
3282 * @param {Roo.menu.Menu} this
3287 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3288 * @param {Roo.menu.Menu} this
3289 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3290 * @param {Roo.EventObject} e
3295 * Fires when the mouse is hovering over this menu
3296 * @param {Roo.menu.Menu} this
3297 * @param {Roo.EventObject} e
3298 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3303 * Fires when the mouse exits this menu
3304 * @param {Roo.menu.Menu} this
3305 * @param {Roo.EventObject} e
3306 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3311 * Fires when a menu item contained in this menu is clicked
3312 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3313 * @param {Roo.EventObject} e
3317 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3320 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3324 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3327 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3329 registerMenu : true,
3331 menuItems :false, // stores the menu items..
3341 getChildContainer : function() {
3345 getAutoCreate : function(){
3347 //if (['right'].indexOf(this.align)!==-1) {
3348 // cfg.cn[1].cls += ' pull-right'
3354 cls : 'dropdown-menu' ,
3355 style : 'z-index:1000'
3359 if (this.type === 'submenu') {
3360 cfg.cls = 'submenu active';
3362 if (this.type === 'treeview') {
3363 cfg.cls = 'treeview-menu';
3368 initEvents : function() {
3370 // Roo.log("ADD event");
3371 // Roo.log(this.triggerEl.dom);
3373 this.triggerEl.on('click', this.onTriggerClick, this);
3375 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3378 if (this.triggerEl.hasClass('nav-item')) {
3379 // dropdown toggle on the 'a' in BS4?
3380 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3382 this.triggerEl.addClass('dropdown-toggle');
3385 this.el.on('touchstart' , this.onTouch, this);
3387 this.el.on('click' , this.onClick, this);
3389 this.el.on("mouseover", this.onMouseOver, this);
3390 this.el.on("mouseout", this.onMouseOut, this);
3394 findTargetItem : function(e)
3396 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3400 //Roo.log(t); Roo.log(t.id);
3402 //Roo.log(this.menuitems);
3403 return this.menuitems.get(t.id);
3405 //return this.items.get(t.menuItemId);
3411 onTouch : function(e)
3413 Roo.log("menu.onTouch");
3414 //e.stopEvent(); this make the user popdown broken
3418 onClick : function(e)
3420 Roo.log("menu.onClick");
3422 var t = this.findTargetItem(e);
3423 if(!t || t.isContainer){
3428 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3429 if(t == this.activeItem && t.shouldDeactivate(e)){
3430 this.activeItem.deactivate();
3431 delete this.activeItem;
3435 this.setActiveItem(t, true);
3443 Roo.log('pass click event');
3447 this.fireEvent("click", this, t, e);
3451 if(!t.href.length || t.href == '#'){
3452 (function() { _this.hide(); }).defer(100);
3457 onMouseOver : function(e){
3458 var t = this.findTargetItem(e);
3461 // if(t.canActivate && !t.disabled){
3462 // this.setActiveItem(t, true);
3466 this.fireEvent("mouseover", this, e, t);
3468 isVisible : function(){
3469 return !this.hidden;
3471 onMouseOut : function(e){
3472 var t = this.findTargetItem(e);
3475 // if(t == this.activeItem && t.shouldDeactivate(e)){
3476 // this.activeItem.deactivate();
3477 // delete this.activeItem;
3480 this.fireEvent("mouseout", this, e, t);
3485 * Displays this menu relative to another element
3486 * @param {String/HTMLElement/Roo.Element} element The element to align to
3487 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3488 * the element (defaults to this.defaultAlign)
3489 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3491 show : function(el, pos, parentMenu)
3493 if (false === this.fireEvent("beforeshow", this)) {
3494 Roo.log("show canceled");
3497 this.parentMenu = parentMenu;
3502 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3505 * Displays this menu at a specific xy position
3506 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3507 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3509 showAt : function(xy, parentMenu, /* private: */_e){
3510 this.parentMenu = parentMenu;
3515 this.fireEvent("beforeshow", this);
3516 //xy = this.el.adjustForConstraints(xy);
3520 this.hideMenuItems();
3521 this.hidden = false;
3522 this.triggerEl.addClass('open');
3523 this.el.addClass('show');
3525 // reassign x when hitting right
3526 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3527 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3530 // reassign y when hitting bottom
3531 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3532 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3535 // but the list may align on trigger left or trigger top... should it be a properity?
3537 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3542 this.fireEvent("show", this);
3548 this.doFocus.defer(50, this);
3552 doFocus : function(){
3554 this.focusEl.focus();
3559 * Hides this menu and optionally all parent menus
3560 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3562 hide : function(deep)
3564 if (false === this.fireEvent("beforehide", this)) {
3565 Roo.log("hide canceled");
3568 this.hideMenuItems();
3569 if(this.el && this.isVisible()){
3571 if(this.activeItem){
3572 this.activeItem.deactivate();
3573 this.activeItem = null;
3575 this.triggerEl.removeClass('open');;
3576 this.el.removeClass('show');
3578 this.fireEvent("hide", this);
3580 if(deep === true && this.parentMenu){
3581 this.parentMenu.hide(true);
3585 onTriggerClick : function(e)
3587 Roo.log('trigger click');
3589 var target = e.getTarget();
3591 Roo.log(target.nodeName.toLowerCase());
3593 if(target.nodeName.toLowerCase() === 'i'){
3599 onTriggerPress : function(e)
3601 Roo.log('trigger press');
3602 //Roo.log(e.getTarget());
3603 // Roo.log(this.triggerEl.dom);
3605 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3606 var pel = Roo.get(e.getTarget());
3607 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3608 Roo.log('is treeview or dropdown?');
3612 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3616 if (this.isVisible()) {
3621 this.show(this.triggerEl, '?', false);
3624 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3631 hideMenuItems : function()
3633 Roo.log("hide Menu Items");
3638 this.el.select('.open',true).each(function(aa) {
3640 aa.removeClass('open');
3644 addxtypeChild : function (tree, cntr) {
3645 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3647 this.menuitems.add(comp);
3659 this.getEl().dom.innerHTML = '';
3660 this.menuitems.clear();
3674 * @class Roo.bootstrap.MenuItem
3675 * @extends Roo.bootstrap.Component
3676 * Bootstrap MenuItem class
3677 * @cfg {String} html the menu label
3678 * @cfg {String} href the link
3679 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3680 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3681 * @cfg {Boolean} active used on sidebars to highlight active itesm
3682 * @cfg {String} fa favicon to show on left of menu item.
3683 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3687 * Create a new MenuItem
3688 * @param {Object} config The config object
3692 Roo.bootstrap.MenuItem = function(config){
3693 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3698 * The raw click event for the entire grid.
3699 * @param {Roo.bootstrap.MenuItem} this
3700 * @param {Roo.EventObject} e
3706 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3710 preventDefault: false,
3711 isContainer : false,
3715 getAutoCreate : function(){
3717 if(this.isContainer){
3720 cls: 'dropdown-menu-item '
3730 cls : 'dropdown-item',
3735 if (this.fa !== false) {
3738 cls : 'fa fa-' + this.fa
3747 cls: 'dropdown-menu-item',
3750 if (this.parent().type == 'treeview') {
3751 cfg.cls = 'treeview-menu';
3754 cfg.cls += ' active';
3759 anc.href = this.href || cfg.cn[0].href ;
3760 ctag.html = this.html || cfg.cn[0].html ;
3764 initEvents: function()
3766 if (this.parent().type == 'treeview') {
3767 this.el.select('a').on('click', this.onClick, this);
3771 this.menu.parentType = this.xtype;
3772 this.menu.triggerEl = this.el;
3773 this.menu = this.addxtype(Roo.apply({}, this.menu));
3777 onClick : function(e)
3779 Roo.log('item on click ');
3781 if(this.preventDefault){
3784 //this.parent().hideMenuItems();
3786 this.fireEvent('click', this, e);
3805 * @class Roo.bootstrap.MenuSeparator
3806 * @extends Roo.bootstrap.Component
3807 * Bootstrap MenuSeparator class
3810 * Create a new MenuItem
3811 * @param {Object} config The config object
3815 Roo.bootstrap.MenuSeparator = function(config){
3816 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3819 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3821 getAutoCreate : function(){
3840 * @class Roo.bootstrap.Modal
3841 * @extends Roo.bootstrap.Component
3842 * Bootstrap Modal class
3843 * @cfg {String} title Title of dialog
3844 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3845 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3846 * @cfg {Boolean} specificTitle default false
3847 * @cfg {Array} buttons Array of buttons or standard button set..
3848 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3849 * @cfg {Boolean} animate default true
3850 * @cfg {Boolean} allow_close default true
3851 * @cfg {Boolean} fitwindow default false
3852 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3853 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3854 * @cfg {String} size (sm|lg|xl) default empty
3855 * @cfg {Number} max_width set the max width of modal
3856 * @cfg {Boolean} editableTitle can the title be edited
3861 * Create a new Modal Dialog
3862 * @param {Object} config The config object
3865 Roo.bootstrap.Modal = function(config){
3866 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3871 * The raw btnclick event for the button
3872 * @param {Roo.EventObject} e
3877 * Fire when dialog resize
3878 * @param {Roo.bootstrap.Modal} this
3879 * @param {Roo.EventObject} e
3883 * @event titlechanged
3884 * Fire when the editable title has been changed
3885 * @param {Roo.bootstrap.Modal} this
3886 * @param {Roo.EventObject} value
3888 "titlechanged" : true
3891 this.buttons = this.buttons || [];
3894 this.tmpl = Roo.factory(this.tmpl);
3899 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3901 title : 'test dialog',
3911 specificTitle: false,
3913 buttonPosition: 'right',
3935 editableTitle : false,
3937 onRender : function(ct, position)
3939 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3942 var cfg = Roo.apply({}, this.getAutoCreate());
3945 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3947 //if (!cfg.name.length) {
3951 cfg.cls += ' ' + this.cls;
3954 cfg.style = this.style;
3956 this.el = Roo.get(document.body).createChild(cfg, position);
3958 //var type = this.el.dom.type;
3961 if(this.tabIndex !== undefined){
3962 this.el.dom.setAttribute('tabIndex', this.tabIndex);
3965 this.dialogEl = this.el.select('.modal-dialog',true).first();
3966 this.bodyEl = this.el.select('.modal-body',true).first();
3967 this.closeEl = this.el.select('.modal-header .close', true).first();
3968 this.headerEl = this.el.select('.modal-header',true).first();
3969 this.titleEl = this.el.select('.modal-title',true).first();
3970 this.footerEl = this.el.select('.modal-footer',true).first();
3972 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3974 //this.el.addClass("x-dlg-modal");
3976 if (this.buttons.length) {
3977 Roo.each(this.buttons, function(bb) {
3978 var b = Roo.apply({}, bb);
3979 b.xns = b.xns || Roo.bootstrap;
3980 b.xtype = b.xtype || 'Button';
3981 if (typeof(b.listeners) == 'undefined') {
3982 b.listeners = { click : this.onButtonClick.createDelegate(this) };
3985 var btn = Roo.factory(b);
3987 btn.render(this.getButtonContainer());
3991 // render the children.
3994 if(typeof(this.items) != 'undefined'){
3995 var items = this.items;
3998 for(var i =0;i < items.length;i++) {
3999 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4003 this.items = nitems;
4005 // where are these used - they used to be body/close/footer
4009 //this.el.addClass([this.fieldClass, this.cls]);
4013 getAutoCreate : function()
4015 // we will default to modal-body-overflow - might need to remove or make optional later.
4017 cls : 'modal-body enable-modal-body-overflow ',
4018 html : this.html || ''
4023 cls : 'modal-title',
4027 if(this.specificTitle){ // WTF is this?
4032 if (this.allow_close && Roo.bootstrap.version == 3) {
4042 if (this.editableTitle) {
4044 cls: 'form-control roo-editable-title d-none',
4050 if (this.allow_close && Roo.bootstrap.version == 4) {
4060 if(this.size.length){
4061 size = 'modal-' + this.size;
4064 var footer = Roo.bootstrap.version == 3 ?
4066 cls : 'modal-footer',
4070 cls: 'btn-' + this.buttonPosition
4075 { // BS4 uses mr-auto on left buttons....
4076 cls : 'modal-footer'
4087 cls: "modal-dialog " + size,
4090 cls : "modal-content",
4093 cls : 'modal-header',
4108 modal.cls += ' fade';
4114 getChildContainer : function() {
4119 getButtonContainer : function() {
4121 return Roo.bootstrap.version == 4 ?
4122 this.el.select('.modal-footer',true).first()
4123 : this.el.select('.modal-footer div',true).first();
4126 initEvents : function()
4128 if (this.allow_close) {
4129 this.closeEl.on('click', this.hide, this);
4131 Roo.EventManager.onWindowResize(this.resize, this, true);
4132 if (this.editableTitle) {
4133 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4134 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4135 this.headerEditEl.on('keyup', function(e) {
4136 if(e.isNavKeyPress()){
4137 this.toggleHeaderInput(false)
4140 this.headerEditEl.on('blur', function(e) {
4141 this.toggleHeaderInput(false)
4150 this.maskEl.setSize(
4151 Roo.lib.Dom.getViewWidth(true),
4152 Roo.lib.Dom.getViewHeight(true)
4155 if (this.fitwindow) {
4159 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4160 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4165 if(this.max_width !== 0) {
4167 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4170 this.setSize(w, this.height);
4174 if(this.max_height) {
4175 this.setSize(w,Math.min(
4177 Roo.lib.Dom.getViewportHeight(true) - 60
4183 if(!this.fit_content) {
4184 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4188 this.setSize(w, Math.min(
4190 this.headerEl.getHeight() +
4191 this.footerEl.getHeight() +
4192 this.getChildHeight(this.bodyEl.dom.childNodes),
4193 Roo.lib.Dom.getViewportHeight(true) - 60)
4199 setSize : function(w,h)
4210 if (!this.rendered) {
4214 //this.el.setStyle('display', 'block');
4215 this.el.removeClass('hideing');
4216 this.el.dom.style.display='block';
4218 Roo.get(document.body).addClass('modal-open');
4220 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4223 this.el.addClass('show');
4224 this.el.addClass('in');
4227 this.el.addClass('show');
4228 this.el.addClass('in');
4231 // not sure how we can show data in here..
4233 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4236 Roo.get(document.body).addClass("x-body-masked");
4238 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4239 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4240 this.maskEl.dom.style.display = 'block';
4241 this.maskEl.addClass('show');
4246 this.fireEvent('show', this);
4248 // set zindex here - otherwise it appears to be ignored...
4249 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4252 this.items.forEach( function(e) {
4253 e.layout ? e.layout() : false;
4261 if(this.fireEvent("beforehide", this) !== false){
4263 this.maskEl.removeClass('show');
4265 this.maskEl.dom.style.display = '';
4266 Roo.get(document.body).removeClass("x-body-masked");
4267 this.el.removeClass('in');
4268 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4270 if(this.animate){ // why
4271 this.el.addClass('hideing');
4272 this.el.removeClass('show');
4274 if (!this.el.hasClass('hideing')) {
4275 return; // it's been shown again...
4278 this.el.dom.style.display='';
4280 Roo.get(document.body).removeClass('modal-open');
4281 this.el.removeClass('hideing');
4285 this.el.removeClass('show');
4286 this.el.dom.style.display='';
4287 Roo.get(document.body).removeClass('modal-open');
4290 this.fireEvent('hide', this);
4293 isVisible : function()
4296 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4300 addButton : function(str, cb)
4304 var b = Roo.apply({}, { html : str } );
4305 b.xns = b.xns || Roo.bootstrap;
4306 b.xtype = b.xtype || 'Button';
4307 if (typeof(b.listeners) == 'undefined') {
4308 b.listeners = { click : cb.createDelegate(this) };
4311 var btn = Roo.factory(b);
4313 btn.render(this.getButtonContainer());
4319 setDefaultButton : function(btn)
4321 //this.el.select('.modal-footer').()
4324 resizeTo: function(w,h)
4326 this.dialogEl.setWidth(w);
4328 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4330 this.bodyEl.setHeight(h - diff);
4332 this.fireEvent('resize', this);
4335 setContentSize : function(w, h)
4339 onButtonClick: function(btn,e)
4342 this.fireEvent('btnclick', btn.name, e);
4345 * Set the title of the Dialog
4346 * @param {String} str new Title
4348 setTitle: function(str) {
4349 this.titleEl.dom.innerHTML = str;
4353 * Set the body of the Dialog
4354 * @param {String} str new Title
4356 setBody: function(str) {
4357 this.bodyEl.dom.innerHTML = str;
4360 * Set the body of the Dialog using the template
4361 * @param {Obj} data - apply this data to the template and replace the body contents.
4363 applyBody: function(obj)
4366 Roo.log("Error - using apply Body without a template");
4369 this.tmpl.overwrite(this.bodyEl, obj);
4372 getChildHeight : function(child_nodes)
4376 child_nodes.length == 0
4381 var child_height = 0;
4383 for(var i = 0; i < child_nodes.length; i++) {
4386 * for modal with tabs...
4387 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4389 var layout_childs = child_nodes[i].childNodes;
4391 for(var j = 0; j < layout_childs.length; j++) {
4393 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4395 var layout_body_childs = layout_childs[j].childNodes;
4397 for(var k = 0; k < layout_body_childs.length; k++) {
4399 if(layout_body_childs[k].classList.contains('navbar')) {
4400 child_height += layout_body_childs[k].offsetHeight;
4404 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4406 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4408 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4410 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4411 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4426 child_height += child_nodes[i].offsetHeight;
4427 // Roo.log(child_nodes[i].offsetHeight);
4430 return child_height;
4432 toggleHeaderInput : function(is_edit)
4435 if (is_edit && this.is_header_editing) {
4436 return; // already editing..
4440 this.headerEditEl.dom.value = this.title;
4441 this.headerEditEl.removeClass('d-none');
4442 this.headerEditEl.dom.focus();
4443 this.titleEl.addClass('d-none');
4445 this.is_header_editing = true;
4448 // flip back to not editing.
4449 this.title = this.headerEditEl.dom.value;
4450 this.headerEditEl.addClass('d-none');
4451 this.titleEl.removeClass('d-none');
4452 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4453 this.is_header_editing = false;
4454 this.fireEvent('titlechanged', this, this.title);
4463 Roo.apply(Roo.bootstrap.Modal, {
4465 * Button config that displays a single OK button
4474 * Button config that displays Yes and No buttons
4490 * Button config that displays OK and Cancel buttons
4505 * Button config that displays Yes, No and Cancel buttons
4530 * messagebox - can be used as a replace
4534 * @class Roo.MessageBox
4535 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4539 Roo.Msg.alert('Status', 'Changes saved successfully.');
4541 // Prompt for user data:
4542 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4544 // process text value...
4548 // Show a dialog using config options:
4550 title:'Save Changes?',
4551 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4552 buttons: Roo.Msg.YESNOCANCEL,
4559 Roo.bootstrap.MessageBox = function(){
4560 var dlg, opt, mask, waitTimer;
4561 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4562 var buttons, activeTextEl, bwidth;
4566 var handleButton = function(button){
4568 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4572 var handleHide = function(){
4574 dlg.el.removeClass(opt.cls);
4577 // Roo.TaskMgr.stop(waitTimer);
4578 // waitTimer = null;
4583 var updateButtons = function(b){
4586 buttons["ok"].hide();
4587 buttons["cancel"].hide();
4588 buttons["yes"].hide();
4589 buttons["no"].hide();
4590 dlg.footerEl.hide();
4594 dlg.footerEl.show();
4595 for(var k in buttons){
4596 if(typeof buttons[k] != "function"){
4599 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4600 width += buttons[k].el.getWidth()+15;
4610 var handleEsc = function(d, k, e){
4611 if(opt && opt.closable !== false){
4621 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4622 * @return {Roo.BasicDialog} The BasicDialog element
4624 getDialog : function(){
4626 dlg = new Roo.bootstrap.Modal( {
4629 //constraintoviewport:false,
4631 //collapsible : false,
4636 //buttonAlign:"center",
4637 closeClick : function(){
4638 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4641 handleButton("cancel");
4646 dlg.on("hide", handleHide);
4648 //dlg.addKeyListener(27, handleEsc);
4650 this.buttons = buttons;
4651 var bt = this.buttonText;
4652 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4653 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4654 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4655 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4657 bodyEl = dlg.bodyEl.createChild({
4659 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4660 '<textarea class="roo-mb-textarea"></textarea>' +
4661 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4663 msgEl = bodyEl.dom.firstChild;
4664 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4665 textboxEl.enableDisplayMode();
4666 textboxEl.addKeyListener([10,13], function(){
4667 if(dlg.isVisible() && opt && opt.buttons){
4670 }else if(opt.buttons.yes){
4671 handleButton("yes");
4675 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4676 textareaEl.enableDisplayMode();
4677 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4678 progressEl.enableDisplayMode();
4680 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4681 var pf = progressEl.dom.firstChild;
4683 pp = Roo.get(pf.firstChild);
4684 pp.setHeight(pf.offsetHeight);
4692 * Updates the message box body text
4693 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4694 * the XHTML-compliant non-breaking space character '&#160;')
4695 * @return {Roo.MessageBox} This message box
4697 updateText : function(text)
4699 if(!dlg.isVisible() && !opt.width){
4700 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4701 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4703 msgEl.innerHTML = text || ' ';
4705 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4706 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4708 Math.min(opt.width || cw , this.maxWidth),
4709 Math.max(opt.minWidth || this.minWidth, bwidth)
4712 activeTextEl.setWidth(w);
4714 if(dlg.isVisible()){
4715 dlg.fixedcenter = false;
4717 // to big, make it scroll. = But as usual stupid IE does not support
4720 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4721 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4722 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4724 bodyEl.dom.style.height = '';
4725 bodyEl.dom.style.overflowY = '';
4728 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4730 bodyEl.dom.style.overflowX = '';
4733 dlg.setContentSize(w, bodyEl.getHeight());
4734 if(dlg.isVisible()){
4735 dlg.fixedcenter = true;
4741 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4742 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4743 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4744 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4745 * @return {Roo.MessageBox} This message box
4747 updateProgress : function(value, text){
4749 this.updateText(text);
4752 if (pp) { // weird bug on my firefox - for some reason this is not defined
4753 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4754 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4760 * Returns true if the message box is currently displayed
4761 * @return {Boolean} True if the message box is visible, else false
4763 isVisible : function(){
4764 return dlg && dlg.isVisible();
4768 * Hides the message box if it is displayed
4771 if(this.isVisible()){
4777 * Displays a new message box, or reinitializes an existing message box, based on the config options
4778 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4779 * The following config object properties are supported:
4781 Property Type Description
4782 ---------- --------------- ------------------------------------------------------------------------------------
4783 animEl String/Element An id or Element from which the message box should animate as it opens and
4784 closes (defaults to undefined)
4785 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4786 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4787 closable Boolean False to hide the top-right close button (defaults to true). Note that
4788 progress and wait dialogs will ignore this property and always hide the
4789 close button as they can only be closed programmatically.
4790 cls String A custom CSS class to apply to the message box element
4791 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4792 displayed (defaults to 75)
4793 fn Function A callback function to execute after closing the dialog. The arguments to the
4794 function will be btn (the name of the button that was clicked, if applicable,
4795 e.g. "ok"), and text (the value of the active text field, if applicable).
4796 Progress and wait dialogs will ignore this option since they do not respond to
4797 user actions and can only be closed programmatically, so any required function
4798 should be called by the same code after it closes the dialog.
4799 icon String A CSS class that provides a background image to be used as an icon for
4800 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4801 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4802 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4803 modal Boolean False to allow user interaction with the page while the message box is
4804 displayed (defaults to true)
4805 msg String A string that will replace the existing message box body text (defaults
4806 to the XHTML-compliant non-breaking space character ' ')
4807 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4808 progress Boolean True to display a progress bar (defaults to false)
4809 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4810 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4811 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4812 title String The title text
4813 value String The string value to set into the active textbox element if displayed
4814 wait Boolean True to display a progress bar (defaults to false)
4815 width Number The width of the dialog in pixels
4822 msg: 'Please enter your address:',
4824 buttons: Roo.MessageBox.OKCANCEL,
4827 animEl: 'addAddressBtn'
4830 * @param {Object} config Configuration options
4831 * @return {Roo.MessageBox} This message box
4833 show : function(options)
4836 // this causes nightmares if you show one dialog after another
4837 // especially on callbacks..
4839 if(this.isVisible()){
4842 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4843 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4844 Roo.log("New Dialog Message:" + options.msg )
4845 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4846 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4849 var d = this.getDialog();
4851 d.setTitle(opt.title || " ");
4852 d.closeEl.setDisplayed(opt.closable !== false);
4853 activeTextEl = textboxEl;
4854 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4859 textareaEl.setHeight(typeof opt.multiline == "number" ?
4860 opt.multiline : this.defaultTextHeight);
4861 activeTextEl = textareaEl;
4870 progressEl.setDisplayed(opt.progress === true);
4872 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4874 this.updateProgress(0);
4875 activeTextEl.dom.value = opt.value || "";
4877 dlg.setDefaultButton(activeTextEl);
4879 var bs = opt.buttons;
4883 }else if(bs && bs.yes){
4884 db = buttons["yes"];
4886 dlg.setDefaultButton(db);
4888 bwidth = updateButtons(opt.buttons);
4889 this.updateText(opt.msg);
4891 d.el.addClass(opt.cls);
4893 d.proxyDrag = opt.proxyDrag === true;
4894 d.modal = opt.modal !== false;
4895 d.mask = opt.modal !== false ? mask : false;
4897 // force it to the end of the z-index stack so it gets a cursor in FF
4898 document.body.appendChild(dlg.el.dom);
4899 d.animateTarget = null;
4900 d.show(options.animEl);
4906 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
4907 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4908 * and closing the message box when the process is complete.
4909 * @param {String} title The title bar text
4910 * @param {String} msg The message box body text
4911 * @return {Roo.MessageBox} This message box
4913 progress : function(title, msg){
4920 minWidth: this.minProgressWidth,
4927 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4928 * If a callback function is passed it will be called after the user clicks the button, and the
4929 * id of the button that was clicked will be passed as the only parameter to the callback
4930 * (could also be the top-right close button).
4931 * @param {String} title The title bar text
4932 * @param {String} msg The message box body text
4933 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4934 * @param {Object} scope (optional) The scope of the callback function
4935 * @return {Roo.MessageBox} This message box
4937 alert : function(title, msg, fn, scope)
4952 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
4953 * interaction while waiting for a long-running process to complete that does not have defined intervals.
4954 * You are responsible for closing the message box when the process is complete.
4955 * @param {String} msg The message box body text
4956 * @param {String} title (optional) The title bar text
4957 * @return {Roo.MessageBox} This message box
4959 wait : function(msg, title){
4970 waitTimer = Roo.TaskMgr.start({
4972 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4980 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4981 * If a callback function is passed it will be called after the user clicks either button, and the id of the
4982 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4983 * @param {String} title The title bar text
4984 * @param {String} msg The message box body text
4985 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4986 * @param {Object} scope (optional) The scope of the callback function
4987 * @return {Roo.MessageBox} This message box
4989 confirm : function(title, msg, fn, scope){
4993 buttons: this.YESNO,
5002 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5003 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5004 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5005 * (could also be the top-right close button) and the text that was entered will be passed as the two
5006 * parameters to the callback.
5007 * @param {String} title The title bar text
5008 * @param {String} msg The message box body text
5009 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5010 * @param {Object} scope (optional) The scope of the callback function
5011 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5012 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5013 * @return {Roo.MessageBox} This message box
5015 prompt : function(title, msg, fn, scope, multiline){
5019 buttons: this.OKCANCEL,
5024 multiline: multiline,
5031 * Button config that displays a single OK button
5036 * Button config that displays Yes and No buttons
5039 YESNO : {yes:true, no:true},
5041 * Button config that displays OK and Cancel buttons
5044 OKCANCEL : {ok:true, cancel:true},
5046 * Button config that displays Yes, No and Cancel buttons
5049 YESNOCANCEL : {yes:true, no:true, cancel:true},
5052 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5055 defaultTextHeight : 75,
5057 * The maximum width in pixels of the message box (defaults to 600)
5062 * The minimum width in pixels of the message box (defaults to 100)
5067 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5068 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5071 minProgressWidth : 250,
5073 * An object containing the default button text strings that can be overriden for localized language support.
5074 * Supported properties are: ok, cancel, yes and no.
5075 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5088 * Shorthand for {@link Roo.MessageBox}
5090 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5091 Roo.Msg = Roo.Msg || Roo.MessageBox;
5100 * @class Roo.bootstrap.Navbar
5101 * @extends Roo.bootstrap.Component
5102 * Bootstrap Navbar class
5105 * Create a new Navbar
5106 * @param {Object} config The config object
5110 Roo.bootstrap.Navbar = function(config){
5111 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5115 * @event beforetoggle
5116 * Fire before toggle the menu
5117 * @param {Roo.EventObject} e
5119 "beforetoggle" : true
5123 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5132 getAutoCreate : function(){
5135 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5139 initEvents :function ()
5141 //Roo.log(this.el.select('.navbar-toggle',true));
5142 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5149 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5151 var size = this.el.getSize();
5152 this.maskEl.setSize(size.width, size.height);
5153 this.maskEl.enableDisplayMode("block");
5162 getChildContainer : function()
5164 if (this.el && this.el.select('.collapse').getCount()) {
5165 return this.el.select('.collapse',true).first();
5180 onToggle : function()
5183 if(this.fireEvent('beforetoggle', this) === false){
5186 var ce = this.el.select('.navbar-collapse',true).first();
5188 if (!ce.hasClass('show')) {
5198 * Expand the navbar pulldown
5200 expand : function ()
5203 var ce = this.el.select('.navbar-collapse',true).first();
5204 if (ce.hasClass('collapsing')) {
5207 ce.dom.style.height = '';
5209 ce.addClass('in'); // old...
5210 ce.removeClass('collapse');
5211 ce.addClass('show');
5212 var h = ce.getHeight();
5214 ce.removeClass('show');
5215 // at this point we should be able to see it..
5216 ce.addClass('collapsing');
5218 ce.setHeight(0); // resize it ...
5219 ce.on('transitionend', function() {
5220 //Roo.log('done transition');
5221 ce.removeClass('collapsing');
5222 ce.addClass('show');
5223 ce.removeClass('collapse');
5225 ce.dom.style.height = '';
5226 }, this, { single: true} );
5228 ce.dom.scrollTop = 0;
5231 * Collapse the navbar pulldown
5233 collapse : function()
5235 var ce = this.el.select('.navbar-collapse',true).first();
5237 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5238 // it's collapsed or collapsing..
5241 ce.removeClass('in'); // old...
5242 ce.setHeight(ce.getHeight());
5243 ce.removeClass('show');
5244 ce.addClass('collapsing');
5246 ce.on('transitionend', function() {
5247 ce.dom.style.height = '';
5248 ce.removeClass('collapsing');
5249 ce.addClass('collapse');
5250 }, this, { single: true} );
5270 * @class Roo.bootstrap.NavSimplebar
5271 * @extends Roo.bootstrap.Navbar
5272 * Bootstrap Sidebar class
5274 * @cfg {Boolean} inverse is inverted color
5276 * @cfg {String} type (nav | pills | tabs)
5277 * @cfg {Boolean} arrangement stacked | justified
5278 * @cfg {String} align (left | right) alignment
5280 * @cfg {Boolean} main (true|false) main nav bar? default false
5281 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5283 * @cfg {String} tag (header|footer|nav|div) default is nav
5285 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5289 * Create a new Sidebar
5290 * @param {Object} config The config object
5294 Roo.bootstrap.NavSimplebar = function(config){
5295 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5298 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5314 getAutoCreate : function(){
5318 tag : this.tag || 'div',
5319 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5321 if (['light','white'].indexOf(this.weight) > -1) {
5322 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5324 cfg.cls += ' bg-' + this.weight;
5327 cfg.cls += ' navbar-inverse';
5331 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5333 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5342 cls: 'nav nav-' + this.xtype,
5348 this.type = this.type || 'nav';
5349 if (['tabs','pills'].indexOf(this.type) != -1) {
5350 cfg.cn[0].cls += ' nav-' + this.type
5354 if (this.type!=='nav') {
5355 Roo.log('nav type must be nav/tabs/pills')
5357 cfg.cn[0].cls += ' navbar-nav'
5363 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5364 cfg.cn[0].cls += ' nav-' + this.arrangement;
5368 if (this.align === 'right') {
5369 cfg.cn[0].cls += ' navbar-right';
5394 * navbar-expand-md fixed-top
5398 * @class Roo.bootstrap.NavHeaderbar
5399 * @extends Roo.bootstrap.NavSimplebar
5400 * Bootstrap Sidebar class
5402 * @cfg {String} brand what is brand
5403 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5404 * @cfg {String} brand_href href of the brand
5405 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5406 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5407 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5408 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5411 * Create a new Sidebar
5412 * @param {Object} config The config object
5416 Roo.bootstrap.NavHeaderbar = function(config){
5417 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5421 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5428 desktopCenter : false,
5431 getAutoCreate : function(){
5434 tag: this.nav || 'nav',
5435 cls: 'navbar navbar-expand-md',
5441 if (this.desktopCenter) {
5442 cn.push({cls : 'container', cn : []});
5450 cls: 'navbar-toggle navbar-toggler',
5451 'data-toggle': 'collapse',
5456 html: 'Toggle navigation'
5460 cls: 'icon-bar navbar-toggler-icon'
5473 cn.push( Roo.bootstrap.version == 4 ? btn : {
5475 cls: 'navbar-header',
5484 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5488 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5490 if (['light','white'].indexOf(this.weight) > -1) {
5491 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5493 cfg.cls += ' bg-' + this.weight;
5496 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5497 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5499 // tag can override this..
5501 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5504 if (this.brand !== '') {
5505 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5506 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5508 href: this.brand_href ? this.brand_href : '#',
5509 cls: 'navbar-brand',
5517 cfg.cls += ' main-nav';
5525 getHeaderChildContainer : function()
5527 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5528 return this.el.select('.navbar-header',true).first();
5531 return this.getChildContainer();
5534 getChildContainer : function()
5537 return this.el.select('.roo-navbar-collapse',true).first();
5542 initEvents : function()
5544 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5546 if (this.autohide) {
5551 Roo.get(document).on('scroll',function(e) {
5552 var ns = Roo.get(document).getScroll().top;
5553 var os = prevScroll;
5557 ft.removeClass('slideDown');
5558 ft.addClass('slideUp');
5561 ft.removeClass('slideUp');
5562 ft.addClass('slideDown');
5583 * @class Roo.bootstrap.NavSidebar
5584 * @extends Roo.bootstrap.Navbar
5585 * Bootstrap Sidebar class
5588 * Create a new Sidebar
5589 * @param {Object} config The config object
5593 Roo.bootstrap.NavSidebar = function(config){
5594 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5597 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5599 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5601 getAutoCreate : function(){
5606 cls: 'sidebar sidebar-nav'
5628 * @class Roo.bootstrap.NavGroup
5629 * @extends Roo.bootstrap.Component
5630 * Bootstrap NavGroup class
5631 * @cfg {String} align (left|right)
5632 * @cfg {Boolean} inverse
5633 * @cfg {String} type (nav|pills|tab) default nav
5634 * @cfg {String} navId - reference Id for navbar.
5638 * Create a new nav group
5639 * @param {Object} config The config object
5642 Roo.bootstrap.NavGroup = function(config){
5643 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5646 Roo.bootstrap.NavGroup.register(this);
5650 * Fires when the active item changes
5651 * @param {Roo.bootstrap.NavGroup} this
5652 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5653 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5660 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5671 getAutoCreate : function()
5673 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5679 if (Roo.bootstrap.version == 4) {
5680 if (['tabs','pills'].indexOf(this.type) != -1) {
5681 cfg.cls += ' nav-' + this.type;
5683 // trying to remove so header bar can right align top?
5684 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5685 // do not use on header bar...
5686 cfg.cls += ' navbar-nav';
5691 if (['tabs','pills'].indexOf(this.type) != -1) {
5692 cfg.cls += ' nav-' + this.type
5694 if (this.type !== 'nav') {
5695 Roo.log('nav type must be nav/tabs/pills')
5697 cfg.cls += ' navbar-nav'
5701 if (this.parent() && this.parent().sidebar) {
5704 cls: 'dashboard-menu sidebar-menu'
5710 if (this.form === true) {
5713 cls: 'navbar-form form-inline'
5715 //nav navbar-right ml-md-auto
5716 if (this.align === 'right') {
5717 cfg.cls += ' navbar-right ml-md-auto';
5719 cfg.cls += ' navbar-left';
5723 if (this.align === 'right') {
5724 cfg.cls += ' navbar-right ml-md-auto';
5726 cfg.cls += ' mr-auto';
5730 cfg.cls += ' navbar-inverse';
5738 * sets the active Navigation item
5739 * @param {Roo.bootstrap.NavItem} the new current navitem
5741 setActiveItem : function(item)
5744 Roo.each(this.navItems, function(v){
5749 v.setActive(false, true);
5756 item.setActive(true, true);
5757 this.fireEvent('changed', this, item, prev);
5762 * gets the active Navigation item
5763 * @return {Roo.bootstrap.NavItem} the current navitem
5765 getActive : function()
5769 Roo.each(this.navItems, function(v){
5780 indexOfNav : function()
5784 Roo.each(this.navItems, function(v,i){
5795 * adds a Navigation item
5796 * @param {Roo.bootstrap.NavItem} the navitem to add
5798 addItem : function(cfg)
5800 if (this.form && Roo.bootstrap.version == 4) {
5803 var cn = new Roo.bootstrap.NavItem(cfg);
5805 cn.parentId = this.id;
5806 cn.onRender(this.el, null);
5810 * register a Navigation item
5811 * @param {Roo.bootstrap.NavItem} the navitem to add
5813 register : function(item)
5815 this.navItems.push( item);
5816 item.navId = this.navId;
5821 * clear all the Navigation item
5824 clearAll : function()
5827 this.el.dom.innerHTML = '';
5830 getNavItem: function(tabId)
5833 Roo.each(this.navItems, function(e) {
5834 if (e.tabId == tabId) {
5844 setActiveNext : function()
5846 var i = this.indexOfNav(this.getActive());
5847 if (i > this.navItems.length) {
5850 this.setActiveItem(this.navItems[i+1]);
5852 setActivePrev : function()
5854 var i = this.indexOfNav(this.getActive());
5858 this.setActiveItem(this.navItems[i-1]);
5860 clearWasActive : function(except) {
5861 Roo.each(this.navItems, function(e) {
5862 if (e.tabId != except.tabId && e.was_active) {
5863 e.was_active = false;
5870 getWasActive : function ()
5873 Roo.each(this.navItems, function(e) {
5888 Roo.apply(Roo.bootstrap.NavGroup, {
5892 * register a Navigation Group
5893 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5895 register : function(navgrp)
5897 this.groups[navgrp.navId] = navgrp;
5901 * fetch a Navigation Group based on the navigation ID
5902 * @param {string} the navgroup to add
5903 * @returns {Roo.bootstrap.NavGroup} the navgroup
5905 get: function(navId) {
5906 if (typeof(this.groups[navId]) == 'undefined') {
5908 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5910 return this.groups[navId] ;
5925 * @class Roo.bootstrap.NavItem
5926 * @extends Roo.bootstrap.Component
5927 * Bootstrap Navbar.NavItem class
5928 * @cfg {String} href link to
5929 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5931 * @cfg {String} html content of button
5932 * @cfg {String} badge text inside badge
5933 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5934 * @cfg {String} glyphicon DEPRICATED - use fa
5935 * @cfg {String} icon DEPRICATED - use fa
5936 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5937 * @cfg {Boolean} active Is item active
5938 * @cfg {Boolean} disabled Is item disabled
5940 * @cfg {Boolean} preventDefault (true | false) default false
5941 * @cfg {String} tabId the tab that this item activates.
5942 * @cfg {String} tagtype (a|span) render as a href or span?
5943 * @cfg {Boolean} animateRef (true|false) link to element default false
5946 * Create a new Navbar Item
5947 * @param {Object} config The config object
5949 Roo.bootstrap.NavItem = function(config){
5950 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5955 * The raw click event for the entire grid.
5956 * @param {Roo.EventObject} e
5961 * Fires when the active item active state changes
5962 * @param {Roo.bootstrap.NavItem} this
5963 * @param {boolean} state the new state
5969 * Fires when scroll to element
5970 * @param {Roo.bootstrap.NavItem} this
5971 * @param {Object} options
5972 * @param {Roo.EventObject} e
5980 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
5989 preventDefault : false,
5997 button_outline : false,
6001 getAutoCreate : function(){
6009 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6011 if (this.disabled) {
6012 cfg.cls += ' disabled';
6016 if (this.button_weight.length) {
6017 cfg.tag = this.href ? 'a' : 'button';
6018 cfg.html = this.html || '';
6019 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6021 cfg.href = this.href;
6024 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6027 // menu .. should add dropdown-menu class - so no need for carat..
6029 if (this.badge !== '') {
6031 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6036 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6040 href : this.href || "#",
6041 html: this.html || ''
6044 if (this.tagtype == 'a') {
6045 cfg.cn[0].cls = 'nav-link';
6048 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6051 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6053 if(this.glyphicon) {
6054 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6059 cfg.cn[0].html += " <span class='caret'></span>";
6063 if (this.badge !== '') {
6065 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6073 onRender : function(ct, position)
6075 // Roo.log("Call onRender: " + this.xtype);
6076 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6080 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6081 this.navLink = this.el.select('.nav-link',true).first();
6086 initEvents: function()
6088 if (typeof (this.menu) != 'undefined') {
6089 this.menu.parentType = this.xtype;
6090 this.menu.triggerEl = this.el;
6091 this.menu = this.addxtype(Roo.apply({}, this.menu));
6094 this.el.select('a',true).on('click', this.onClick, this);
6096 if(this.tagtype == 'span'){
6097 this.el.select('span',true).on('click', this.onClick, this);
6100 // at this point parent should be available..
6101 this.parent().register(this);
6104 onClick : function(e)
6106 if (e.getTarget('.dropdown-menu-item')) {
6107 // did you click on a menu itemm.... - then don't trigger onclick..
6112 this.preventDefault ||
6115 Roo.log("NavItem - prevent Default?");
6119 if (this.disabled) {
6123 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6124 if (tg && tg.transition) {
6125 Roo.log("waiting for the transitionend");
6131 //Roo.log("fire event clicked");
6132 if(this.fireEvent('click', this, e) === false){
6136 if(this.tagtype == 'span'){
6140 //Roo.log(this.href);
6141 var ael = this.el.select('a',true).first();
6144 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6145 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6146 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6147 return; // ignore... - it's a 'hash' to another page.
6149 Roo.log("NavItem - prevent Default?");
6151 this.scrollToElement(e);
6155 var p = this.parent();
6157 if (['tabs','pills'].indexOf(p.type)!==-1) {
6158 if (typeof(p.setActiveItem) !== 'undefined') {
6159 p.setActiveItem(this);
6163 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6164 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6165 // remove the collapsed menu expand...
6166 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6170 isActive: function () {
6173 setActive : function(state, fire, is_was_active)
6175 if (this.active && !state && this.navId) {
6176 this.was_active = true;
6177 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6179 nv.clearWasActive(this);
6183 this.active = state;
6186 this.el.removeClass('active');
6187 this.navLink ? this.navLink.removeClass('active') : false;
6188 } else if (!this.el.hasClass('active')) {
6190 this.el.addClass('active');
6191 if (Roo.bootstrap.version == 4 && this.navLink ) {
6192 this.navLink.addClass('active');
6197 this.fireEvent('changed', this, state);
6200 // show a panel if it's registered and related..
6202 if (!this.navId || !this.tabId || !state || is_was_active) {
6206 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6210 var pan = tg.getPanelByName(this.tabId);
6214 // if we can not flip to new panel - go back to old nav highlight..
6215 if (false == tg.showPanel(pan)) {
6216 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6218 var onav = nv.getWasActive();
6220 onav.setActive(true, false, true);
6229 // this should not be here...
6230 setDisabled : function(state)
6232 this.disabled = state;
6234 this.el.removeClass('disabled');
6235 } else if (!this.el.hasClass('disabled')) {
6236 this.el.addClass('disabled');
6242 * Fetch the element to display the tooltip on.
6243 * @return {Roo.Element} defaults to this.el
6245 tooltipEl : function()
6247 return this.el.select('' + this.tagtype + '', true).first();
6250 scrollToElement : function(e)
6252 var c = document.body;
6255 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6257 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6258 c = document.documentElement;
6261 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6267 var o = target.calcOffsetsTo(c);
6274 this.fireEvent('scrollto', this, options, e);
6276 Roo.get(c).scrollTo('top', options.value, true);
6289 * <span> icon </span>
6290 * <span> text </span>
6291 * <span>badge </span>
6295 * @class Roo.bootstrap.NavSidebarItem
6296 * @extends Roo.bootstrap.NavItem
6297 * Bootstrap Navbar.NavSidebarItem class
6298 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6299 * {Boolean} open is the menu open
6300 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6301 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6302 * {String} buttonSize (sm|md|lg)the extra classes for the button
6303 * {Boolean} showArrow show arrow next to the text (default true)
6305 * Create a new Navbar Button
6306 * @param {Object} config The config object
6308 Roo.bootstrap.NavSidebarItem = function(config){
6309 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6314 * The raw click event for the entire grid.
6315 * @param {Roo.EventObject} e
6320 * Fires when the active item active state changes
6321 * @param {Roo.bootstrap.NavSidebarItem} this
6322 * @param {boolean} state the new state
6330 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6332 badgeWeight : 'default',
6338 buttonWeight : 'default',
6344 getAutoCreate : function(){
6349 href : this.href || '#',
6355 if(this.buttonView){
6358 href : this.href || '#',
6359 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6372 cfg.cls += ' active';
6375 if (this.disabled) {
6376 cfg.cls += ' disabled';
6379 cfg.cls += ' open x-open';
6382 if (this.glyphicon || this.icon) {
6383 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6384 a.cn.push({ tag : 'i', cls : c }) ;
6387 if(!this.buttonView){
6390 html : this.html || ''
6397 if (this.badge !== '') {
6398 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6404 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6407 a.cls += ' dropdown-toggle treeview' ;
6413 initEvents : function()
6415 if (typeof (this.menu) != 'undefined') {
6416 this.menu.parentType = this.xtype;
6417 this.menu.triggerEl = this.el;
6418 this.menu = this.addxtype(Roo.apply({}, this.menu));
6421 this.el.on('click', this.onClick, this);
6423 if(this.badge !== ''){
6424 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6429 onClick : function(e)
6436 if(this.preventDefault){
6440 this.fireEvent('click', this, e);
6443 disable : function()
6445 this.setDisabled(true);
6450 this.setDisabled(false);
6453 setDisabled : function(state)
6455 if(this.disabled == state){
6459 this.disabled = state;
6462 this.el.addClass('disabled');
6466 this.el.removeClass('disabled');
6471 setActive : function(state)
6473 if(this.active == state){
6477 this.active = state;
6480 this.el.addClass('active');
6484 this.el.removeClass('active');
6489 isActive: function ()
6494 setBadge : function(str)
6500 this.badgeEl.dom.innerHTML = str;
6517 * @class Roo.bootstrap.Row
6518 * @extends Roo.bootstrap.Component
6519 * Bootstrap Row class (contains columns...)
6523 * @param {Object} config The config object
6526 Roo.bootstrap.Row = function(config){
6527 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6530 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6532 getAutoCreate : function(){
6551 * @class Roo.bootstrap.Pagination
6552 * @extends Roo.bootstrap.Component
6553 * Bootstrap Pagination class
6554 * @cfg {String} size xs | sm | md | lg
6555 * @cfg {Boolean} inverse false | true
6558 * Create a new Pagination
6559 * @param {Object} config The config object
6562 Roo.bootstrap.Pagination = function(config){
6563 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6566 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6572 getAutoCreate : function(){
6578 cfg.cls += ' inverse';
6584 cfg.cls += " " + this.cls;
6602 * @class Roo.bootstrap.PaginationItem
6603 * @extends Roo.bootstrap.Component
6604 * Bootstrap PaginationItem class
6605 * @cfg {String} html text
6606 * @cfg {String} href the link
6607 * @cfg {Boolean} preventDefault (true | false) default true
6608 * @cfg {Boolean} active (true | false) default false
6609 * @cfg {Boolean} disabled default false
6613 * Create a new PaginationItem
6614 * @param {Object} config The config object
6618 Roo.bootstrap.PaginationItem = function(config){
6619 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6624 * The raw click event for the entire grid.
6625 * @param {Roo.EventObject} e
6631 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6635 preventDefault: true,
6640 getAutoCreate : function(){
6646 href : this.href ? this.href : '#',
6647 html : this.html ? this.html : ''
6657 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6661 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6667 initEvents: function() {
6669 this.el.on('click', this.onClick, this);
6672 onClick : function(e)
6674 Roo.log('PaginationItem on click ');
6675 if(this.preventDefault){
6683 this.fireEvent('click', this, e);
6699 * @class Roo.bootstrap.Slider
6700 * @extends Roo.bootstrap.Component
6701 * Bootstrap Slider class
6704 * Create a new Slider
6705 * @param {Object} config The config object
6708 Roo.bootstrap.Slider = function(config){
6709 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6712 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6714 getAutoCreate : function(){
6718 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6722 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6734 * Ext JS Library 1.1.1
6735 * Copyright(c) 2006-2007, Ext JS, LLC.
6737 * Originally Released Under LGPL - original licence link has changed is not relivant.
6740 * <script type="text/javascript">
6745 * @class Roo.grid.ColumnModel
6746 * @extends Roo.util.Observable
6747 * This is the default implementation of a ColumnModel used by the Grid. It defines
6748 * the columns in the grid.
6751 var colModel = new Roo.grid.ColumnModel([
6752 {header: "Ticker", width: 60, sortable: true, locked: true},
6753 {header: "Company Name", width: 150, sortable: true},
6754 {header: "Market Cap.", width: 100, sortable: true},
6755 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6756 {header: "Employees", width: 100, sortable: true, resizable: false}
6761 * The config options listed for this class are options which may appear in each
6762 * individual column definition.
6763 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6765 * @param {Object} config An Array of column config objects. See this class's
6766 * config objects for details.
6768 Roo.grid.ColumnModel = function(config){
6770 * The config passed into the constructor
6772 this.config = config;
6775 // if no id, create one
6776 // if the column does not have a dataIndex mapping,
6777 // map it to the order it is in the config
6778 for(var i = 0, len = config.length; i < len; i++){
6780 if(typeof c.dataIndex == "undefined"){
6783 if(typeof c.renderer == "string"){
6784 c.renderer = Roo.util.Format[c.renderer];
6786 if(typeof c.id == "undefined"){
6789 if(c.editor && c.editor.xtype){
6790 c.editor = Roo.factory(c.editor, Roo.grid);
6792 if(c.editor && c.editor.isFormField){
6793 c.editor = new Roo.grid.GridEditor(c.editor);
6795 this.lookup[c.id] = c;
6799 * The width of columns which have no width specified (defaults to 100)
6802 this.defaultWidth = 100;
6805 * Default sortable of columns which have no sortable specified (defaults to false)
6808 this.defaultSortable = false;
6812 * @event widthchange
6813 * Fires when the width of a column changes.
6814 * @param {ColumnModel} this
6815 * @param {Number} columnIndex The column index
6816 * @param {Number} newWidth The new width
6818 "widthchange": true,
6820 * @event headerchange
6821 * Fires when the text of a header changes.
6822 * @param {ColumnModel} this
6823 * @param {Number} columnIndex The column index
6824 * @param {Number} newText The new header text
6826 "headerchange": true,
6828 * @event hiddenchange
6829 * Fires when a column is hidden or "unhidden".
6830 * @param {ColumnModel} this
6831 * @param {Number} columnIndex The column index
6832 * @param {Boolean} hidden true if hidden, false otherwise
6834 "hiddenchange": true,
6836 * @event columnmoved
6837 * Fires when a column is moved.
6838 * @param {ColumnModel} this
6839 * @param {Number} oldIndex
6840 * @param {Number} newIndex
6842 "columnmoved" : true,
6844 * @event columlockchange
6845 * Fires when a column's locked state is changed
6846 * @param {ColumnModel} this
6847 * @param {Number} colIndex
6848 * @param {Boolean} locked true if locked
6850 "columnlockchange" : true
6852 Roo.grid.ColumnModel.superclass.constructor.call(this);
6854 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6856 * @cfg {String} header The header text to display in the Grid view.
6859 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6860 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6861 * specified, the column's index is used as an index into the Record's data Array.
6864 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6865 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6868 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6869 * Defaults to the value of the {@link #defaultSortable} property.
6870 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6873 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6876 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6879 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6882 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6885 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6886 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6887 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6888 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6891 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6894 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6897 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6900 * @cfg {String} cursor (Optional)
6903 * @cfg {String} tooltip (Optional)
6906 * @cfg {Number} xs (Optional)
6909 * @cfg {Number} sm (Optional)
6912 * @cfg {Number} md (Optional)
6915 * @cfg {Number} lg (Optional)
6918 * Returns the id of the column at the specified index.
6919 * @param {Number} index The column index
6920 * @return {String} the id
6922 getColumnId : function(index){
6923 return this.config[index].id;
6927 * Returns the column for a specified id.
6928 * @param {String} id The column id
6929 * @return {Object} the column
6931 getColumnById : function(id){
6932 return this.lookup[id];
6937 * Returns the column for a specified dataIndex.
6938 * @param {String} dataIndex The column dataIndex
6939 * @return {Object|Boolean} the column or false if not found
6941 getColumnByDataIndex: function(dataIndex){
6942 var index = this.findColumnIndex(dataIndex);
6943 return index > -1 ? this.config[index] : false;
6947 * Returns the index for a specified column id.
6948 * @param {String} id The column id
6949 * @return {Number} the index, or -1 if not found
6951 getIndexById : function(id){
6952 for(var i = 0, len = this.config.length; i < len; i++){
6953 if(this.config[i].id == id){
6961 * Returns the index for a specified column dataIndex.
6962 * @param {String} dataIndex The column dataIndex
6963 * @return {Number} the index, or -1 if not found
6966 findColumnIndex : function(dataIndex){
6967 for(var i = 0, len = this.config.length; i < len; i++){
6968 if(this.config[i].dataIndex == dataIndex){
6976 moveColumn : function(oldIndex, newIndex){
6977 var c = this.config[oldIndex];
6978 this.config.splice(oldIndex, 1);
6979 this.config.splice(newIndex, 0, c);
6980 this.dataMap = null;
6981 this.fireEvent("columnmoved", this, oldIndex, newIndex);
6984 isLocked : function(colIndex){
6985 return this.config[colIndex].locked === true;
6988 setLocked : function(colIndex, value, suppressEvent){
6989 if(this.isLocked(colIndex) == value){
6992 this.config[colIndex].locked = value;
6994 this.fireEvent("columnlockchange", this, colIndex, value);
6998 getTotalLockedWidth : function(){
7000 for(var i = 0; i < this.config.length; i++){
7001 if(this.isLocked(i) && !this.isHidden(i)){
7002 this.totalWidth += this.getColumnWidth(i);
7008 getLockedCount : function(){
7009 for(var i = 0, len = this.config.length; i < len; i++){
7010 if(!this.isLocked(i)){
7015 return this.config.length;
7019 * Returns the number of columns.
7022 getColumnCount : function(visibleOnly){
7023 if(visibleOnly === true){
7025 for(var i = 0, len = this.config.length; i < len; i++){
7026 if(!this.isHidden(i)){
7032 return this.config.length;
7036 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7037 * @param {Function} fn
7038 * @param {Object} scope (optional)
7039 * @return {Array} result
7041 getColumnsBy : function(fn, scope){
7043 for(var i = 0, len = this.config.length; i < len; i++){
7044 var c = this.config[i];
7045 if(fn.call(scope||this, c, i) === true){
7053 * Returns true if the specified column is sortable.
7054 * @param {Number} col The column index
7057 isSortable : function(col){
7058 if(typeof this.config[col].sortable == "undefined"){
7059 return this.defaultSortable;
7061 return this.config[col].sortable;
7065 * Returns the rendering (formatting) function defined for the column.
7066 * @param {Number} col The column index.
7067 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7069 getRenderer : function(col){
7070 if(!this.config[col].renderer){
7071 return Roo.grid.ColumnModel.defaultRenderer;
7073 return this.config[col].renderer;
7077 * Sets the rendering (formatting) function for a column.
7078 * @param {Number} col The column index
7079 * @param {Function} fn The function to use to process the cell's raw data
7080 * to return HTML markup for the grid view. The render function is called with
7081 * the following parameters:<ul>
7082 * <li>Data value.</li>
7083 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7084 * <li>css A CSS style string to apply to the table cell.</li>
7085 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7086 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7087 * <li>Row index</li>
7088 * <li>Column index</li>
7089 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7091 setRenderer : function(col, fn){
7092 this.config[col].renderer = fn;
7096 * Returns the width for the specified column.
7097 * @param {Number} col The column index
7100 getColumnWidth : function(col){
7101 return this.config[col].width * 1 || this.defaultWidth;
7105 * Sets the width for a column.
7106 * @param {Number} col The column index
7107 * @param {Number} width The new width
7109 setColumnWidth : function(col, width, suppressEvent){
7110 this.config[col].width = width;
7111 this.totalWidth = null;
7113 this.fireEvent("widthchange", this, col, width);
7118 * Returns the total width of all columns.
7119 * @param {Boolean} includeHidden True to include hidden column widths
7122 getTotalWidth : function(includeHidden){
7123 if(!this.totalWidth){
7124 this.totalWidth = 0;
7125 for(var i = 0, len = this.config.length; i < len; i++){
7126 if(includeHidden || !this.isHidden(i)){
7127 this.totalWidth += this.getColumnWidth(i);
7131 return this.totalWidth;
7135 * Returns the header for the specified column.
7136 * @param {Number} col The column index
7139 getColumnHeader : function(col){
7140 return this.config[col].header;
7144 * Sets the header for a column.
7145 * @param {Number} col The column index
7146 * @param {String} header The new header
7148 setColumnHeader : function(col, header){
7149 this.config[col].header = header;
7150 this.fireEvent("headerchange", this, col, header);
7154 * Returns the tooltip for the specified column.
7155 * @param {Number} col The column index
7158 getColumnTooltip : function(col){
7159 return this.config[col].tooltip;
7162 * Sets the tooltip for a column.
7163 * @param {Number} col The column index
7164 * @param {String} tooltip The new tooltip
7166 setColumnTooltip : function(col, tooltip){
7167 this.config[col].tooltip = tooltip;
7171 * Returns the dataIndex for the specified column.
7172 * @param {Number} col The column index
7175 getDataIndex : function(col){
7176 return this.config[col].dataIndex;
7180 * Sets the dataIndex for a column.
7181 * @param {Number} col The column index
7182 * @param {Number} dataIndex The new dataIndex
7184 setDataIndex : function(col, dataIndex){
7185 this.config[col].dataIndex = dataIndex;
7191 * Returns true if the cell is editable.
7192 * @param {Number} colIndex The column index
7193 * @param {Number} rowIndex The row index - this is nto actually used..?
7196 isCellEditable : function(colIndex, rowIndex){
7197 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7201 * Returns the editor defined for the cell/column.
7202 * return false or null to disable editing.
7203 * @param {Number} colIndex The column index
7204 * @param {Number} rowIndex The row index
7207 getCellEditor : function(colIndex, rowIndex){
7208 return this.config[colIndex].editor;
7212 * Sets if a column is editable.
7213 * @param {Number} col The column index
7214 * @param {Boolean} editable True if the column is editable
7216 setEditable : function(col, editable){
7217 this.config[col].editable = editable;
7222 * Returns true if the column is hidden.
7223 * @param {Number} colIndex The column index
7226 isHidden : function(colIndex){
7227 return this.config[colIndex].hidden;
7232 * Returns true if the column width cannot be changed
7234 isFixed : function(colIndex){
7235 return this.config[colIndex].fixed;
7239 * Returns true if the column can be resized
7242 isResizable : function(colIndex){
7243 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7246 * Sets if a column is hidden.
7247 * @param {Number} colIndex The column index
7248 * @param {Boolean} hidden True if the column is hidden
7250 setHidden : function(colIndex, hidden){
7251 this.config[colIndex].hidden = hidden;
7252 this.totalWidth = null;
7253 this.fireEvent("hiddenchange", this, colIndex, hidden);
7257 * Sets the editor for a column.
7258 * @param {Number} col The column index
7259 * @param {Object} editor The editor object
7261 setEditor : function(col, editor){
7262 this.config[col].editor = editor;
7266 Roo.grid.ColumnModel.defaultRenderer = function(value)
7268 if(typeof value == "object") {
7271 if(typeof value == "string" && value.length < 1){
7275 return String.format("{0}", value);
7278 // Alias for backwards compatibility
7279 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7282 * Ext JS Library 1.1.1
7283 * Copyright(c) 2006-2007, Ext JS, LLC.
7285 * Originally Released Under LGPL - original licence link has changed is not relivant.
7288 * <script type="text/javascript">
7292 * @class Roo.LoadMask
7293 * A simple utility class for generically masking elements while loading data. If the element being masked has
7294 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7295 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7296 * element's UpdateManager load indicator and will be destroyed after the initial load.
7298 * Create a new LoadMask
7299 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7300 * @param {Object} config The config object
7302 Roo.LoadMask = function(el, config){
7303 this.el = Roo.get(el);
7304 Roo.apply(this, config);
7306 this.store.on('beforeload', this.onBeforeLoad, this);
7307 this.store.on('load', this.onLoad, this);
7308 this.store.on('loadexception', this.onLoadException, this);
7309 this.removeMask = false;
7311 var um = this.el.getUpdateManager();
7312 um.showLoadIndicator = false; // disable the default indicator
7313 um.on('beforeupdate', this.onBeforeLoad, this);
7314 um.on('update', this.onLoad, this);
7315 um.on('failure', this.onLoad, this);
7316 this.removeMask = true;
7320 Roo.LoadMask.prototype = {
7322 * @cfg {Boolean} removeMask
7323 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7324 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7328 * The text to display in a centered loading message box (defaults to 'Loading...')
7332 * @cfg {String} msgCls
7333 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7335 msgCls : 'x-mask-loading',
7338 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7344 * Disables the mask to prevent it from being displayed
7346 disable : function(){
7347 this.disabled = true;
7351 * Enables the mask so that it can be displayed
7353 enable : function(){
7354 this.disabled = false;
7357 onLoadException : function()
7361 if (typeof(arguments[3]) != 'undefined') {
7362 Roo.MessageBox.alert("Error loading",arguments[3]);
7366 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7367 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7374 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7379 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7383 onBeforeLoad : function(){
7385 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7390 destroy : function(){
7392 this.store.un('beforeload', this.onBeforeLoad, this);
7393 this.store.un('load', this.onLoad, this);
7394 this.store.un('loadexception', this.onLoadException, this);
7396 var um = this.el.getUpdateManager();
7397 um.un('beforeupdate', this.onBeforeLoad, this);
7398 um.un('update', this.onLoad, this);
7399 um.un('failure', this.onLoad, this);
7410 * @class Roo.bootstrap.Table
7411 * @extends Roo.bootstrap.Component
7412 * Bootstrap Table class
7413 * @cfg {String} cls table class
7414 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7415 * @cfg {String} bgcolor Specifies the background color for a table
7416 * @cfg {Number} border Specifies whether the table cells should have borders or not
7417 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7418 * @cfg {Number} cellspacing Specifies the space between cells
7419 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7420 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7421 * @cfg {String} sortable Specifies that the table should be sortable
7422 * @cfg {String} summary Specifies a summary of the content of a table
7423 * @cfg {Number} width Specifies the width of a table
7424 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7426 * @cfg {boolean} striped Should the rows be alternative striped
7427 * @cfg {boolean} bordered Add borders to the table
7428 * @cfg {boolean} hover Add hover highlighting
7429 * @cfg {boolean} condensed Format condensed
7430 * @cfg {boolean} responsive Format condensed
7431 * @cfg {Boolean} loadMask (true|false) default false
7432 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7433 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7434 * @cfg {Boolean} rowSelection (true|false) default false
7435 * @cfg {Boolean} cellSelection (true|false) default false
7436 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7437 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7438 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7439 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7443 * Create a new Table
7444 * @param {Object} config The config object
7447 Roo.bootstrap.Table = function(config){
7448 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7453 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7454 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7455 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7456 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7458 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7460 this.sm.grid = this;
7461 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7462 this.sm = this.selModel;
7463 this.sm.xmodule = this.xmodule || false;
7466 if (this.cm && typeof(this.cm.config) == 'undefined') {
7467 this.colModel = new Roo.grid.ColumnModel(this.cm);
7468 this.cm = this.colModel;
7469 this.cm.xmodule = this.xmodule || false;
7472 this.store= Roo.factory(this.store, Roo.data);
7473 this.ds = this.store;
7474 this.ds.xmodule = this.xmodule || false;
7477 if (this.footer && this.store) {
7478 this.footer.dataSource = this.ds;
7479 this.footer = Roo.factory(this.footer);
7486 * Fires when a cell is clicked
7487 * @param {Roo.bootstrap.Table} this
7488 * @param {Roo.Element} el
7489 * @param {Number} rowIndex
7490 * @param {Number} columnIndex
7491 * @param {Roo.EventObject} e
7495 * @event celldblclick
7496 * Fires when a cell is double clicked
7497 * @param {Roo.bootstrap.Table} this
7498 * @param {Roo.Element} el
7499 * @param {Number} rowIndex
7500 * @param {Number} columnIndex
7501 * @param {Roo.EventObject} e
7503 "celldblclick" : true,
7506 * Fires when a row is clicked
7507 * @param {Roo.bootstrap.Table} this
7508 * @param {Roo.Element} el
7509 * @param {Number} rowIndex
7510 * @param {Roo.EventObject} e
7514 * @event rowdblclick
7515 * Fires when a row is double clicked
7516 * @param {Roo.bootstrap.Table} this
7517 * @param {Roo.Element} el
7518 * @param {Number} rowIndex
7519 * @param {Roo.EventObject} e
7521 "rowdblclick" : true,
7524 * Fires when a mouseover occur
7525 * @param {Roo.bootstrap.Table} this
7526 * @param {Roo.Element} el
7527 * @param {Number} rowIndex
7528 * @param {Number} columnIndex
7529 * @param {Roo.EventObject} e
7534 * Fires when a mouseout occur
7535 * @param {Roo.bootstrap.Table} this
7536 * @param {Roo.Element} el
7537 * @param {Number} rowIndex
7538 * @param {Number} columnIndex
7539 * @param {Roo.EventObject} e
7544 * Fires when a row is rendered, so you can change add a style to it.
7545 * @param {Roo.bootstrap.Table} this
7546 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7550 * @event rowsrendered
7551 * Fires when all the rows have been rendered
7552 * @param {Roo.bootstrap.Table} this
7554 'rowsrendered' : true,
7556 * @event contextmenu
7557 * The raw contextmenu event for the entire grid.
7558 * @param {Roo.EventObject} e
7560 "contextmenu" : true,
7562 * @event rowcontextmenu
7563 * Fires when a row is right clicked
7564 * @param {Roo.bootstrap.Table} this
7565 * @param {Number} rowIndex
7566 * @param {Roo.EventObject} e
7568 "rowcontextmenu" : true,
7570 * @event cellcontextmenu
7571 * Fires when a cell is right clicked
7572 * @param {Roo.bootstrap.Table} this
7573 * @param {Number} rowIndex
7574 * @param {Number} cellIndex
7575 * @param {Roo.EventObject} e
7577 "cellcontextmenu" : true,
7579 * @event headercontextmenu
7580 * Fires when a header is right clicked
7581 * @param {Roo.bootstrap.Table} this
7582 * @param {Number} columnIndex
7583 * @param {Roo.EventObject} e
7585 "headercontextmenu" : true
7589 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7615 rowSelection : false,
7616 cellSelection : false,
7619 // Roo.Element - the tbody
7621 // Roo.Element - thead element
7624 container: false, // used by gridpanel...
7630 auto_hide_footer : false,
7632 getAutoCreate : function()
7634 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7641 if (this.scrollBody) {
7642 cfg.cls += ' table-body-fixed';
7645 cfg.cls += ' table-striped';
7649 cfg.cls += ' table-hover';
7651 if (this.bordered) {
7652 cfg.cls += ' table-bordered';
7654 if (this.condensed) {
7655 cfg.cls += ' table-condensed';
7657 if (this.responsive) {
7658 cfg.cls += ' table-responsive';
7662 cfg.cls+= ' ' +this.cls;
7665 // this lot should be simplifed...
7678 ].forEach(function(k) {
7686 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7689 if(this.store || this.cm){
7690 if(this.headerShow){
7691 cfg.cn.push(this.renderHeader());
7694 cfg.cn.push(this.renderBody());
7696 if(this.footerShow){
7697 cfg.cn.push(this.renderFooter());
7699 // where does this come from?
7700 //cfg.cls+= ' TableGrid';
7703 return { cn : [ cfg ] };
7706 initEvents : function()
7708 if(!this.store || !this.cm){
7711 if (this.selModel) {
7712 this.selModel.initEvents();
7716 //Roo.log('initEvents with ds!!!!');
7718 this.mainBody = this.el.select('tbody', true).first();
7719 this.mainHead = this.el.select('thead', true).first();
7720 this.mainFoot = this.el.select('tfoot', true).first();
7726 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7727 e.on('click', _this.sort, _this);
7730 this.mainBody.on("click", this.onClick, this);
7731 this.mainBody.on("dblclick", this.onDblClick, this);
7733 // why is this done????? = it breaks dialogs??
7734 //this.parent().el.setStyle('position', 'relative');
7738 this.footer.parentId = this.id;
7739 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7742 this.el.select('tfoot tr td').first().addClass('hide');
7747 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7750 this.store.on('load', this.onLoad, this);
7751 this.store.on('beforeload', this.onBeforeLoad, this);
7752 this.store.on('update', this.onUpdate, this);
7753 this.store.on('add', this.onAdd, this);
7754 this.store.on("clear", this.clear, this);
7756 this.el.on("contextmenu", this.onContextMenu, this);
7758 this.mainBody.on('scroll', this.onBodyScroll, this);
7760 this.cm.on("headerchange", this.onHeaderChange, this);
7762 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7766 onContextMenu : function(e, t)
7768 this.processEvent("contextmenu", e);
7771 processEvent : function(name, e)
7773 if (name != 'touchstart' ) {
7774 this.fireEvent(name, e);
7777 var t = e.getTarget();
7779 var cell = Roo.get(t);
7785 if(cell.findParent('tfoot', false, true)){
7789 if(cell.findParent('thead', false, true)){
7791 if(e.getTarget().nodeName.toLowerCase() != 'th'){
7792 cell = Roo.get(t).findParent('th', false, true);
7794 Roo.log("failed to find th in thead?");
7795 Roo.log(e.getTarget());
7800 var cellIndex = cell.dom.cellIndex;
7802 var ename = name == 'touchstart' ? 'click' : name;
7803 this.fireEvent("header" + ename, this, cellIndex, e);
7808 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7809 cell = Roo.get(t).findParent('td', false, true);
7811 Roo.log("failed to find th in tbody?");
7812 Roo.log(e.getTarget());
7817 var row = cell.findParent('tr', false, true);
7818 var cellIndex = cell.dom.cellIndex;
7819 var rowIndex = row.dom.rowIndex - 1;
7823 this.fireEvent("row" + name, this, rowIndex, e);
7827 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7833 onMouseover : function(e, el)
7835 var cell = Roo.get(el);
7841 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7842 cell = cell.findParent('td', false, true);
7845 var row = cell.findParent('tr', false, true);
7846 var cellIndex = cell.dom.cellIndex;
7847 var rowIndex = row.dom.rowIndex - 1; // start from 0
7849 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7853 onMouseout : function(e, el)
7855 var cell = Roo.get(el);
7861 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7862 cell = cell.findParent('td', false, true);
7865 var row = cell.findParent('tr', false, true);
7866 var cellIndex = cell.dom.cellIndex;
7867 var rowIndex = row.dom.rowIndex - 1; // start from 0
7869 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7873 onClick : function(e, el)
7875 var cell = Roo.get(el);
7877 if(!cell || (!this.cellSelection && !this.rowSelection)){
7881 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7882 cell = cell.findParent('td', false, true);
7885 if(!cell || typeof(cell) == 'undefined'){
7889 var row = cell.findParent('tr', false, true);
7891 if(!row || typeof(row) == 'undefined'){
7895 var cellIndex = cell.dom.cellIndex;
7896 var rowIndex = this.getRowIndex(row);
7898 // why??? - should these not be based on SelectionModel?
7899 if(this.cellSelection){
7900 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7903 if(this.rowSelection){
7904 this.fireEvent('rowclick', this, row, rowIndex, e);
7910 onDblClick : function(e,el)
7912 var cell = Roo.get(el);
7914 if(!cell || (!this.cellSelection && !this.rowSelection)){
7918 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7919 cell = cell.findParent('td', false, true);
7922 if(!cell || typeof(cell) == 'undefined'){
7926 var row = cell.findParent('tr', false, true);
7928 if(!row || typeof(row) == 'undefined'){
7932 var cellIndex = cell.dom.cellIndex;
7933 var rowIndex = this.getRowIndex(row);
7935 if(this.cellSelection){
7936 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7939 if(this.rowSelection){
7940 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7944 sort : function(e,el)
7946 var col = Roo.get(el);
7948 if(!col.hasClass('sortable')){
7952 var sort = col.attr('sort');
7955 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7959 this.store.sortInfo = {field : sort, direction : dir};
7962 Roo.log("calling footer first");
7963 this.footer.onClick('first');
7966 this.store.load({ params : { start : 0 } });
7970 renderHeader : function()
7978 this.totalWidth = 0;
7980 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7982 var config = cm.config[i];
7986 cls : 'x-hcol-' + i,
7988 html: cm.getColumnHeader(i)
7993 if(typeof(config.sortable) != 'undefined' && config.sortable){
7995 c.html = '<i class="glyphicon"></i>' + c.html;
7998 // could use BS4 hidden-..-down
8000 if(typeof(config.lgHeader) != 'undefined'){
8001 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8004 if(typeof(config.mdHeader) != 'undefined'){
8005 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8008 if(typeof(config.smHeader) != 'undefined'){
8009 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8012 if(typeof(config.xsHeader) != 'undefined'){
8013 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8020 if(typeof(config.tooltip) != 'undefined'){
8021 c.tooltip = config.tooltip;
8024 if(typeof(config.colspan) != 'undefined'){
8025 c.colspan = config.colspan;
8028 if(typeof(config.hidden) != 'undefined' && config.hidden){
8029 c.style += ' display:none;';
8032 if(typeof(config.dataIndex) != 'undefined'){
8033 c.sort = config.dataIndex;
8038 if(typeof(config.align) != 'undefined' && config.align.length){
8039 c.style += ' text-align:' + config.align + ';';
8042 if(typeof(config.width) != 'undefined'){
8043 c.style += ' width:' + config.width + 'px;';
8044 this.totalWidth += config.width;
8046 this.totalWidth += 100; // assume minimum of 100 per column?
8049 if(typeof(config.cls) != 'undefined'){
8050 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8053 ['xs','sm','md','lg'].map(function(size){
8055 if(typeof(config[size]) == 'undefined'){
8059 if (!config[size]) { // 0 = hidden
8060 // BS 4 '0' is treated as hide that column and below.
8061 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8065 c.cls += ' col-' + size + '-' + config[size] + (
8066 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8078 renderBody : function()
8088 colspan : this.cm.getColumnCount()
8098 renderFooter : function()
8108 colspan : this.cm.getColumnCount()
8122 // Roo.log('ds onload');
8127 var ds = this.store;
8129 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8130 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8131 if (_this.store.sortInfo) {
8133 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8134 e.select('i', true).addClass(['glyphicon-arrow-up']);
8137 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8138 e.select('i', true).addClass(['glyphicon-arrow-down']);
8143 var tbody = this.mainBody;
8145 if(ds.getCount() > 0){
8146 ds.data.each(function(d,rowIndex){
8147 var row = this.renderRow(cm, ds, rowIndex);
8149 tbody.createChild(row);
8153 if(row.cellObjects.length){
8154 Roo.each(row.cellObjects, function(r){
8155 _this.renderCellObject(r);
8162 var tfoot = this.el.select('tfoot', true).first();
8164 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8166 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8168 var total = this.ds.getTotalCount();
8170 if(this.footer.pageSize < total){
8171 this.mainFoot.show();
8175 Roo.each(this.el.select('tbody td', true).elements, function(e){
8176 e.on('mouseover', _this.onMouseover, _this);
8179 Roo.each(this.el.select('tbody td', true).elements, function(e){
8180 e.on('mouseout', _this.onMouseout, _this);
8182 this.fireEvent('rowsrendered', this);
8188 onUpdate : function(ds,record)
8190 this.refreshRow(record);
8194 onRemove : function(ds, record, index, isUpdate){
8195 if(isUpdate !== true){
8196 this.fireEvent("beforerowremoved", this, index, record);
8198 var bt = this.mainBody.dom;
8200 var rows = this.el.select('tbody > tr', true).elements;
8202 if(typeof(rows[index]) != 'undefined'){
8203 bt.removeChild(rows[index].dom);
8206 // if(bt.rows[index]){
8207 // bt.removeChild(bt.rows[index]);
8210 if(isUpdate !== true){
8211 //this.stripeRows(index);
8212 //this.syncRowHeights(index, index);
8214 this.fireEvent("rowremoved", this, index, record);
8218 onAdd : function(ds, records, rowIndex)
8220 //Roo.log('on Add called');
8221 // - note this does not handle multiple adding very well..
8222 var bt = this.mainBody.dom;
8223 for (var i =0 ; i < records.length;i++) {
8224 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8225 //Roo.log(records[i]);
8226 //Roo.log(this.store.getAt(rowIndex+i));
8227 this.insertRow(this.store, rowIndex + i, false);
8234 refreshRow : function(record){
8235 var ds = this.store, index;
8236 if(typeof record == 'number'){
8238 record = ds.getAt(index);
8240 index = ds.indexOf(record);
8242 this.insertRow(ds, index, true);
8244 this.onRemove(ds, record, index+1, true);
8246 //this.syncRowHeights(index, index);
8248 this.fireEvent("rowupdated", this, index, record);
8251 insertRow : function(dm, rowIndex, isUpdate){
8254 this.fireEvent("beforerowsinserted", this, rowIndex);
8256 //var s = this.getScrollState();
8257 var row = this.renderRow(this.cm, this.store, rowIndex);
8258 // insert before rowIndex..
8259 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8263 if(row.cellObjects.length){
8264 Roo.each(row.cellObjects, function(r){
8265 _this.renderCellObject(r);
8270 this.fireEvent("rowsinserted", this, rowIndex);
8271 //this.syncRowHeights(firstRow, lastRow);
8272 //this.stripeRows(firstRow);
8279 getRowDom : function(rowIndex)
8281 var rows = this.el.select('tbody > tr', true).elements;
8283 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8286 // returns the object tree for a tr..
8289 renderRow : function(cm, ds, rowIndex)
8291 var d = ds.getAt(rowIndex);
8295 cls : 'x-row-' + rowIndex,
8299 var cellObjects = [];
8301 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8302 var config = cm.config[i];
8304 var renderer = cm.getRenderer(i);
8308 if(typeof(renderer) !== 'undefined'){
8309 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8311 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8312 // and are rendered into the cells after the row is rendered - using the id for the element.
8314 if(typeof(value) === 'object'){
8324 rowIndex : rowIndex,
8329 this.fireEvent('rowclass', this, rowcfg);
8333 cls : rowcfg.rowClass + ' x-col-' + i,
8335 html: (typeof(value) === 'object') ? '' : value
8342 if(typeof(config.colspan) != 'undefined'){
8343 td.colspan = config.colspan;
8346 if(typeof(config.hidden) != 'undefined' && config.hidden){
8347 td.style += ' display:none;';
8350 if(typeof(config.align) != 'undefined' && config.align.length){
8351 td.style += ' text-align:' + config.align + ';';
8353 if(typeof(config.valign) != 'undefined' && config.valign.length){
8354 td.style += ' vertical-align:' + config.valign + ';';
8357 if(typeof(config.width) != 'undefined'){
8358 td.style += ' width:' + config.width + 'px;';
8361 if(typeof(config.cursor) != 'undefined'){
8362 td.style += ' cursor:' + config.cursor + ';';
8365 if(typeof(config.cls) != 'undefined'){
8366 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8369 ['xs','sm','md','lg'].map(function(size){
8371 if(typeof(config[size]) == 'undefined'){
8377 if (!config[size]) { // 0 = hidden
8378 // BS 4 '0' is treated as hide that column and below.
8379 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8383 td.cls += ' col-' + size + '-' + config[size] + (
8384 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8394 row.cellObjects = cellObjects;
8402 onBeforeLoad : function()
8411 this.el.select('tbody', true).first().dom.innerHTML = '';
8414 * Show or hide a row.
8415 * @param {Number} rowIndex to show or hide
8416 * @param {Boolean} state hide
8418 setRowVisibility : function(rowIndex, state)
8420 var bt = this.mainBody.dom;
8422 var rows = this.el.select('tbody > tr', true).elements;
8424 if(typeof(rows[rowIndex]) == 'undefined'){
8427 rows[rowIndex].dom.style.display = state ? '' : 'none';
8431 getSelectionModel : function(){
8433 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8435 return this.selModel;
8438 * Render the Roo.bootstrap object from renderder
8440 renderCellObject : function(r)
8444 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8446 var t = r.cfg.render(r.container);
8449 Roo.each(r.cfg.cn, function(c){
8451 container: t.getChildContainer(),
8454 _this.renderCellObject(child);
8459 getRowIndex : function(row)
8463 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8474 * Returns the grid's underlying element = used by panel.Grid
8475 * @return {Element} The element
8477 getGridEl : function(){
8481 * Forces a resize - used by panel.Grid
8482 * @return {Element} The element
8484 autoSize : function()
8486 //var ctr = Roo.get(this.container.dom.parentElement);
8487 var ctr = Roo.get(this.el.dom);
8489 var thd = this.getGridEl().select('thead',true).first();
8490 var tbd = this.getGridEl().select('tbody', true).first();
8491 var tfd = this.getGridEl().select('tfoot', true).first();
8493 var cw = ctr.getWidth();
8497 tbd.setWidth(ctr.getWidth());
8498 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8499 // this needs fixing for various usage - currently only hydra job advers I think..
8501 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8503 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8506 cw = Math.max(cw, this.totalWidth);
8507 this.getGridEl().select('tr',true).setWidth(cw);
8508 // resize 'expandable coloumn?
8510 return; // we doe not have a view in this design..
8513 onBodyScroll: function()
8515 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8517 this.mainHead.setStyle({
8518 'position' : 'relative',
8519 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8525 var scrollHeight = this.mainBody.dom.scrollHeight;
8527 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8529 var height = this.mainBody.getHeight();
8531 if(scrollHeight - height == scrollTop) {
8533 var total = this.ds.getTotalCount();
8535 if(this.footer.cursor + this.footer.pageSize < total){
8537 this.footer.ds.load({
8539 start : this.footer.cursor + this.footer.pageSize,
8540 limit : this.footer.pageSize
8550 onHeaderChange : function()
8552 var header = this.renderHeader();
8553 var table = this.el.select('table', true).first();
8555 this.mainHead.remove();
8556 this.mainHead = table.createChild(header, this.mainBody, false);
8559 onHiddenChange : function(colModel, colIndex, hidden)
8561 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8562 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8564 this.CSS.updateRule(thSelector, "display", "");
8565 this.CSS.updateRule(tdSelector, "display", "");
8568 this.CSS.updateRule(thSelector, "display", "none");
8569 this.CSS.updateRule(tdSelector, "display", "none");
8572 this.onHeaderChange();
8576 setColumnWidth: function(col_index, width)
8578 // width = "md-2 xs-2..."
8579 if(!this.colModel.config[col_index]) {
8583 var w = width.split(" ");
8585 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8587 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8590 for(var j = 0; j < w.length; j++) {
8596 var size_cls = w[j].split("-");
8598 if(!Number.isInteger(size_cls[1] * 1)) {
8602 if(!this.colModel.config[col_index][size_cls[0]]) {
8606 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8610 h_row[0].classList.replace(
8611 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8612 "col-"+size_cls[0]+"-"+size_cls[1]
8615 for(var i = 0; i < rows.length; i++) {
8617 var size_cls = w[j].split("-");
8619 if(!Number.isInteger(size_cls[1] * 1)) {
8623 if(!this.colModel.config[col_index][size_cls[0]]) {
8627 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8631 rows[i].classList.replace(
8632 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8633 "col-"+size_cls[0]+"-"+size_cls[1]
8637 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8652 * @class Roo.bootstrap.TableCell
8653 * @extends Roo.bootstrap.Component
8654 * Bootstrap TableCell class
8655 * @cfg {String} html cell contain text
8656 * @cfg {String} cls cell class
8657 * @cfg {String} tag cell tag (td|th) default td
8658 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8659 * @cfg {String} align Aligns the content in a cell
8660 * @cfg {String} axis Categorizes cells
8661 * @cfg {String} bgcolor Specifies the background color of a cell
8662 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8663 * @cfg {Number} colspan Specifies the number of columns a cell should span
8664 * @cfg {String} headers Specifies one or more header cells a cell is related to
8665 * @cfg {Number} height Sets the height of a cell
8666 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8667 * @cfg {Number} rowspan Sets the number of rows a cell should span
8668 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8669 * @cfg {String} valign Vertical aligns the content in a cell
8670 * @cfg {Number} width Specifies the width of a cell
8673 * Create a new TableCell
8674 * @param {Object} config The config object
8677 Roo.bootstrap.TableCell = function(config){
8678 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8681 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8701 getAutoCreate : function(){
8702 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8722 cfg.align=this.align
8728 cfg.bgcolor=this.bgcolor
8731 cfg.charoff=this.charoff
8734 cfg.colspan=this.colspan
8737 cfg.headers=this.headers
8740 cfg.height=this.height
8743 cfg.nowrap=this.nowrap
8746 cfg.rowspan=this.rowspan
8749 cfg.scope=this.scope
8752 cfg.valign=this.valign
8755 cfg.width=this.width
8774 * @class Roo.bootstrap.TableRow
8775 * @extends Roo.bootstrap.Component
8776 * Bootstrap TableRow class
8777 * @cfg {String} cls row class
8778 * @cfg {String} align Aligns the content in a table row
8779 * @cfg {String} bgcolor Specifies a background color for a table row
8780 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8781 * @cfg {String} valign Vertical aligns the content in a table row
8784 * Create a new TableRow
8785 * @param {Object} config The config object
8788 Roo.bootstrap.TableRow = function(config){
8789 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8792 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
8800 getAutoCreate : function(){
8801 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8811 cfg.align = this.align;
8814 cfg.bgcolor = this.bgcolor;
8817 cfg.charoff = this.charoff;
8820 cfg.valign = this.valign;
8838 * @class Roo.bootstrap.TableBody
8839 * @extends Roo.bootstrap.Component
8840 * Bootstrap TableBody class
8841 * @cfg {String} cls element class
8842 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8843 * @cfg {String} align Aligns the content inside the element
8844 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8845 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8848 * Create a new TableBody
8849 * @param {Object} config The config object
8852 Roo.bootstrap.TableBody = function(config){
8853 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8856 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8864 getAutoCreate : function(){
8865 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8879 cfg.align = this.align;
8882 cfg.charoff = this.charoff;
8885 cfg.valign = this.valign;
8892 // initEvents : function()
8899 // this.store = Roo.factory(this.store, Roo.data);
8900 // this.store.on('load', this.onLoad, this);
8902 // this.store.load();
8906 // onLoad: function ()
8908 // this.fireEvent('load', this);
8918 * Ext JS Library 1.1.1
8919 * Copyright(c) 2006-2007, Ext JS, LLC.
8921 * Originally Released Under LGPL - original licence link has changed is not relivant.
8924 * <script type="text/javascript">
8927 // as we use this in bootstrap.
8928 Roo.namespace('Roo.form');
8930 * @class Roo.form.Action
8931 * Internal Class used to handle form actions
8933 * @param {Roo.form.BasicForm} el The form element or its id
8934 * @param {Object} config Configuration options
8939 // define the action interface
8940 Roo.form.Action = function(form, options){
8942 this.options = options || {};
8945 * Client Validation Failed
8948 Roo.form.Action.CLIENT_INVALID = 'client';
8950 * Server Validation Failed
8953 Roo.form.Action.SERVER_INVALID = 'server';
8955 * Connect to Server Failed
8958 Roo.form.Action.CONNECT_FAILURE = 'connect';
8960 * Reading Data from Server Failed
8963 Roo.form.Action.LOAD_FAILURE = 'load';
8965 Roo.form.Action.prototype = {
8967 failureType : undefined,
8968 response : undefined,
8972 run : function(options){
8977 success : function(response){
8982 handleResponse : function(response){
8986 // default connection failure
8987 failure : function(response){
8989 this.response = response;
8990 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8991 this.form.afterAction(this, false);
8994 processResponse : function(response){
8995 this.response = response;
8996 if(!response.responseText){
8999 this.result = this.handleResponse(response);
9003 // utility functions used internally
9004 getUrl : function(appendParams){
9005 var url = this.options.url || this.form.url || this.form.el.dom.action;
9007 var p = this.getParams();
9009 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9015 getMethod : function(){
9016 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9019 getParams : function(){
9020 var bp = this.form.baseParams;
9021 var p = this.options.params;
9023 if(typeof p == "object"){
9024 p = Roo.urlEncode(Roo.applyIf(p, bp));
9025 }else if(typeof p == 'string' && bp){
9026 p += '&' + Roo.urlEncode(bp);
9029 p = Roo.urlEncode(bp);
9034 createCallback : function(){
9036 success: this.success,
9037 failure: this.failure,
9039 timeout: (this.form.timeout*1000),
9040 upload: this.form.fileUpload ? this.success : undefined
9045 Roo.form.Action.Submit = function(form, options){
9046 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9049 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9052 haveProgress : false,
9053 uploadComplete : false,
9055 // uploadProgress indicator.
9056 uploadProgress : function()
9058 if (!this.form.progressUrl) {
9062 if (!this.haveProgress) {
9063 Roo.MessageBox.progress("Uploading", "Uploading");
9065 if (this.uploadComplete) {
9066 Roo.MessageBox.hide();
9070 this.haveProgress = true;
9072 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9074 var c = new Roo.data.Connection();
9076 url : this.form.progressUrl,
9081 success : function(req){
9082 //console.log(data);
9086 rdata = Roo.decode(req.responseText)
9088 Roo.log("Invalid data from server..");
9092 if (!rdata || !rdata.success) {
9094 Roo.MessageBox.alert(Roo.encode(rdata));
9097 var data = rdata.data;
9099 if (this.uploadComplete) {
9100 Roo.MessageBox.hide();
9105 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9106 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9109 this.uploadProgress.defer(2000,this);
9112 failure: function(data) {
9113 Roo.log('progress url failed ');
9124 // run get Values on the form, so it syncs any secondary forms.
9125 this.form.getValues();
9127 var o = this.options;
9128 var method = this.getMethod();
9129 var isPost = method == 'POST';
9130 if(o.clientValidation === false || this.form.isValid()){
9132 if (this.form.progressUrl) {
9133 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9134 (new Date() * 1) + '' + Math.random());
9139 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9140 form:this.form.el.dom,
9141 url:this.getUrl(!isPost),
9143 params:isPost ? this.getParams() : null,
9144 isUpload: this.form.fileUpload,
9145 formData : this.form.formData
9148 this.uploadProgress();
9150 }else if (o.clientValidation !== false){ // client validation failed
9151 this.failureType = Roo.form.Action.CLIENT_INVALID;
9152 this.form.afterAction(this, false);
9156 success : function(response)
9158 this.uploadComplete= true;
9159 if (this.haveProgress) {
9160 Roo.MessageBox.hide();
9164 var result = this.processResponse(response);
9165 if(result === true || result.success){
9166 this.form.afterAction(this, true);
9170 this.form.markInvalid(result.errors);
9171 this.failureType = Roo.form.Action.SERVER_INVALID;
9173 this.form.afterAction(this, false);
9175 failure : function(response)
9177 this.uploadComplete= true;
9178 if (this.haveProgress) {
9179 Roo.MessageBox.hide();
9182 this.response = response;
9183 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9184 this.form.afterAction(this, false);
9187 handleResponse : function(response){
9188 if(this.form.errorReader){
9189 var rs = this.form.errorReader.read(response);
9192 for(var i = 0, len = rs.records.length; i < len; i++) {
9193 var r = rs.records[i];
9197 if(errors.length < 1){
9201 success : rs.success,
9207 ret = Roo.decode(response.responseText);
9211 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9221 Roo.form.Action.Load = function(form, options){
9222 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9223 this.reader = this.form.reader;
9226 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9231 Roo.Ajax.request(Roo.apply(
9232 this.createCallback(), {
9233 method:this.getMethod(),
9234 url:this.getUrl(false),
9235 params:this.getParams()
9239 success : function(response){
9241 var result = this.processResponse(response);
9242 if(result === true || !result.success || !result.data){
9243 this.failureType = Roo.form.Action.LOAD_FAILURE;
9244 this.form.afterAction(this, false);
9247 this.form.clearInvalid();
9248 this.form.setValues(result.data);
9249 this.form.afterAction(this, true);
9252 handleResponse : function(response){
9253 if(this.form.reader){
9254 var rs = this.form.reader.read(response);
9255 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9257 success : rs.success,
9261 return Roo.decode(response.responseText);
9265 Roo.form.Action.ACTION_TYPES = {
9266 'load' : Roo.form.Action.Load,
9267 'submit' : Roo.form.Action.Submit
9276 * @class Roo.bootstrap.Form
9277 * @extends Roo.bootstrap.Component
9278 * Bootstrap Form class
9279 * @cfg {String} method GET | POST (default POST)
9280 * @cfg {String} labelAlign top | left (default top)
9281 * @cfg {String} align left | right - for navbars
9282 * @cfg {Boolean} loadMask load mask when submit (default true)
9287 * @param {Object} config The config object
9291 Roo.bootstrap.Form = function(config){
9293 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9295 Roo.bootstrap.Form.popover.apply();
9299 * @event clientvalidation
9300 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9301 * @param {Form} this
9302 * @param {Boolean} valid true if the form has passed client-side validation
9304 clientvalidation: true,
9306 * @event beforeaction
9307 * Fires before any action is performed. Return false to cancel the action.
9308 * @param {Form} this
9309 * @param {Action} action The action to be performed
9313 * @event actionfailed
9314 * Fires when an action fails.
9315 * @param {Form} this
9316 * @param {Action} action The action that failed
9318 actionfailed : true,
9320 * @event actioncomplete
9321 * Fires when an action is completed.
9322 * @param {Form} this
9323 * @param {Action} action The action that completed
9325 actioncomplete : true
9329 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9332 * @cfg {String} method
9333 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9338 * The URL to use for form actions if one isn't supplied in the action options.
9341 * @cfg {Boolean} fileUpload
9342 * Set to true if this form is a file upload.
9346 * @cfg {Object} baseParams
9347 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9351 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9355 * @cfg {Sting} align (left|right) for navbar forms
9360 activeAction : null,
9363 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9364 * element by passing it or its id or mask the form itself by passing in true.
9367 waitMsgTarget : false,
9372 * @cfg {Boolean} errorMask (true|false) default false
9377 * @cfg {Number} maskOffset Default 100
9382 * @cfg {Boolean} maskBody
9386 getAutoCreate : function(){
9390 method : this.method || 'POST',
9391 id : this.id || Roo.id(),
9394 if (this.parent().xtype.match(/^Nav/)) {
9395 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9399 if (this.labelAlign == 'left' ) {
9400 cfg.cls += ' form-horizontal';
9406 initEvents : function()
9408 this.el.on('submit', this.onSubmit, this);
9409 // this was added as random key presses on the form where triggering form submit.
9410 this.el.on('keypress', function(e) {
9411 if (e.getCharCode() != 13) {
9414 // we might need to allow it for textareas.. and some other items.
9415 // check e.getTarget().
9417 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9421 Roo.log("keypress blocked");
9429 onSubmit : function(e){
9434 * Returns true if client-side validation on the form is successful.
9437 isValid : function(){
9438 var items = this.getItems();
9442 items.each(function(f){
9448 Roo.log('invalid field: ' + f.name);
9452 if(!target && f.el.isVisible(true)){
9458 if(this.errorMask && !valid){
9459 Roo.bootstrap.Form.popover.mask(this, target);
9466 * Returns true if any fields in this form have changed since their original load.
9469 isDirty : function(){
9471 var items = this.getItems();
9472 items.each(function(f){
9482 * Performs a predefined action (submit or load) or custom actions you define on this form.
9483 * @param {String} actionName The name of the action type
9484 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9485 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9486 * accept other config options):
9488 Property Type Description
9489 ---------------- --------------- ----------------------------------------------------------------------------------
9490 url String The url for the action (defaults to the form's url)
9491 method String The form method to use (defaults to the form's method, or POST if not defined)
9492 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9493 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9494 validate the form on the client (defaults to false)
9496 * @return {BasicForm} this
9498 doAction : function(action, options){
9499 if(typeof action == 'string'){
9500 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9502 if(this.fireEvent('beforeaction', this, action) !== false){
9503 this.beforeAction(action);
9504 action.run.defer(100, action);
9510 beforeAction : function(action){
9511 var o = action.options;
9516 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9518 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9521 // not really supported yet.. ??
9523 //if(this.waitMsgTarget === true){
9524 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9525 //}else if(this.waitMsgTarget){
9526 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9527 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9529 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9535 afterAction : function(action, success){
9536 this.activeAction = null;
9537 var o = action.options;
9542 Roo.get(document.body).unmask();
9548 //if(this.waitMsgTarget === true){
9549 // this.el.unmask();
9550 //}else if(this.waitMsgTarget){
9551 // this.waitMsgTarget.unmask();
9553 // Roo.MessageBox.updateProgress(1);
9554 // Roo.MessageBox.hide();
9561 Roo.callback(o.success, o.scope, [this, action]);
9562 this.fireEvent('actioncomplete', this, action);
9566 // failure condition..
9567 // we have a scenario where updates need confirming.
9568 // eg. if a locking scenario exists..
9569 // we look for { errors : { needs_confirm : true }} in the response.
9571 (typeof(action.result) != 'undefined') &&
9572 (typeof(action.result.errors) != 'undefined') &&
9573 (typeof(action.result.errors.needs_confirm) != 'undefined')
9576 Roo.log("not supported yet");
9579 Roo.MessageBox.confirm(
9580 "Change requires confirmation",
9581 action.result.errorMsg,
9586 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9596 Roo.callback(o.failure, o.scope, [this, action]);
9597 // show an error message if no failed handler is set..
9598 if (!this.hasListener('actionfailed')) {
9599 Roo.log("need to add dialog support");
9601 Roo.MessageBox.alert("Error",
9602 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9603 action.result.errorMsg :
9604 "Saving Failed, please check your entries or try again"
9609 this.fireEvent('actionfailed', this, action);
9614 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9615 * @param {String} id The value to search for
9618 findField : function(id){
9619 var items = this.getItems();
9620 var field = items.get(id);
9622 items.each(function(f){
9623 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9630 return field || null;
9633 * Mark fields in this form invalid in bulk.
9634 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9635 * @return {BasicForm} this
9637 markInvalid : function(errors){
9638 if(errors instanceof Array){
9639 for(var i = 0, len = errors.length; i < len; i++){
9640 var fieldError = errors[i];
9641 var f = this.findField(fieldError.id);
9643 f.markInvalid(fieldError.msg);
9649 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9650 field.markInvalid(errors[id]);
9654 //Roo.each(this.childForms || [], function (f) {
9655 // f.markInvalid(errors);
9662 * Set values for fields in this form in bulk.
9663 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9664 * @return {BasicForm} this
9666 setValues : function(values){
9667 if(values instanceof Array){ // array of objects
9668 for(var i = 0, len = values.length; i < len; i++){
9670 var f = this.findField(v.id);
9672 f.setValue(v.value);
9673 if(this.trackResetOnLoad){
9674 f.originalValue = f.getValue();
9678 }else{ // object hash
9681 if(typeof values[id] != 'function' && (field = this.findField(id))){
9683 if (field.setFromData &&
9685 field.displayField &&
9686 // combos' with local stores can
9687 // be queried via setValue()
9688 // to set their value..
9689 (field.store && !field.store.isLocal)
9693 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9694 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9695 field.setFromData(sd);
9697 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9699 field.setFromData(values);
9702 field.setValue(values[id]);
9706 if(this.trackResetOnLoad){
9707 field.originalValue = field.getValue();
9713 //Roo.each(this.childForms || [], function (f) {
9714 // f.setValues(values);
9721 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9722 * they are returned as an array.
9723 * @param {Boolean} asString
9726 getValues : function(asString){
9727 //if (this.childForms) {
9728 // copy values from the child forms
9729 // Roo.each(this.childForms, function (f) {
9730 // this.setValues(f.getValues());
9736 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9737 if(asString === true){
9740 return Roo.urlDecode(fs);
9744 * Returns the fields in this form as an object with key/value pairs.
9745 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9748 getFieldValues : function(with_hidden)
9750 var items = this.getItems();
9752 items.each(function(f){
9758 var v = f.getValue();
9760 if (f.inputType =='radio') {
9761 if (typeof(ret[f.getName()]) == 'undefined') {
9762 ret[f.getName()] = ''; // empty..
9765 if (!f.el.dom.checked) {
9773 if(f.xtype == 'MoneyField'){
9774 ret[f.currencyName] = f.getCurrency();
9777 // not sure if this supported any more..
9778 if ((typeof(v) == 'object') && f.getRawValue) {
9779 v = f.getRawValue() ; // dates..
9781 // combo boxes where name != hiddenName...
9782 if (f.name !== false && f.name != '' && f.name != f.getName()) {
9783 ret[f.name] = f.getRawValue();
9785 ret[f.getName()] = v;
9792 * Clears all invalid messages in this form.
9793 * @return {BasicForm} this
9795 clearInvalid : function(){
9796 var items = this.getItems();
9798 items.each(function(f){
9807 * @return {BasicForm} this
9810 var items = this.getItems();
9811 items.each(function(f){
9815 Roo.each(this.childForms || [], function (f) {
9823 getItems : function()
9825 var r=new Roo.util.MixedCollection(false, function(o){
9826 return o.id || (o.id = Roo.id());
9828 var iter = function(el) {
9835 Roo.each(el.items,function(e) {
9844 hideFields : function(items)
9846 Roo.each(items, function(i){
9848 var f = this.findField(i);
9859 showFields : function(items)
9861 Roo.each(items, function(i){
9863 var f = this.findField(i);
9876 Roo.apply(Roo.bootstrap.Form, {
9903 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9904 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9905 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9906 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9909 this.maskEl.top.enableDisplayMode("block");
9910 this.maskEl.left.enableDisplayMode("block");
9911 this.maskEl.bottom.enableDisplayMode("block");
9912 this.maskEl.right.enableDisplayMode("block");
9914 this.toolTip = new Roo.bootstrap.Tooltip({
9915 cls : 'roo-form-error-popover',
9917 'left' : ['r-l', [-2,0], 'right'],
9918 'right' : ['l-r', [2,0], 'left'],
9919 'bottom' : ['tl-bl', [0,2], 'top'],
9920 'top' : [ 'bl-tl', [0,-2], 'bottom']
9924 this.toolTip.render(Roo.get(document.body));
9926 this.toolTip.el.enableDisplayMode("block");
9928 Roo.get(document.body).on('click', function(){
9932 Roo.get(document.body).on('touchstart', function(){
9936 this.isApplied = true
9939 mask : function(form, target)
9943 this.target = target;
9945 if(!this.form.errorMask || !target.el){
9949 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9951 Roo.log(scrollable);
9953 var ot = this.target.el.calcOffsetsTo(scrollable);
9955 var scrollTo = ot[1] - this.form.maskOffset;
9957 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9959 scrollable.scrollTo('top', scrollTo);
9961 var box = this.target.el.getBox();
9963 var zIndex = Roo.bootstrap.Modal.zIndex++;
9966 this.maskEl.top.setStyle('position', 'absolute');
9967 this.maskEl.top.setStyle('z-index', zIndex);
9968 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9969 this.maskEl.top.setLeft(0);
9970 this.maskEl.top.setTop(0);
9971 this.maskEl.top.show();
9973 this.maskEl.left.setStyle('position', 'absolute');
9974 this.maskEl.left.setStyle('z-index', zIndex);
9975 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9976 this.maskEl.left.setLeft(0);
9977 this.maskEl.left.setTop(box.y - this.padding);
9978 this.maskEl.left.show();
9980 this.maskEl.bottom.setStyle('position', 'absolute');
9981 this.maskEl.bottom.setStyle('z-index', zIndex);
9982 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9983 this.maskEl.bottom.setLeft(0);
9984 this.maskEl.bottom.setTop(box.bottom + this.padding);
9985 this.maskEl.bottom.show();
9987 this.maskEl.right.setStyle('position', 'absolute');
9988 this.maskEl.right.setStyle('z-index', zIndex);
9989 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9990 this.maskEl.right.setLeft(box.right + this.padding);
9991 this.maskEl.right.setTop(box.y - this.padding);
9992 this.maskEl.right.show();
9994 this.toolTip.bindEl = this.target.el;
9996 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9998 var tip = this.target.blankText;
10000 if(this.target.getValue() !== '' ) {
10002 if (this.target.invalidText.length) {
10003 tip = this.target.invalidText;
10004 } else if (this.target.regexText.length){
10005 tip = this.target.regexText;
10009 this.toolTip.show(tip);
10011 this.intervalID = window.setInterval(function() {
10012 Roo.bootstrap.Form.popover.unmask();
10015 window.onwheel = function(){ return false;};
10017 (function(){ this.isMasked = true; }).defer(500, this);
10021 unmask : function()
10023 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10027 this.maskEl.top.setStyle('position', 'absolute');
10028 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10029 this.maskEl.top.hide();
10031 this.maskEl.left.setStyle('position', 'absolute');
10032 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10033 this.maskEl.left.hide();
10035 this.maskEl.bottom.setStyle('position', 'absolute');
10036 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10037 this.maskEl.bottom.hide();
10039 this.maskEl.right.setStyle('position', 'absolute');
10040 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10041 this.maskEl.right.hide();
10043 this.toolTip.hide();
10045 this.toolTip.el.hide();
10047 window.onwheel = function(){ return true;};
10049 if(this.intervalID){
10050 window.clearInterval(this.intervalID);
10051 this.intervalID = false;
10054 this.isMasked = false;
10064 * Ext JS Library 1.1.1
10065 * Copyright(c) 2006-2007, Ext JS, LLC.
10067 * Originally Released Under LGPL - original licence link has changed is not relivant.
10070 * <script type="text/javascript">
10073 * @class Roo.form.VTypes
10074 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10077 Roo.form.VTypes = function(){
10078 // closure these in so they are only created once.
10079 var alpha = /^[a-zA-Z_]+$/;
10080 var alphanum = /^[a-zA-Z0-9_]+$/;
10081 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10082 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10084 // All these messages and functions are configurable
10087 * The function used to validate email addresses
10088 * @param {String} value The email address
10090 'email' : function(v){
10091 return email.test(v);
10094 * The error text to display when the email validation function returns false
10097 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10099 * The keystroke filter mask to be applied on email input
10102 'emailMask' : /[a-z0-9_\.\-@]/i,
10105 * The function used to validate URLs
10106 * @param {String} value The URL
10108 'url' : function(v){
10109 return url.test(v);
10112 * The error text to display when the url validation function returns false
10115 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10118 * The function used to validate alpha values
10119 * @param {String} value The value
10121 'alpha' : function(v){
10122 return alpha.test(v);
10125 * The error text to display when the alpha validation function returns false
10128 'alphaText' : 'This field should only contain letters and _',
10130 * The keystroke filter mask to be applied on alpha input
10133 'alphaMask' : /[a-z_]/i,
10136 * The function used to validate alphanumeric values
10137 * @param {String} value The value
10139 'alphanum' : function(v){
10140 return alphanum.test(v);
10143 * The error text to display when the alphanumeric validation function returns false
10146 'alphanumText' : 'This field should only contain letters, numbers and _',
10148 * The keystroke filter mask to be applied on alphanumeric input
10151 'alphanumMask' : /[a-z0-9_]/i
10161 * @class Roo.bootstrap.Input
10162 * @extends Roo.bootstrap.Component
10163 * Bootstrap Input class
10164 * @cfg {Boolean} disabled is it disabled
10165 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
10166 * @cfg {String} name name of the input
10167 * @cfg {string} fieldLabel - the label associated
10168 * @cfg {string} placeholder - placeholder to put in text.
10169 * @cfg {string} before - input group add on before
10170 * @cfg {string} after - input group add on after
10171 * @cfg {string} size - (lg|sm) or leave empty..
10172 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10173 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10174 * @cfg {Number} md colspan out of 12 for computer-sized screens
10175 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10176 * @cfg {string} value default value of the input
10177 * @cfg {Number} labelWidth set the width of label
10178 * @cfg {Number} labellg set the width of label (1-12)
10179 * @cfg {Number} labelmd set the width of label (1-12)
10180 * @cfg {Number} labelsm set the width of label (1-12)
10181 * @cfg {Number} labelxs set the width of label (1-12)
10182 * @cfg {String} labelAlign (top|left)
10183 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10184 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10185 * @cfg {String} indicatorpos (left|right) default left
10186 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10187 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10188 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10190 * @cfg {String} align (left|center|right) Default left
10191 * @cfg {Boolean} forceFeedback (true|false) Default false
10194 * Create a new Input
10195 * @param {Object} config The config object
10198 Roo.bootstrap.Input = function(config){
10200 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10205 * Fires when this field receives input focus.
10206 * @param {Roo.form.Field} this
10211 * Fires when this field loses input focus.
10212 * @param {Roo.form.Field} this
10216 * @event specialkey
10217 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10218 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10219 * @param {Roo.form.Field} this
10220 * @param {Roo.EventObject} e The event object
10225 * Fires just before the field blurs if the field value has changed.
10226 * @param {Roo.form.Field} this
10227 * @param {Mixed} newValue The new value
10228 * @param {Mixed} oldValue The original value
10233 * Fires after the field has been marked as invalid.
10234 * @param {Roo.form.Field} this
10235 * @param {String} msg The validation message
10240 * Fires after the field has been validated with no errors.
10241 * @param {Roo.form.Field} this
10246 * Fires after the key up
10247 * @param {Roo.form.Field} this
10248 * @param {Roo.EventObject} e The event Object
10254 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10256 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10257 automatic validation (defaults to "keyup").
10259 validationEvent : "keyup",
10261 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10263 validateOnBlur : true,
10265 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10267 validationDelay : 250,
10269 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10271 focusClass : "x-form-focus", // not needed???
10275 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10277 invalidClass : "has-warning",
10280 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10282 validClass : "has-success",
10285 * @cfg {Boolean} hasFeedback (true|false) default true
10287 hasFeedback : true,
10290 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10292 invalidFeedbackClass : "glyphicon-warning-sign",
10295 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10297 validFeedbackClass : "glyphicon-ok",
10300 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10302 selectOnFocus : false,
10305 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10309 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10314 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10316 disableKeyFilter : false,
10319 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10323 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10327 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10329 blankText : "Please complete this mandatory field",
10332 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10336 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10338 maxLength : Number.MAX_VALUE,
10340 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10342 minLengthText : "The minimum length for this field is {0}",
10344 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10346 maxLengthText : "The maximum length for this field is {0}",
10350 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10351 * If available, this function will be called only after the basic validators all return true, and will be passed the
10352 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10356 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10357 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10358 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10362 * @cfg {String} regexText -- Depricated - use Invalid Text
10367 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10373 autocomplete: false,
10377 inputType : 'text',
10380 placeholder: false,
10385 preventMark: false,
10386 isFormField : true,
10389 labelAlign : false,
10392 formatedValue : false,
10393 forceFeedback : false,
10395 indicatorpos : 'left',
10405 parentLabelAlign : function()
10408 while (parent.parent()) {
10409 parent = parent.parent();
10410 if (typeof(parent.labelAlign) !='undefined') {
10411 return parent.labelAlign;
10418 getAutoCreate : function()
10420 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10426 if(this.inputType != 'hidden'){
10427 cfg.cls = 'form-group' //input-group
10433 type : this.inputType,
10434 value : this.value,
10435 cls : 'form-control',
10436 placeholder : this.placeholder || '',
10437 autocomplete : this.autocomplete || 'new-password'
10440 if(this.capture.length){
10441 input.capture = this.capture;
10444 if(this.accept.length){
10445 input.accept = this.accept + "/*";
10449 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10452 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10453 input.maxLength = this.maxLength;
10456 if (this.disabled) {
10457 input.disabled=true;
10460 if (this.readOnly) {
10461 input.readonly=true;
10465 input.name = this.name;
10469 input.cls += ' input-' + this.size;
10473 ['xs','sm','md','lg'].map(function(size){
10474 if (settings[size]) {
10475 cfg.cls += ' col-' + size + '-' + settings[size];
10479 var inputblock = input;
10483 cls: 'glyphicon form-control-feedback'
10486 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10489 cls : 'has-feedback',
10497 if (this.before || this.after) {
10500 cls : 'input-group',
10504 if (this.before && typeof(this.before) == 'string') {
10506 inputblock.cn.push({
10508 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10512 if (this.before && typeof(this.before) == 'object') {
10513 this.before = Roo.factory(this.before);
10515 inputblock.cn.push({
10517 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10518 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10522 inputblock.cn.push(input);
10524 if (this.after && typeof(this.after) == 'string') {
10525 inputblock.cn.push({
10527 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10531 if (this.after && typeof(this.after) == 'object') {
10532 this.after = Roo.factory(this.after);
10534 inputblock.cn.push({
10536 cls : 'roo-input-after input-group-append input-group-text input-group-' +
10537 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10541 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10542 inputblock.cls += ' has-feedback';
10543 inputblock.cn.push(feedback);
10548 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10549 tooltip : 'This field is required'
10551 if (Roo.bootstrap.version == 4) {
10554 style : 'display-none'
10557 if (align ==='left' && this.fieldLabel.length) {
10559 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10566 cls : 'control-label col-form-label',
10567 html : this.fieldLabel
10578 var labelCfg = cfg.cn[1];
10579 var contentCfg = cfg.cn[2];
10581 if(this.indicatorpos == 'right'){
10586 cls : 'control-label col-form-label',
10590 html : this.fieldLabel
10604 labelCfg = cfg.cn[0];
10605 contentCfg = cfg.cn[1];
10609 if(this.labelWidth > 12){
10610 labelCfg.style = "width: " + this.labelWidth + 'px';
10613 if(this.labelWidth < 13 && this.labelmd == 0){
10614 this.labelmd = this.labelWidth;
10617 if(this.labellg > 0){
10618 labelCfg.cls += ' col-lg-' + this.labellg;
10619 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10622 if(this.labelmd > 0){
10623 labelCfg.cls += ' col-md-' + this.labelmd;
10624 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10627 if(this.labelsm > 0){
10628 labelCfg.cls += ' col-sm-' + this.labelsm;
10629 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10632 if(this.labelxs > 0){
10633 labelCfg.cls += ' col-xs-' + this.labelxs;
10634 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10638 } else if ( this.fieldLabel.length) {
10643 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10644 tooltip : 'This field is required'
10648 //cls : 'input-group-addon',
10649 html : this.fieldLabel
10657 if(this.indicatorpos == 'right'){
10662 //cls : 'input-group-addon',
10663 html : this.fieldLabel
10668 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10669 tooltip : 'This field is required'
10689 if (this.parentType === 'Navbar' && this.parent().bar) {
10690 cfg.cls += ' navbar-form';
10693 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10694 // on BS4 we do this only if not form
10695 cfg.cls += ' navbar-form';
10703 * return the real input element.
10705 inputEl: function ()
10707 return this.el.select('input.form-control',true).first();
10710 tooltipEl : function()
10712 return this.inputEl();
10715 indicatorEl : function()
10717 if (Roo.bootstrap.version == 4) {
10718 return false; // not enabled in v4 yet.
10721 var indicator = this.el.select('i.roo-required-indicator',true).first();
10731 setDisabled : function(v)
10733 var i = this.inputEl().dom;
10735 i.removeAttribute('disabled');
10739 i.setAttribute('disabled','true');
10741 initEvents : function()
10744 this.inputEl().on("keydown" , this.fireKey, this);
10745 this.inputEl().on("focus", this.onFocus, this);
10746 this.inputEl().on("blur", this.onBlur, this);
10748 this.inputEl().relayEvent('keyup', this);
10750 this.indicator = this.indicatorEl();
10752 if(this.indicator){
10753 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10756 // reference to original value for reset
10757 this.originalValue = this.getValue();
10758 //Roo.form.TextField.superclass.initEvents.call(this);
10759 if(this.validationEvent == 'keyup'){
10760 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10761 this.inputEl().on('keyup', this.filterValidation, this);
10763 else if(this.validationEvent !== false){
10764 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10767 if(this.selectOnFocus){
10768 this.on("focus", this.preFocus, this);
10771 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10772 this.inputEl().on("keypress", this.filterKeys, this);
10774 this.inputEl().relayEvent('keypress', this);
10777 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
10778 this.el.on("click", this.autoSize, this);
10781 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10782 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10785 if (typeof(this.before) == 'object') {
10786 this.before.render(this.el.select('.roo-input-before',true).first());
10788 if (typeof(this.after) == 'object') {
10789 this.after.render(this.el.select('.roo-input-after',true).first());
10792 this.inputEl().on('change', this.onChange, this);
10795 filterValidation : function(e){
10796 if(!e.isNavKeyPress()){
10797 this.validationTask.delay(this.validationDelay);
10801 * Validates the field value
10802 * @return {Boolean} True if the value is valid, else false
10804 validate : function(){
10805 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10806 if(this.disabled || this.validateValue(this.getRawValue())){
10811 this.markInvalid();
10817 * Validates a value according to the field's validation rules and marks the field as invalid
10818 * if the validation fails
10819 * @param {Mixed} value The value to validate
10820 * @return {Boolean} True if the value is valid, else false
10822 validateValue : function(value)
10824 if(this.getVisibilityEl().hasClass('hidden')){
10828 if(value.length < 1) { // if it's blank
10829 if(this.allowBlank){
10835 if(value.length < this.minLength){
10838 if(value.length > this.maxLength){
10842 var vt = Roo.form.VTypes;
10843 if(!vt[this.vtype](value, this)){
10847 if(typeof this.validator == "function"){
10848 var msg = this.validator(value);
10852 if (typeof(msg) == 'string') {
10853 this.invalidText = msg;
10857 if(this.regex && !this.regex.test(value)){
10865 fireKey : function(e){
10866 //Roo.log('field ' + e.getKey());
10867 if(e.isNavKeyPress()){
10868 this.fireEvent("specialkey", this, e);
10871 focus : function (selectText){
10873 this.inputEl().focus();
10874 if(selectText === true){
10875 this.inputEl().dom.select();
10881 onFocus : function(){
10882 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10883 // this.el.addClass(this.focusClass);
10885 if(!this.hasFocus){
10886 this.hasFocus = true;
10887 this.startValue = this.getValue();
10888 this.fireEvent("focus", this);
10892 beforeBlur : Roo.emptyFn,
10896 onBlur : function(){
10898 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10899 //this.el.removeClass(this.focusClass);
10901 this.hasFocus = false;
10902 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10905 var v = this.getValue();
10906 if(String(v) !== String(this.startValue)){
10907 this.fireEvent('change', this, v, this.startValue);
10909 this.fireEvent("blur", this);
10912 onChange : function(e)
10914 var v = this.getValue();
10915 if(String(v) !== String(this.startValue)){
10916 this.fireEvent('change', this, v, this.startValue);
10922 * Resets the current field value to the originally loaded value and clears any validation messages
10924 reset : function(){
10925 this.setValue(this.originalValue);
10929 * Returns the name of the field
10930 * @return {Mixed} name The name field
10932 getName: function(){
10936 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10937 * @return {Mixed} value The field value
10939 getValue : function(){
10941 var v = this.inputEl().getValue();
10946 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10947 * @return {Mixed} value The field value
10949 getRawValue : function(){
10950 var v = this.inputEl().getValue();
10956 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10957 * @param {Mixed} value The value to set
10959 setRawValue : function(v){
10960 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10963 selectText : function(start, end){
10964 var v = this.getRawValue();
10966 start = start === undefined ? 0 : start;
10967 end = end === undefined ? v.length : end;
10968 var d = this.inputEl().dom;
10969 if(d.setSelectionRange){
10970 d.setSelectionRange(start, end);
10971 }else if(d.createTextRange){
10972 var range = d.createTextRange();
10973 range.moveStart("character", start);
10974 range.moveEnd("character", v.length-end);
10981 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
10982 * @param {Mixed} value The value to set
10984 setValue : function(v){
10987 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10993 processValue : function(value){
10994 if(this.stripCharsRe){
10995 var newValue = value.replace(this.stripCharsRe, '');
10996 if(newValue !== value){
10997 this.setRawValue(newValue);
11004 preFocus : function(){
11006 if(this.selectOnFocus){
11007 this.inputEl().dom.select();
11010 filterKeys : function(e){
11011 var k = e.getKey();
11012 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11015 var c = e.getCharCode(), cc = String.fromCharCode(c);
11016 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11019 if(!this.maskRe.test(cc)){
11024 * Clear any invalid styles/messages for this field
11026 clearInvalid : function(){
11028 if(!this.el || this.preventMark){ // not rendered
11033 this.el.removeClass([this.invalidClass, 'is-invalid']);
11035 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11037 var feedback = this.el.select('.form-control-feedback', true).first();
11040 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11045 if(this.indicator){
11046 this.indicator.removeClass('visible');
11047 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11050 this.fireEvent('valid', this);
11054 * Mark this field as valid
11056 markValid : function()
11058 if(!this.el || this.preventMark){ // not rendered...
11062 this.el.removeClass([this.invalidClass, this.validClass]);
11063 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11065 var feedback = this.el.select('.form-control-feedback', true).first();
11068 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11071 if(this.indicator){
11072 this.indicator.removeClass('visible');
11073 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11081 if(this.allowBlank && !this.getRawValue().length){
11084 if (Roo.bootstrap.version == 3) {
11085 this.el.addClass(this.validClass);
11087 this.inputEl().addClass('is-valid');
11090 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11092 var feedback = this.el.select('.form-control-feedback', true).first();
11095 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11096 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11101 this.fireEvent('valid', this);
11105 * Mark this field as invalid
11106 * @param {String} msg The validation message
11108 markInvalid : function(msg)
11110 if(!this.el || this.preventMark){ // not rendered
11114 this.el.removeClass([this.invalidClass, this.validClass]);
11115 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11117 var feedback = this.el.select('.form-control-feedback', true).first();
11120 this.el.select('.form-control-feedback', true).first().removeClass(
11121 [this.invalidFeedbackClass, this.validFeedbackClass]);
11128 if(this.allowBlank && !this.getRawValue().length){
11132 if(this.indicator){
11133 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11134 this.indicator.addClass('visible');
11136 if (Roo.bootstrap.version == 3) {
11137 this.el.addClass(this.invalidClass);
11139 this.inputEl().addClass('is-invalid');
11144 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11146 var feedback = this.el.select('.form-control-feedback', true).first();
11149 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11151 if(this.getValue().length || this.forceFeedback){
11152 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11159 this.fireEvent('invalid', this, msg);
11162 SafariOnKeyDown : function(event)
11164 // this is a workaround for a password hang bug on chrome/ webkit.
11165 if (this.inputEl().dom.type != 'password') {
11169 var isSelectAll = false;
11171 if(this.inputEl().dom.selectionEnd > 0){
11172 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11174 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11175 event.preventDefault();
11180 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11182 event.preventDefault();
11183 // this is very hacky as keydown always get's upper case.
11185 var cc = String.fromCharCode(event.getCharCode());
11186 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11190 adjustWidth : function(tag, w){
11191 tag = tag.toLowerCase();
11192 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11193 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11194 if(tag == 'input'){
11197 if(tag == 'textarea'){
11200 }else if(Roo.isOpera){
11201 if(tag == 'input'){
11204 if(tag == 'textarea'){
11212 setFieldLabel : function(v)
11214 if(!this.rendered){
11218 if(this.indicatorEl()){
11219 var ar = this.el.select('label > span',true);
11221 if (ar.elements.length) {
11222 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11223 this.fieldLabel = v;
11227 var br = this.el.select('label',true);
11229 if(br.elements.length) {
11230 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11231 this.fieldLabel = v;
11235 Roo.log('Cannot Found any of label > span || label in input');
11239 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11240 this.fieldLabel = v;
11255 * @class Roo.bootstrap.TextArea
11256 * @extends Roo.bootstrap.Input
11257 * Bootstrap TextArea class
11258 * @cfg {Number} cols Specifies the visible width of a text area
11259 * @cfg {Number} rows Specifies the visible number of lines in a text area
11260 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11261 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11262 * @cfg {string} html text
11265 * Create a new TextArea
11266 * @param {Object} config The config object
11269 Roo.bootstrap.TextArea = function(config){
11270 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11274 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11284 getAutoCreate : function(){
11286 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11292 if(this.inputType != 'hidden'){
11293 cfg.cls = 'form-group' //input-group
11301 value : this.value || '',
11302 html: this.html || '',
11303 cls : 'form-control',
11304 placeholder : this.placeholder || ''
11308 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11309 input.maxLength = this.maxLength;
11313 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11317 input.cols = this.cols;
11320 if (this.readOnly) {
11321 input.readonly = true;
11325 input.name = this.name;
11329 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11333 ['xs','sm','md','lg'].map(function(size){
11334 if (settings[size]) {
11335 cfg.cls += ' col-' + size + '-' + settings[size];
11339 var inputblock = input;
11341 if(this.hasFeedback && !this.allowBlank){
11345 cls: 'glyphicon form-control-feedback'
11349 cls : 'has-feedback',
11358 if (this.before || this.after) {
11361 cls : 'input-group',
11365 inputblock.cn.push({
11367 cls : 'input-group-addon',
11372 inputblock.cn.push(input);
11374 if(this.hasFeedback && !this.allowBlank){
11375 inputblock.cls += ' has-feedback';
11376 inputblock.cn.push(feedback);
11380 inputblock.cn.push({
11382 cls : 'input-group-addon',
11389 if (align ==='left' && this.fieldLabel.length) {
11394 cls : 'control-label',
11395 html : this.fieldLabel
11406 if(this.labelWidth > 12){
11407 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11410 if(this.labelWidth < 13 && this.labelmd == 0){
11411 this.labelmd = this.labelWidth;
11414 if(this.labellg > 0){
11415 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11416 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11419 if(this.labelmd > 0){
11420 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11421 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11424 if(this.labelsm > 0){
11425 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11426 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11429 if(this.labelxs > 0){
11430 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11431 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11434 } else if ( this.fieldLabel.length) {
11439 //cls : 'input-group-addon',
11440 html : this.fieldLabel
11458 if (this.disabled) {
11459 input.disabled=true;
11466 * return the real textarea element.
11468 inputEl: function ()
11470 return this.el.select('textarea.form-control',true).first();
11474 * Clear any invalid styles/messages for this field
11476 clearInvalid : function()
11479 if(!this.el || this.preventMark){ // not rendered
11483 var label = this.el.select('label', true).first();
11484 var icon = this.el.select('i.fa-star', true).first();
11489 this.el.removeClass( this.validClass);
11490 this.inputEl().removeClass('is-invalid');
11492 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11494 var feedback = this.el.select('.form-control-feedback', true).first();
11497 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11502 this.fireEvent('valid', this);
11506 * Mark this field as valid
11508 markValid : function()
11510 if(!this.el || this.preventMark){ // not rendered
11514 this.el.removeClass([this.invalidClass, this.validClass]);
11515 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11517 var feedback = this.el.select('.form-control-feedback', true).first();
11520 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11523 if(this.disabled || this.allowBlank){
11527 var label = this.el.select('label', true).first();
11528 var icon = this.el.select('i.fa-star', true).first();
11533 if (Roo.bootstrap.version == 3) {
11534 this.el.addClass(this.validClass);
11536 this.inputEl().addClass('is-valid');
11540 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11542 var feedback = this.el.select('.form-control-feedback', true).first();
11545 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11546 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11551 this.fireEvent('valid', this);
11555 * Mark this field as invalid
11556 * @param {String} msg The validation message
11558 markInvalid : function(msg)
11560 if(!this.el || this.preventMark){ // not rendered
11564 this.el.removeClass([this.invalidClass, this.validClass]);
11565 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11567 var feedback = this.el.select('.form-control-feedback', true).first();
11570 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11573 if(this.disabled || this.allowBlank){
11577 var label = this.el.select('label', true).first();
11578 var icon = this.el.select('i.fa-star', true).first();
11580 if(!this.getValue().length && label && !icon){
11581 this.el.createChild({
11583 cls : 'text-danger fa fa-lg fa-star',
11584 tooltip : 'This field is required',
11585 style : 'margin-right:5px;'
11589 if (Roo.bootstrap.version == 3) {
11590 this.el.addClass(this.invalidClass);
11592 this.inputEl().addClass('is-invalid');
11595 // fixme ... this may be depricated need to test..
11596 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11598 var feedback = this.el.select('.form-control-feedback', true).first();
11601 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11603 if(this.getValue().length || this.forceFeedback){
11604 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11611 this.fireEvent('invalid', this, msg);
11619 * trigger field - base class for combo..
11624 * @class Roo.bootstrap.TriggerField
11625 * @extends Roo.bootstrap.Input
11626 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11627 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11628 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11629 * for which you can provide a custom implementation. For example:
11631 var trigger = new Roo.bootstrap.TriggerField();
11632 trigger.onTriggerClick = myTriggerFn;
11633 trigger.applyTo('my-field');
11636 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11637 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11638 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11639 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11640 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11643 * Create a new TriggerField.
11644 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11645 * to the base TextField)
11647 Roo.bootstrap.TriggerField = function(config){
11648 this.mimicing = false;
11649 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11652 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11654 * @cfg {String} triggerClass A CSS class to apply to the trigger
11657 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11662 * @cfg {Boolean} removable (true|false) special filter default false
11666 /** @cfg {Boolean} grow @hide */
11667 /** @cfg {Number} growMin @hide */
11668 /** @cfg {Number} growMax @hide */
11674 autoSize: Roo.emptyFn,
11678 deferHeight : true,
11681 actionMode : 'wrap',
11686 getAutoCreate : function(){
11688 var align = this.labelAlign || this.parentLabelAlign();
11693 cls: 'form-group' //input-group
11700 type : this.inputType,
11701 cls : 'form-control',
11702 autocomplete: 'new-password',
11703 placeholder : this.placeholder || ''
11707 input.name = this.name;
11710 input.cls += ' input-' + this.size;
11713 if (this.disabled) {
11714 input.disabled=true;
11717 var inputblock = input;
11719 if(this.hasFeedback && !this.allowBlank){
11723 cls: 'glyphicon form-control-feedback'
11726 if(this.removable && !this.editable ){
11728 cls : 'has-feedback',
11734 cls : 'roo-combo-removable-btn close'
11741 cls : 'has-feedback',
11750 if(this.removable && !this.editable ){
11752 cls : 'roo-removable',
11758 cls : 'roo-combo-removable-btn close'
11765 if (this.before || this.after) {
11768 cls : 'input-group',
11772 inputblock.cn.push({
11774 cls : 'input-group-addon input-group-prepend input-group-text',
11779 inputblock.cn.push(input);
11781 if(this.hasFeedback && !this.allowBlank){
11782 inputblock.cls += ' has-feedback';
11783 inputblock.cn.push(feedback);
11787 inputblock.cn.push({
11789 cls : 'input-group-addon input-group-append input-group-text',
11798 var ibwrap = inputblock;
11803 cls: 'roo-select2-choices',
11807 cls: 'roo-select2-search-field',
11819 cls: 'roo-select2-container input-group',
11824 cls: 'form-hidden-field'
11830 if(!this.multiple && this.showToggleBtn){
11836 if (this.caret != false) {
11839 cls: 'fa fa-' + this.caret
11846 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11848 Roo.bootstrap.version == 3 ? caret : '',
11851 cls: 'combobox-clear',
11865 combobox.cls += ' roo-select2-container-multi';
11869 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11870 tooltip : 'This field is required'
11872 if (Roo.bootstrap.version == 4) {
11875 style : 'display:none'
11880 if (align ==='left' && this.fieldLabel.length) {
11882 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11889 cls : 'control-label',
11890 html : this.fieldLabel
11902 var labelCfg = cfg.cn[1];
11903 var contentCfg = cfg.cn[2];
11905 if(this.indicatorpos == 'right'){
11910 cls : 'control-label',
11914 html : this.fieldLabel
11928 labelCfg = cfg.cn[0];
11929 contentCfg = cfg.cn[1];
11932 if(this.labelWidth > 12){
11933 labelCfg.style = "width: " + this.labelWidth + 'px';
11936 if(this.labelWidth < 13 && this.labelmd == 0){
11937 this.labelmd = this.labelWidth;
11940 if(this.labellg > 0){
11941 labelCfg.cls += ' col-lg-' + this.labellg;
11942 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11945 if(this.labelmd > 0){
11946 labelCfg.cls += ' col-md-' + this.labelmd;
11947 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11950 if(this.labelsm > 0){
11951 labelCfg.cls += ' col-sm-' + this.labelsm;
11952 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11955 if(this.labelxs > 0){
11956 labelCfg.cls += ' col-xs-' + this.labelxs;
11957 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11960 } else if ( this.fieldLabel.length) {
11961 // Roo.log(" label");
11966 //cls : 'input-group-addon',
11967 html : this.fieldLabel
11975 if(this.indicatorpos == 'right'){
11983 html : this.fieldLabel
11997 // Roo.log(" no label && no align");
12004 ['xs','sm','md','lg'].map(function(size){
12005 if (settings[size]) {
12006 cfg.cls += ' col-' + size + '-' + settings[size];
12017 onResize : function(w, h){
12018 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12019 // if(typeof w == 'number'){
12020 // var x = w - this.trigger.getWidth();
12021 // this.inputEl().setWidth(this.adjustWidth('input', x));
12022 // this.trigger.setStyle('left', x+'px');
12027 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12030 getResizeEl : function(){
12031 return this.inputEl();
12035 getPositionEl : function(){
12036 return this.inputEl();
12040 alignErrorIcon : function(){
12041 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12045 initEvents : function(){
12049 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12050 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12051 if(!this.multiple && this.showToggleBtn){
12052 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12053 if(this.hideTrigger){
12054 this.trigger.setDisplayed(false);
12056 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12060 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12063 if(this.removable && !this.editable && !this.tickable){
12064 var close = this.closeTriggerEl();
12067 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12068 close.on('click', this.removeBtnClick, this, close);
12072 //this.trigger.addClassOnOver('x-form-trigger-over');
12073 //this.trigger.addClassOnClick('x-form-trigger-click');
12076 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12080 closeTriggerEl : function()
12082 var close = this.el.select('.roo-combo-removable-btn', true).first();
12083 return close ? close : false;
12086 removeBtnClick : function(e, h, el)
12088 e.preventDefault();
12090 if(this.fireEvent("remove", this) !== false){
12092 this.fireEvent("afterremove", this)
12096 createList : function()
12098 this.list = Roo.get(document.body).createChild({
12099 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12100 cls: 'typeahead typeahead-long dropdown-menu',
12101 style: 'display:none'
12104 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12109 initTrigger : function(){
12114 onDestroy : function(){
12116 this.trigger.removeAllListeners();
12117 // this.trigger.remove();
12120 // this.wrap.remove();
12122 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12126 onFocus : function(){
12127 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12129 if(!this.mimicing){
12130 this.wrap.addClass('x-trigger-wrap-focus');
12131 this.mimicing = true;
12132 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12133 if(this.monitorTab){
12134 this.el.on("keydown", this.checkTab, this);
12141 checkTab : function(e){
12142 if(e.getKey() == e.TAB){
12143 this.triggerBlur();
12148 onBlur : function(){
12153 mimicBlur : function(e, t){
12155 if(!this.wrap.contains(t) && this.validateBlur()){
12156 this.triggerBlur();
12162 triggerBlur : function(){
12163 this.mimicing = false;
12164 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12165 if(this.monitorTab){
12166 this.el.un("keydown", this.checkTab, this);
12168 //this.wrap.removeClass('x-trigger-wrap-focus');
12169 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12173 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12174 validateBlur : function(e, t){
12179 onDisable : function(){
12180 this.inputEl().dom.disabled = true;
12181 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12183 // this.wrap.addClass('x-item-disabled');
12188 onEnable : function(){
12189 this.inputEl().dom.disabled = false;
12190 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12192 // this.el.removeClass('x-item-disabled');
12197 onShow : function(){
12198 var ae = this.getActionEl();
12201 ae.dom.style.display = '';
12202 ae.dom.style.visibility = 'visible';
12208 onHide : function(){
12209 var ae = this.getActionEl();
12210 ae.dom.style.display = 'none';
12214 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12215 * by an implementing function.
12217 * @param {EventObject} e
12219 onTriggerClick : Roo.emptyFn
12227 * @class Roo.bootstrap.CardUploader
12228 * @extends Roo.bootstrap.Button
12229 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12230 * @cfg {Number} errorTimeout default 3000
12231 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12232 * @cfg {Array} html The button text.
12236 * Create a new CardUploader
12237 * @param {Object} config The config object
12240 Roo.bootstrap.CardUploader = function(config){
12244 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12247 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12254 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12257 errorTimeout : 3000,
12261 fileCollection : false,
12264 getAutoCreate : function()
12268 cls :'form-group' ,
12273 //cls : 'input-group-addon',
12274 html : this.fieldLabel
12281 value : this.value,
12282 cls : 'd-none form-control'
12287 multiple : 'multiple',
12289 cls : 'd-none roo-card-upload-selector'
12293 cls : 'roo-card-uploader-button-container w-100 mb-2'
12296 cls : 'card-columns roo-card-uploader-container'
12306 getChildContainer : function() /// what children are added to.
12308 return this.containerEl;
12311 getButtonContainer : function() /// what children are added to.
12313 return this.el.select(".roo-card-uploader-button-container").first();
12316 initEvents : function()
12319 Roo.bootstrap.Input.prototype.initEvents.call(this);
12323 xns: Roo.bootstrap,
12326 container_method : 'getButtonContainer' ,
12327 html : this.html, // fix changable?
12330 'click' : function(btn, e) {
12339 this.urlAPI = (window.createObjectURL && window) ||
12340 (window.URL && URL.revokeObjectURL && URL) ||
12341 (window.webkitURL && webkitURL);
12346 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12348 this.selectorEl.on('change', this.onFileSelected, this);
12351 this.images.forEach(function(img) {
12354 this.images = false;
12356 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12362 onClick : function(e)
12364 e.preventDefault();
12366 this.selectorEl.dom.click();
12370 onFileSelected : function(e)
12372 e.preventDefault();
12374 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12378 Roo.each(this.selectorEl.dom.files, function(file){
12379 this.addFile(file);
12388 addFile : function(file)
12391 if(typeof(file) === 'string'){
12392 throw "Add file by name?"; // should not happen
12396 if(!file || !this.urlAPI){
12406 var url = _this.urlAPI.createObjectURL( file);
12409 id : Roo.bootstrap.CardUploader.ID--,
12410 is_uploaded : false,
12413 mimetype : file.type,
12420 addCard : function (data)
12422 // hidden input element?
12423 // if the file is not an image...
12424 //then we need to use something other that and header_image
12429 xns : Roo.bootstrap,
12430 xtype : 'CardFooter',
12433 xns : Roo.bootstrap,
12439 xns : Roo.bootstrap,
12441 html : String.format("<small>{0}</small>", data.title),
12442 cls : 'col-11 text-left',
12447 click : function() {
12448 this.downloadCard(data.id)
12454 xns : Roo.bootstrap,
12462 click : function() {
12463 t.removeCard(data.id)
12475 var cn = this.addxtype(
12478 xns : Roo.bootstrap,
12481 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12482 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12483 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12488 initEvents : function() {
12489 Roo.bootstrap.Card.prototype.initEvents.call(this);
12490 this.imgEl = this.el.select('.card-img-top').first();
12492 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12493 this.imgEl.set({ 'pointer' : 'cursor' });
12502 // dont' really need ot update items.
12503 // this.items.push(cn);
12504 this.fileCollection.add(cn);
12505 this.updateInput();
12508 removeCard : function(id)
12511 var card = this.fileCollection.get(id);
12512 card.data.is_deleted = 1;
12513 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12514 this.fileCollection.remove(card);
12515 //this.items = this.items.filter(function(e) { return e != card });
12516 // dont' really need ot update items.
12517 card.el.dom.parentNode.removeChild(card.el.dom);
12522 this.fileCollection.each(function(card) {
12523 card.el.dom.parentNode.removeChild(card.el.dom);
12525 this.fileCollection.clear();
12526 this.updateInput();
12529 updateInput : function()
12532 this.fileCollection.each(function(e) {
12536 this.inputEl().dom.value = JSON.stringify(data);
12543 Roo.bootstrap.CardUploader.ID = -1;/*
12545 * Ext JS Library 1.1.1
12546 * Copyright(c) 2006-2007, Ext JS, LLC.
12548 * Originally Released Under LGPL - original licence link has changed is not relivant.
12551 * <script type="text/javascript">
12556 * @class Roo.data.SortTypes
12558 * Defines the default sorting (casting?) comparison functions used when sorting data.
12560 Roo.data.SortTypes = {
12562 * Default sort that does nothing
12563 * @param {Mixed} s The value being converted
12564 * @return {Mixed} The comparison value
12566 none : function(s){
12571 * The regular expression used to strip tags
12575 stripTagsRE : /<\/?[^>]+>/gi,
12578 * Strips all HTML tags to sort on text only
12579 * @param {Mixed} s The value being converted
12580 * @return {String} The comparison value
12582 asText : function(s){
12583 return String(s).replace(this.stripTagsRE, "");
12587 * Strips all HTML tags to sort on text only - Case insensitive
12588 * @param {Mixed} s The value being converted
12589 * @return {String} The comparison value
12591 asUCText : function(s){
12592 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12596 * Case insensitive string
12597 * @param {Mixed} s The value being converted
12598 * @return {String} The comparison value
12600 asUCString : function(s) {
12601 return String(s).toUpperCase();
12606 * @param {Mixed} s The value being converted
12607 * @return {Number} The comparison value
12609 asDate : function(s) {
12613 if(s instanceof Date){
12614 return s.getTime();
12616 return Date.parse(String(s));
12621 * @param {Mixed} s The value being converted
12622 * @return {Float} The comparison value
12624 asFloat : function(s) {
12625 var val = parseFloat(String(s).replace(/,/g, ""));
12634 * @param {Mixed} s The value being converted
12635 * @return {Number} The comparison value
12637 asInt : function(s) {
12638 var val = parseInt(String(s).replace(/,/g, ""));
12646 * Ext JS Library 1.1.1
12647 * Copyright(c) 2006-2007, Ext JS, LLC.
12649 * Originally Released Under LGPL - original licence link has changed is not relivant.
12652 * <script type="text/javascript">
12656 * @class Roo.data.Record
12657 * Instances of this class encapsulate both record <em>definition</em> information, and record
12658 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12659 * to access Records cached in an {@link Roo.data.Store} object.<br>
12661 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12662 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12665 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12667 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12668 * {@link #create}. The parameters are the same.
12669 * @param {Array} data An associative Array of data values keyed by the field name.
12670 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12671 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12672 * not specified an integer id is generated.
12674 Roo.data.Record = function(data, id){
12675 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12680 * Generate a constructor for a specific record layout.
12681 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12682 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12683 * Each field definition object may contain the following properties: <ul>
12684 * <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,
12685 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12686 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12687 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12688 * is being used, then this is a string containing the javascript expression to reference the data relative to
12689 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12690 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12691 * this may be omitted.</p></li>
12692 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12693 * <ul><li>auto (Default, implies no conversion)</li>
12698 * <li>date</li></ul></p></li>
12699 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12700 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12701 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12702 * by the Reader into an object that will be stored in the Record. It is passed the
12703 * following parameters:<ul>
12704 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12706 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12708 * <br>usage:<br><pre><code>
12709 var TopicRecord = Roo.data.Record.create(
12710 {name: 'title', mapping: 'topic_title'},
12711 {name: 'author', mapping: 'username'},
12712 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12713 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12714 {name: 'lastPoster', mapping: 'user2'},
12715 {name: 'excerpt', mapping: 'post_text'}
12718 var myNewRecord = new TopicRecord({
12719 title: 'Do my job please',
12722 lastPost: new Date(),
12723 lastPoster: 'Animal',
12724 excerpt: 'No way dude!'
12726 myStore.add(myNewRecord);
12731 Roo.data.Record.create = function(o){
12732 var f = function(){
12733 f.superclass.constructor.apply(this, arguments);
12735 Roo.extend(f, Roo.data.Record);
12736 var p = f.prototype;
12737 p.fields = new Roo.util.MixedCollection(false, function(field){
12740 for(var i = 0, len = o.length; i < len; i++){
12741 p.fields.add(new Roo.data.Field(o[i]));
12743 f.getField = function(name){
12744 return p.fields.get(name);
12749 Roo.data.Record.AUTO_ID = 1000;
12750 Roo.data.Record.EDIT = 'edit';
12751 Roo.data.Record.REJECT = 'reject';
12752 Roo.data.Record.COMMIT = 'commit';
12754 Roo.data.Record.prototype = {
12756 * Readonly flag - true if this record has been modified.
12765 join : function(store){
12766 this.store = store;
12770 * Set the named field to the specified value.
12771 * @param {String} name The name of the field to set.
12772 * @param {Object} value The value to set the field to.
12774 set : function(name, value){
12775 if(this.data[name] == value){
12779 if(!this.modified){
12780 this.modified = {};
12782 if(typeof this.modified[name] == 'undefined'){
12783 this.modified[name] = this.data[name];
12785 this.data[name] = value;
12786 if(!this.editing && this.store){
12787 this.store.afterEdit(this);
12792 * Get the value of the named field.
12793 * @param {String} name The name of the field to get the value of.
12794 * @return {Object} The value of the field.
12796 get : function(name){
12797 return this.data[name];
12801 beginEdit : function(){
12802 this.editing = true;
12803 this.modified = {};
12807 cancelEdit : function(){
12808 this.editing = false;
12809 delete this.modified;
12813 endEdit : function(){
12814 this.editing = false;
12815 if(this.dirty && this.store){
12816 this.store.afterEdit(this);
12821 * Usually called by the {@link Roo.data.Store} which owns the Record.
12822 * Rejects all changes made to the Record since either creation, or the last commit operation.
12823 * Modified fields are reverted to their original values.
12825 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12826 * of reject operations.
12828 reject : function(){
12829 var m = this.modified;
12831 if(typeof m[n] != "function"){
12832 this.data[n] = m[n];
12835 this.dirty = false;
12836 delete this.modified;
12837 this.editing = false;
12839 this.store.afterReject(this);
12844 * Usually called by the {@link Roo.data.Store} which owns the Record.
12845 * Commits all changes made to the Record since either creation, or the last commit operation.
12847 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12848 * of commit operations.
12850 commit : function(){
12851 this.dirty = false;
12852 delete this.modified;
12853 this.editing = false;
12855 this.store.afterCommit(this);
12860 hasError : function(){
12861 return this.error != null;
12865 clearError : function(){
12870 * Creates a copy of this record.
12871 * @param {String} id (optional) A new record id if you don't want to use this record's id
12874 copy : function(newId) {
12875 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12879 * Ext JS Library 1.1.1
12880 * Copyright(c) 2006-2007, Ext JS, LLC.
12882 * Originally Released Under LGPL - original licence link has changed is not relivant.
12885 * <script type="text/javascript">
12891 * @class Roo.data.Store
12892 * @extends Roo.util.Observable
12893 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12894 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12896 * 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
12897 * has no knowledge of the format of the data returned by the Proxy.<br>
12899 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12900 * instances from the data object. These records are cached and made available through accessor functions.
12902 * Creates a new Store.
12903 * @param {Object} config A config object containing the objects needed for the Store to access data,
12904 * and read the data into Records.
12906 Roo.data.Store = function(config){
12907 this.data = new Roo.util.MixedCollection(false);
12908 this.data.getKey = function(o){
12911 this.baseParams = {};
12913 this.paramNames = {
12918 "multisort" : "_multisort"
12921 if(config && config.data){
12922 this.inlineData = config.data;
12923 delete config.data;
12926 Roo.apply(this, config);
12928 if(this.reader){ // reader passed
12929 this.reader = Roo.factory(this.reader, Roo.data);
12930 this.reader.xmodule = this.xmodule || false;
12931 if(!this.recordType){
12932 this.recordType = this.reader.recordType;
12934 if(this.reader.onMetaChange){
12935 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12939 if(this.recordType){
12940 this.fields = this.recordType.prototype.fields;
12942 this.modified = [];
12946 * @event datachanged
12947 * Fires when the data cache has changed, and a widget which is using this Store
12948 * as a Record cache should refresh its view.
12949 * @param {Store} this
12951 datachanged : true,
12953 * @event metachange
12954 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12955 * @param {Store} this
12956 * @param {Object} meta The JSON metadata
12961 * Fires when Records have been added to the Store
12962 * @param {Store} this
12963 * @param {Roo.data.Record[]} records The array of Records added
12964 * @param {Number} index The index at which the record(s) were added
12969 * Fires when a Record has been removed from the Store
12970 * @param {Store} this
12971 * @param {Roo.data.Record} record The Record that was removed
12972 * @param {Number} index The index at which the record was removed
12977 * Fires when a Record has been updated
12978 * @param {Store} this
12979 * @param {Roo.data.Record} record The Record that was updated
12980 * @param {String} operation The update operation being performed. Value may be one of:
12982 Roo.data.Record.EDIT
12983 Roo.data.Record.REJECT
12984 Roo.data.Record.COMMIT
12990 * Fires when the data cache has been cleared.
12991 * @param {Store} this
12995 * @event beforeload
12996 * Fires before a request is made for a new data object. If the beforeload handler returns false
12997 * the load action will be canceled.
12998 * @param {Store} this
12999 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13003 * @event beforeloadadd
13004 * Fires after a new set of Records has been loaded.
13005 * @param {Store} this
13006 * @param {Roo.data.Record[]} records The Records that were loaded
13007 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13009 beforeloadadd : true,
13012 * Fires after a new set of Records has been loaded, before they are added to the store.
13013 * @param {Store} this
13014 * @param {Roo.data.Record[]} records The Records that were loaded
13015 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13016 * @params {Object} return from reader
13020 * @event loadexception
13021 * Fires if an exception occurs in the Proxy during loading.
13022 * Called with the signature of the Proxy's "loadexception" event.
13023 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13026 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13027 * @param {Object} load options
13028 * @param {Object} jsonData from your request (normally this contains the Exception)
13030 loadexception : true
13034 this.proxy = Roo.factory(this.proxy, Roo.data);
13035 this.proxy.xmodule = this.xmodule || false;
13036 this.relayEvents(this.proxy, ["loadexception"]);
13038 this.sortToggle = {};
13039 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13041 Roo.data.Store.superclass.constructor.call(this);
13043 if(this.inlineData){
13044 this.loadData(this.inlineData);
13045 delete this.inlineData;
13049 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13051 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13052 * without a remote query - used by combo/forms at present.
13056 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13059 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13062 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13063 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13066 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13067 * on any HTTP request
13070 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13073 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13077 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13078 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13080 remoteSort : false,
13083 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13084 * loaded or when a record is removed. (defaults to false).
13086 pruneModifiedRecords : false,
13089 lastOptions : null,
13092 * Add Records to the Store and fires the add event.
13093 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13095 add : function(records){
13096 records = [].concat(records);
13097 for(var i = 0, len = records.length; i < len; i++){
13098 records[i].join(this);
13100 var index = this.data.length;
13101 this.data.addAll(records);
13102 this.fireEvent("add", this, records, index);
13106 * Remove a Record from the Store and fires the remove event.
13107 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13109 remove : function(record){
13110 var index = this.data.indexOf(record);
13111 this.data.removeAt(index);
13113 if(this.pruneModifiedRecords){
13114 this.modified.remove(record);
13116 this.fireEvent("remove", this, record, index);
13120 * Remove all Records from the Store and fires the clear event.
13122 removeAll : function(){
13124 if(this.pruneModifiedRecords){
13125 this.modified = [];
13127 this.fireEvent("clear", this);
13131 * Inserts Records to the Store at the given index and fires the add event.
13132 * @param {Number} index The start index at which to insert the passed Records.
13133 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13135 insert : function(index, records){
13136 records = [].concat(records);
13137 for(var i = 0, len = records.length; i < len; i++){
13138 this.data.insert(index, records[i]);
13139 records[i].join(this);
13141 this.fireEvent("add", this, records, index);
13145 * Get the index within the cache of the passed Record.
13146 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13147 * @return {Number} The index of the passed Record. Returns -1 if not found.
13149 indexOf : function(record){
13150 return this.data.indexOf(record);
13154 * Get the index within the cache of the Record with the passed id.
13155 * @param {String} id The id of the Record to find.
13156 * @return {Number} The index of the Record. Returns -1 if not found.
13158 indexOfId : function(id){
13159 return this.data.indexOfKey(id);
13163 * Get the Record with the specified id.
13164 * @param {String} id The id of the Record to find.
13165 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13167 getById : function(id){
13168 return this.data.key(id);
13172 * Get the Record at the specified index.
13173 * @param {Number} index The index of the Record to find.
13174 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13176 getAt : function(index){
13177 return this.data.itemAt(index);
13181 * Returns a range of Records between specified indices.
13182 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13183 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13184 * @return {Roo.data.Record[]} An array of Records
13186 getRange : function(start, end){
13187 return this.data.getRange(start, end);
13191 storeOptions : function(o){
13192 o = Roo.apply({}, o);
13195 this.lastOptions = o;
13199 * Loads the Record cache from the configured Proxy using the configured Reader.
13201 * If using remote paging, then the first load call must specify the <em>start</em>
13202 * and <em>limit</em> properties in the options.params property to establish the initial
13203 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13205 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13206 * and this call will return before the new data has been loaded. Perform any post-processing
13207 * in a callback function, or in a "load" event handler.</strong>
13209 * @param {Object} options An object containing properties which control loading options:<ul>
13210 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13211 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13212 * passed the following arguments:<ul>
13213 * <li>r : Roo.data.Record[]</li>
13214 * <li>options: Options object from the load call</li>
13215 * <li>success: Boolean success indicator</li></ul></li>
13216 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13217 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13220 load : function(options){
13221 options = options || {};
13222 if(this.fireEvent("beforeload", this, options) !== false){
13223 this.storeOptions(options);
13224 var p = Roo.apply(options.params || {}, this.baseParams);
13225 // if meta was not loaded from remote source.. try requesting it.
13226 if (!this.reader.metaFromRemote) {
13227 p._requestMeta = 1;
13229 if(this.sortInfo && this.remoteSort){
13230 var pn = this.paramNames;
13231 p[pn["sort"]] = this.sortInfo.field;
13232 p[pn["dir"]] = this.sortInfo.direction;
13234 if (this.multiSort) {
13235 var pn = this.paramNames;
13236 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13239 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13244 * Reloads the Record cache from the configured Proxy using the configured Reader and
13245 * the options from the last load operation performed.
13246 * @param {Object} options (optional) An object containing properties which may override the options
13247 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13248 * the most recently used options are reused).
13250 reload : function(options){
13251 this.load(Roo.applyIf(options||{}, this.lastOptions));
13255 // Called as a callback by the Reader during a load operation.
13256 loadRecords : function(o, options, success){
13257 if(!o || success === false){
13258 if(success !== false){
13259 this.fireEvent("load", this, [], options, o);
13261 if(options.callback){
13262 options.callback.call(options.scope || this, [], options, false);
13266 // if data returned failure - throw an exception.
13267 if (o.success === false) {
13268 // show a message if no listener is registered.
13269 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13270 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13272 // loadmask wil be hooked into this..
13273 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13276 var r = o.records, t = o.totalRecords || r.length;
13278 this.fireEvent("beforeloadadd", this, r, options, o);
13280 if(!options || options.add !== true){
13281 if(this.pruneModifiedRecords){
13282 this.modified = [];
13284 for(var i = 0, len = r.length; i < len; i++){
13288 this.data = this.snapshot;
13289 delete this.snapshot;
13292 this.data.addAll(r);
13293 this.totalLength = t;
13295 this.fireEvent("datachanged", this);
13297 this.totalLength = Math.max(t, this.data.length+r.length);
13301 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13303 var e = new Roo.data.Record({});
13305 e.set(this.parent.displayField, this.parent.emptyTitle);
13306 e.set(this.parent.valueField, '');
13311 this.fireEvent("load", this, r, options, o);
13312 if(options.callback){
13313 options.callback.call(options.scope || this, r, options, true);
13319 * Loads data from a passed data block. A Reader which understands the format of the data
13320 * must have been configured in the constructor.
13321 * @param {Object} data The data block from which to read the Records. The format of the data expected
13322 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13323 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13325 loadData : function(o, append){
13326 var r = this.reader.readRecords(o);
13327 this.loadRecords(r, {add: append}, true);
13331 * using 'cn' the nested child reader read the child array into it's child stores.
13332 * @param {Object} rec The record with a 'children array
13334 loadDataFromChildren : function(rec)
13336 this.loadData(this.reader.toLoadData(rec));
13341 * Gets the number of cached records.
13343 * <em>If using paging, this may not be the total size of the dataset. If the data object
13344 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13345 * the data set size</em>
13347 getCount : function(){
13348 return this.data.length || 0;
13352 * Gets the total number of records in the dataset as returned by the server.
13354 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13355 * the dataset size</em>
13357 getTotalCount : function(){
13358 return this.totalLength || 0;
13362 * Returns the sort state of the Store as an object with two properties:
13364 field {String} The name of the field by which the Records are sorted
13365 direction {String} The sort order, "ASC" or "DESC"
13368 getSortState : function(){
13369 return this.sortInfo;
13373 applySort : function(){
13374 if(this.sortInfo && !this.remoteSort){
13375 var s = this.sortInfo, f = s.field;
13376 var st = this.fields.get(f).sortType;
13377 var fn = function(r1, r2){
13378 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13379 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13381 this.data.sort(s.direction, fn);
13382 if(this.snapshot && this.snapshot != this.data){
13383 this.snapshot.sort(s.direction, fn);
13389 * Sets the default sort column and order to be used by the next load operation.
13390 * @param {String} fieldName The name of the field to sort by.
13391 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13393 setDefaultSort : function(field, dir){
13394 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13398 * Sort the Records.
13399 * If remote sorting is used, the sort is performed on the server, and the cache is
13400 * reloaded. If local sorting is used, the cache is sorted internally.
13401 * @param {String} fieldName The name of the field to sort by.
13402 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13404 sort : function(fieldName, dir){
13405 var f = this.fields.get(fieldName);
13407 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13409 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13410 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13415 this.sortToggle[f.name] = dir;
13416 this.sortInfo = {field: f.name, direction: dir};
13417 if(!this.remoteSort){
13419 this.fireEvent("datachanged", this);
13421 this.load(this.lastOptions);
13426 * Calls the specified function for each of the Records in the cache.
13427 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13428 * Returning <em>false</em> aborts and exits the iteration.
13429 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13431 each : function(fn, scope){
13432 this.data.each(fn, scope);
13436 * Gets all records modified since the last commit. Modified records are persisted across load operations
13437 * (e.g., during paging).
13438 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13440 getModifiedRecords : function(){
13441 return this.modified;
13445 createFilterFn : function(property, value, anyMatch){
13446 if(!value.exec){ // not a regex
13447 value = String(value);
13448 if(value.length == 0){
13451 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13453 return function(r){
13454 return value.test(r.data[property]);
13459 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13460 * @param {String} property A field on your records
13461 * @param {Number} start The record index to start at (defaults to 0)
13462 * @param {Number} end The last record index to include (defaults to length - 1)
13463 * @return {Number} The sum
13465 sum : function(property, start, end){
13466 var rs = this.data.items, v = 0;
13467 start = start || 0;
13468 end = (end || end === 0) ? end : rs.length-1;
13470 for(var i = start; i <= end; i++){
13471 v += (rs[i].data[property] || 0);
13477 * Filter the records by a specified property.
13478 * @param {String} field A field on your records
13479 * @param {String/RegExp} value Either a string that the field
13480 * should start with or a RegExp to test against the field
13481 * @param {Boolean} anyMatch True to match any part not just the beginning
13483 filter : function(property, value, anyMatch){
13484 var fn = this.createFilterFn(property, value, anyMatch);
13485 return fn ? this.filterBy(fn) : this.clearFilter();
13489 * Filter by a function. The specified function will be called with each
13490 * record in this data source. If the function returns true the record is included,
13491 * otherwise it is filtered.
13492 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13493 * @param {Object} scope (optional) The scope of the function (defaults to this)
13495 filterBy : function(fn, scope){
13496 this.snapshot = this.snapshot || this.data;
13497 this.data = this.queryBy(fn, scope||this);
13498 this.fireEvent("datachanged", this);
13502 * Query the records by a specified property.
13503 * @param {String} field A field on your records
13504 * @param {String/RegExp} value Either a string that the field
13505 * should start with or a RegExp to test against the field
13506 * @param {Boolean} anyMatch True to match any part not just the beginning
13507 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13509 query : function(property, value, anyMatch){
13510 var fn = this.createFilterFn(property, value, anyMatch);
13511 return fn ? this.queryBy(fn) : this.data.clone();
13515 * Query by a function. The specified function will be called with each
13516 * record in this data source. If the function returns true the record is included
13518 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13519 * @param {Object} scope (optional) The scope of the function (defaults to this)
13520 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13522 queryBy : function(fn, scope){
13523 var data = this.snapshot || this.data;
13524 return data.filterBy(fn, scope||this);
13528 * Collects unique values for a particular dataIndex from this store.
13529 * @param {String} dataIndex The property to collect
13530 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13531 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13532 * @return {Array} An array of the unique values
13534 collect : function(dataIndex, allowNull, bypassFilter){
13535 var d = (bypassFilter === true && this.snapshot) ?
13536 this.snapshot.items : this.data.items;
13537 var v, sv, r = [], l = {};
13538 for(var i = 0, len = d.length; i < len; i++){
13539 v = d[i].data[dataIndex];
13541 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13550 * Revert to a view of the Record cache with no filtering applied.
13551 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13553 clearFilter : function(suppressEvent){
13554 if(this.snapshot && this.snapshot != this.data){
13555 this.data = this.snapshot;
13556 delete this.snapshot;
13557 if(suppressEvent !== true){
13558 this.fireEvent("datachanged", this);
13564 afterEdit : function(record){
13565 if(this.modified.indexOf(record) == -1){
13566 this.modified.push(record);
13568 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13572 afterReject : function(record){
13573 this.modified.remove(record);
13574 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13578 afterCommit : function(record){
13579 this.modified.remove(record);
13580 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13584 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13585 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13587 commitChanges : function(){
13588 var m = this.modified.slice(0);
13589 this.modified = [];
13590 for(var i = 0, len = m.length; i < len; i++){
13596 * Cancel outstanding changes on all changed records.
13598 rejectChanges : function(){
13599 var m = this.modified.slice(0);
13600 this.modified = [];
13601 for(var i = 0, len = m.length; i < len; i++){
13606 onMetaChange : function(meta, rtype, o){
13607 this.recordType = rtype;
13608 this.fields = rtype.prototype.fields;
13609 delete this.snapshot;
13610 this.sortInfo = meta.sortInfo || this.sortInfo;
13611 this.modified = [];
13612 this.fireEvent('metachange', this, this.reader.meta);
13615 moveIndex : function(data, type)
13617 var index = this.indexOf(data);
13619 var newIndex = index + type;
13623 this.insert(newIndex, data);
13628 * Ext JS Library 1.1.1
13629 * Copyright(c) 2006-2007, Ext JS, LLC.
13631 * Originally Released Under LGPL - original licence link has changed is not relivant.
13634 * <script type="text/javascript">
13638 * @class Roo.data.SimpleStore
13639 * @extends Roo.data.Store
13640 * Small helper class to make creating Stores from Array data easier.
13641 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13642 * @cfg {Array} fields An array of field definition objects, or field name strings.
13643 * @cfg {Object} an existing reader (eg. copied from another store)
13644 * @cfg {Array} data The multi-dimensional array of data
13646 * @param {Object} config
13648 Roo.data.SimpleStore = function(config)
13650 Roo.data.SimpleStore.superclass.constructor.call(this, {
13652 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13655 Roo.data.Record.create(config.fields)
13657 proxy : new Roo.data.MemoryProxy(config.data)
13661 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13663 * Ext JS Library 1.1.1
13664 * Copyright(c) 2006-2007, Ext JS, LLC.
13666 * Originally Released Under LGPL - original licence link has changed is not relivant.
13669 * <script type="text/javascript">
13674 * @extends Roo.data.Store
13675 * @class Roo.data.JsonStore
13676 * Small helper class to make creating Stores for JSON data easier. <br/>
13678 var store = new Roo.data.JsonStore({
13679 url: 'get-images.php',
13681 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13684 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13685 * JsonReader and HttpProxy (unless inline data is provided).</b>
13686 * @cfg {Array} fields An array of field definition objects, or field name strings.
13688 * @param {Object} config
13690 Roo.data.JsonStore = function(c){
13691 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13692 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13693 reader: new Roo.data.JsonReader(c, c.fields)
13696 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13698 * Ext JS Library 1.1.1
13699 * Copyright(c) 2006-2007, Ext JS, LLC.
13701 * Originally Released Under LGPL - original licence link has changed is not relivant.
13704 * <script type="text/javascript">
13708 Roo.data.Field = function(config){
13709 if(typeof config == "string"){
13710 config = {name: config};
13712 Roo.apply(this, config);
13715 this.type = "auto";
13718 var st = Roo.data.SortTypes;
13719 // named sortTypes are supported, here we look them up
13720 if(typeof this.sortType == "string"){
13721 this.sortType = st[this.sortType];
13724 // set default sortType for strings and dates
13725 if(!this.sortType){
13728 this.sortType = st.asUCString;
13731 this.sortType = st.asDate;
13734 this.sortType = st.none;
13739 var stripRe = /[\$,%]/g;
13741 // prebuilt conversion function for this field, instead of
13742 // switching every time we're reading a value
13744 var cv, dateFormat = this.dateFormat;
13749 cv = function(v){ return v; };
13752 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13756 return v !== undefined && v !== null && v !== '' ?
13757 parseInt(String(v).replace(stripRe, ""), 10) : '';
13762 return v !== undefined && v !== null && v !== '' ?
13763 parseFloat(String(v).replace(stripRe, ""), 10) : '';
13768 cv = function(v){ return v === true || v === "true" || v == 1; };
13775 if(v instanceof Date){
13779 if(dateFormat == "timestamp"){
13780 return new Date(v*1000);
13782 return Date.parseDate(v, dateFormat);
13784 var parsed = Date.parse(v);
13785 return parsed ? new Date(parsed) : null;
13794 Roo.data.Field.prototype = {
13802 * Ext JS Library 1.1.1
13803 * Copyright(c) 2006-2007, Ext JS, LLC.
13805 * Originally Released Under LGPL - original licence link has changed is not relivant.
13808 * <script type="text/javascript">
13811 // Base class for reading structured data from a data source. This class is intended to be
13812 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13815 * @class Roo.data.DataReader
13816 * Base class for reading structured data from a data source. This class is intended to be
13817 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13820 Roo.data.DataReader = function(meta, recordType){
13824 this.recordType = recordType instanceof Array ?
13825 Roo.data.Record.create(recordType) : recordType;
13828 Roo.data.DataReader.prototype = {
13831 readerType : 'Data',
13833 * Create an empty record
13834 * @param {Object} data (optional) - overlay some values
13835 * @return {Roo.data.Record} record created.
13837 newRow : function(d) {
13839 this.recordType.prototype.fields.each(function(c) {
13841 case 'int' : da[c.name] = 0; break;
13842 case 'date' : da[c.name] = new Date(); break;
13843 case 'float' : da[c.name] = 0.0; break;
13844 case 'boolean' : da[c.name] = false; break;
13845 default : da[c.name] = ""; break;
13849 return new this.recordType(Roo.apply(da, d));
13855 * Ext JS Library 1.1.1
13856 * Copyright(c) 2006-2007, Ext JS, LLC.
13858 * Originally Released Under LGPL - original licence link has changed is not relivant.
13861 * <script type="text/javascript">
13865 * @class Roo.data.DataProxy
13866 * @extends Roo.data.Observable
13867 * This class is an abstract base class for implementations which provide retrieval of
13868 * unformatted data objects.<br>
13870 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13871 * (of the appropriate type which knows how to parse the data object) to provide a block of
13872 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13874 * Custom implementations must implement the load method as described in
13875 * {@link Roo.data.HttpProxy#load}.
13877 Roo.data.DataProxy = function(){
13880 * @event beforeload
13881 * Fires before a network request is made to retrieve a data object.
13882 * @param {Object} This DataProxy object.
13883 * @param {Object} params The params parameter to the load function.
13888 * Fires before the load method's callback is called.
13889 * @param {Object} This DataProxy object.
13890 * @param {Object} o The data object.
13891 * @param {Object} arg The callback argument object passed to the load function.
13895 * @event loadexception
13896 * Fires if an Exception occurs during data retrieval.
13897 * @param {Object} This DataProxy object.
13898 * @param {Object} o The data object.
13899 * @param {Object} arg The callback argument object passed to the load function.
13900 * @param {Object} e The Exception.
13902 loadexception : true
13904 Roo.data.DataProxy.superclass.constructor.call(this);
13907 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13910 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13914 * Ext JS Library 1.1.1
13915 * Copyright(c) 2006-2007, Ext JS, LLC.
13917 * Originally Released Under LGPL - original licence link has changed is not relivant.
13920 * <script type="text/javascript">
13923 * @class Roo.data.MemoryProxy
13924 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13925 * to the Reader when its load method is called.
13927 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13929 Roo.data.MemoryProxy = function(data){
13933 Roo.data.MemoryProxy.superclass.constructor.call(this);
13937 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13940 * Load data from the requested source (in this case an in-memory
13941 * data object passed to the constructor), read the data object into
13942 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13943 * process that block using the passed callback.
13944 * @param {Object} params This parameter is not used by the MemoryProxy class.
13945 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13946 * object into a block of Roo.data.Records.
13947 * @param {Function} callback The function into which to pass the block of Roo.data.records.
13948 * The function must be passed <ul>
13949 * <li>The Record block object</li>
13950 * <li>The "arg" argument from the load function</li>
13951 * <li>A boolean success indicator</li>
13953 * @param {Object} scope The scope in which to call the callback
13954 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13956 load : function(params, reader, callback, scope, arg){
13957 params = params || {};
13960 result = reader.readRecords(params.data ? params.data :this.data);
13962 this.fireEvent("loadexception", this, arg, null, e);
13963 callback.call(scope, null, arg, false);
13966 callback.call(scope, result, arg, true);
13970 update : function(params, records){
13975 * Ext JS Library 1.1.1
13976 * Copyright(c) 2006-2007, Ext JS, LLC.
13978 * Originally Released Under LGPL - original licence link has changed is not relivant.
13981 * <script type="text/javascript">
13984 * @class Roo.data.HttpProxy
13985 * @extends Roo.data.DataProxy
13986 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13987 * configured to reference a certain URL.<br><br>
13989 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13990 * from which the running page was served.<br><br>
13992 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13994 * Be aware that to enable the browser to parse an XML document, the server must set
13995 * the Content-Type header in the HTTP response to "text/xml".
13997 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13998 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
13999 * will be used to make the request.
14001 Roo.data.HttpProxy = function(conn){
14002 Roo.data.HttpProxy.superclass.constructor.call(this);
14003 // is conn a conn config or a real conn?
14005 this.useAjax = !conn || !conn.events;
14009 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14010 // thse are take from connection...
14013 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14016 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14017 * extra parameters to each request made by this object. (defaults to undefined)
14020 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14021 * to each request made by this object. (defaults to undefined)
14024 * @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)
14027 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14030 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14036 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14040 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14041 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14042 * a finer-grained basis than the DataProxy events.
14044 getConnection : function(){
14045 return this.useAjax ? Roo.Ajax : this.conn;
14049 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14050 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14051 * process that block using the passed callback.
14052 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14053 * for the request to the remote server.
14054 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14055 * object into a block of Roo.data.Records.
14056 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14057 * The function must be passed <ul>
14058 * <li>The Record block object</li>
14059 * <li>The "arg" argument from the load function</li>
14060 * <li>A boolean success indicator</li>
14062 * @param {Object} scope The scope in which to call the callback
14063 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14065 load : function(params, reader, callback, scope, arg){
14066 if(this.fireEvent("beforeload", this, params) !== false){
14068 params : params || {},
14070 callback : callback,
14075 callback : this.loadResponse,
14079 Roo.applyIf(o, this.conn);
14080 if(this.activeRequest){
14081 Roo.Ajax.abort(this.activeRequest);
14083 this.activeRequest = Roo.Ajax.request(o);
14085 this.conn.request(o);
14088 callback.call(scope||this, null, arg, false);
14093 loadResponse : function(o, success, response){
14094 delete this.activeRequest;
14096 this.fireEvent("loadexception", this, o, response);
14097 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14102 result = o.reader.read(response);
14104 this.fireEvent("loadexception", this, o, response, e);
14105 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14109 this.fireEvent("load", this, o, o.request.arg);
14110 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14114 update : function(dataSet){
14119 updateResponse : function(dataSet){
14124 * Ext JS Library 1.1.1
14125 * Copyright(c) 2006-2007, Ext JS, LLC.
14127 * Originally Released Under LGPL - original licence link has changed is not relivant.
14130 * <script type="text/javascript">
14134 * @class Roo.data.ScriptTagProxy
14135 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14136 * other than the originating domain of the running page.<br><br>
14138 * <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
14139 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14141 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14142 * source code that is used as the source inside a <script> tag.<br><br>
14144 * In order for the browser to process the returned data, the server must wrap the data object
14145 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14146 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14147 * depending on whether the callback name was passed:
14150 boolean scriptTag = false;
14151 String cb = request.getParameter("callback");
14154 response.setContentType("text/javascript");
14156 response.setContentType("application/x-json");
14158 Writer out = response.getWriter();
14160 out.write(cb + "(");
14162 out.print(dataBlock.toJsonString());
14169 * @param {Object} config A configuration object.
14171 Roo.data.ScriptTagProxy = function(config){
14172 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14173 Roo.apply(this, config);
14174 this.head = document.getElementsByTagName("head")[0];
14177 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14179 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14181 * @cfg {String} url The URL from which to request the data object.
14184 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14188 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14189 * the server the name of the callback function set up by the load call to process the returned data object.
14190 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14191 * javascript output which calls this named function passing the data object as its only parameter.
14193 callbackParam : "callback",
14195 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14196 * name to the request.
14201 * Load data from the configured URL, read the data object into
14202 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14203 * process that block using the passed callback.
14204 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14205 * for the request to the remote server.
14206 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14207 * object into a block of Roo.data.Records.
14208 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14209 * The function must be passed <ul>
14210 * <li>The Record block object</li>
14211 * <li>The "arg" argument from the load function</li>
14212 * <li>A boolean success indicator</li>
14214 * @param {Object} scope The scope in which to call the callback
14215 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14217 load : function(params, reader, callback, scope, arg){
14218 if(this.fireEvent("beforeload", this, params) !== false){
14220 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14222 var url = this.url;
14223 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14225 url += "&_dc=" + (new Date().getTime());
14227 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14230 cb : "stcCallback"+transId,
14231 scriptId : "stcScript"+transId,
14235 callback : callback,
14241 window[trans.cb] = function(o){
14242 conn.handleResponse(o, trans);
14245 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14247 if(this.autoAbort !== false){
14251 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14253 var script = document.createElement("script");
14254 script.setAttribute("src", url);
14255 script.setAttribute("type", "text/javascript");
14256 script.setAttribute("id", trans.scriptId);
14257 this.head.appendChild(script);
14259 this.trans = trans;
14261 callback.call(scope||this, null, arg, false);
14266 isLoading : function(){
14267 return this.trans ? true : false;
14271 * Abort the current server request.
14273 abort : function(){
14274 if(this.isLoading()){
14275 this.destroyTrans(this.trans);
14280 destroyTrans : function(trans, isLoaded){
14281 this.head.removeChild(document.getElementById(trans.scriptId));
14282 clearTimeout(trans.timeoutId);
14284 window[trans.cb] = undefined;
14286 delete window[trans.cb];
14289 // if hasn't been loaded, wait for load to remove it to prevent script error
14290 window[trans.cb] = function(){
14291 window[trans.cb] = undefined;
14293 delete window[trans.cb];
14300 handleResponse : function(o, trans){
14301 this.trans = false;
14302 this.destroyTrans(trans, true);
14305 result = trans.reader.readRecords(o);
14307 this.fireEvent("loadexception", this, o, trans.arg, e);
14308 trans.callback.call(trans.scope||window, null, trans.arg, false);
14311 this.fireEvent("load", this, o, trans.arg);
14312 trans.callback.call(trans.scope||window, result, trans.arg, true);
14316 handleFailure : function(trans){
14317 this.trans = false;
14318 this.destroyTrans(trans, false);
14319 this.fireEvent("loadexception", this, null, trans.arg);
14320 trans.callback.call(trans.scope||window, null, trans.arg, false);
14324 * Ext JS Library 1.1.1
14325 * Copyright(c) 2006-2007, Ext JS, LLC.
14327 * Originally Released Under LGPL - original licence link has changed is not relivant.
14330 * <script type="text/javascript">
14334 * @class Roo.data.JsonReader
14335 * @extends Roo.data.DataReader
14336 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14337 * based on mappings in a provided Roo.data.Record constructor.
14339 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14340 * in the reply previously.
14345 var RecordDef = Roo.data.Record.create([
14346 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14347 {name: 'occupation'} // This field will use "occupation" as the mapping.
14349 var myReader = new Roo.data.JsonReader({
14350 totalProperty: "results", // The property which contains the total dataset size (optional)
14351 root: "rows", // The property which contains an Array of row objects
14352 id: "id" // The property within each row object that provides an ID for the record (optional)
14356 * This would consume a JSON file like this:
14358 { 'results': 2, 'rows': [
14359 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14360 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14363 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14364 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14365 * paged from the remote server.
14366 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14367 * @cfg {String} root name of the property which contains the Array of row objects.
14368 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14369 * @cfg {Array} fields Array of field definition objects
14371 * Create a new JsonReader
14372 * @param {Object} meta Metadata configuration options
14373 * @param {Object} recordType Either an Array of field definition objects,
14374 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14376 Roo.data.JsonReader = function(meta, recordType){
14379 // set some defaults:
14380 Roo.applyIf(meta, {
14381 totalProperty: 'total',
14382 successProperty : 'success',
14387 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14389 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14391 readerType : 'Json',
14394 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14395 * Used by Store query builder to append _requestMeta to params.
14398 metaFromRemote : false,
14400 * This method is only used by a DataProxy which has retrieved data from a remote server.
14401 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14402 * @return {Object} data A data block which is used by an Roo.data.Store object as
14403 * a cache of Roo.data.Records.
14405 read : function(response){
14406 var json = response.responseText;
14408 var o = /* eval:var:o */ eval("("+json+")");
14410 throw {message: "JsonReader.read: Json object not found"};
14416 this.metaFromRemote = true;
14417 this.meta = o.metaData;
14418 this.recordType = Roo.data.Record.create(o.metaData.fields);
14419 this.onMetaChange(this.meta, this.recordType, o);
14421 return this.readRecords(o);
14424 // private function a store will implement
14425 onMetaChange : function(meta, recordType, o){
14432 simpleAccess: function(obj, subsc) {
14439 getJsonAccessor: function(){
14441 return function(expr) {
14443 return(re.test(expr))
14444 ? new Function("obj", "return obj." + expr)
14449 return Roo.emptyFn;
14454 * Create a data block containing Roo.data.Records from an XML document.
14455 * @param {Object} o An object which contains an Array of row objects in the property specified
14456 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14457 * which contains the total size of the dataset.
14458 * @return {Object} data A data block which is used by an Roo.data.Store object as
14459 * a cache of Roo.data.Records.
14461 readRecords : function(o){
14463 * After any data loads, the raw JSON data is available for further custom processing.
14467 var s = this.meta, Record = this.recordType,
14468 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14470 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14472 if(s.totalProperty) {
14473 this.getTotal = this.getJsonAccessor(s.totalProperty);
14475 if(s.successProperty) {
14476 this.getSuccess = this.getJsonAccessor(s.successProperty);
14478 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14480 var g = this.getJsonAccessor(s.id);
14481 this.getId = function(rec) {
14483 return (r === undefined || r === "") ? null : r;
14486 this.getId = function(){return null;};
14489 for(var jj = 0; jj < fl; jj++){
14491 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14492 this.ef[jj] = this.getJsonAccessor(map);
14496 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14497 if(s.totalProperty){
14498 var vt = parseInt(this.getTotal(o), 10);
14503 if(s.successProperty){
14504 var vs = this.getSuccess(o);
14505 if(vs === false || vs === 'false'){
14510 for(var i = 0; i < c; i++){
14513 var id = this.getId(n);
14514 for(var j = 0; j < fl; j++){
14516 var v = this.ef[j](n);
14518 Roo.log('missing convert for ' + f.name);
14522 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14524 var record = new Record(values, id);
14526 records[i] = record;
14532 totalRecords : totalRecords
14535 // used when loading children.. @see loadDataFromChildren
14536 toLoadData: function(rec)
14538 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14539 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14540 return { data : data, total : data.length };
14545 * Ext JS Library 1.1.1
14546 * Copyright(c) 2006-2007, Ext JS, LLC.
14548 * Originally Released Under LGPL - original licence link has changed is not relivant.
14551 * <script type="text/javascript">
14555 * @class Roo.data.ArrayReader
14556 * @extends Roo.data.DataReader
14557 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14558 * Each element of that Array represents a row of data fields. The
14559 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14560 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14564 var RecordDef = Roo.data.Record.create([
14565 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14566 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14568 var myReader = new Roo.data.ArrayReader({
14569 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14573 * This would consume an Array like this:
14575 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14579 * Create a new JsonReader
14580 * @param {Object} meta Metadata configuration options.
14581 * @param {Object|Array} recordType Either an Array of field definition objects
14583 * @cfg {Array} fields Array of field definition objects
14584 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14585 * as specified to {@link Roo.data.Record#create},
14586 * or an {@link Roo.data.Record} object
14589 * created using {@link Roo.data.Record#create}.
14591 Roo.data.ArrayReader = function(meta, recordType)
14593 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14596 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14599 * Create a data block containing Roo.data.Records from an XML document.
14600 * @param {Object} o An Array of row objects which represents the dataset.
14601 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14602 * a cache of Roo.data.Records.
14604 readRecords : function(o)
14606 var sid = this.meta ? this.meta.id : null;
14607 var recordType = this.recordType, fields = recordType.prototype.fields;
14610 for(var i = 0; i < root.length; i++){
14613 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14614 for(var j = 0, jlen = fields.length; j < jlen; j++){
14615 var f = fields.items[j];
14616 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14617 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14619 values[f.name] = v;
14621 var record = new recordType(values, id);
14623 records[records.length] = record;
14627 totalRecords : records.length
14630 // used when loading children.. @see loadDataFromChildren
14631 toLoadData: function(rec)
14633 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14634 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14645 * @class Roo.bootstrap.ComboBox
14646 * @extends Roo.bootstrap.TriggerField
14647 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14648 * @cfg {Boolean} append (true|false) default false
14649 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14650 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14651 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14652 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14653 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14654 * @cfg {Boolean} animate default true
14655 * @cfg {Boolean} emptyResultText only for touch device
14656 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14657 * @cfg {String} emptyTitle default ''
14659 * Create a new ComboBox.
14660 * @param {Object} config Configuration options
14662 Roo.bootstrap.ComboBox = function(config){
14663 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14667 * Fires when the dropdown list is expanded
14668 * @param {Roo.bootstrap.ComboBox} combo This combo box
14673 * Fires when the dropdown list is collapsed
14674 * @param {Roo.bootstrap.ComboBox} combo This combo box
14678 * @event beforeselect
14679 * Fires before a list item is selected. Return false to cancel the selection.
14680 * @param {Roo.bootstrap.ComboBox} combo This combo box
14681 * @param {Roo.data.Record} record The data record returned from the underlying store
14682 * @param {Number} index The index of the selected item in the dropdown list
14684 'beforeselect' : true,
14687 * Fires when a list item is selected
14688 * @param {Roo.bootstrap.ComboBox} combo This combo box
14689 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14690 * @param {Number} index The index of the selected item in the dropdown list
14694 * @event beforequery
14695 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14696 * The event object passed has these properties:
14697 * @param {Roo.bootstrap.ComboBox} combo This combo box
14698 * @param {String} query The query
14699 * @param {Boolean} forceAll true to force "all" query
14700 * @param {Boolean} cancel true to cancel the query
14701 * @param {Object} e The query event object
14703 'beforequery': true,
14706 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14707 * @param {Roo.bootstrap.ComboBox} combo This combo box
14712 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14713 * @param {Roo.bootstrap.ComboBox} combo This combo box
14714 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14719 * Fires when the remove value from the combobox array
14720 * @param {Roo.bootstrap.ComboBox} combo This combo box
14724 * @event afterremove
14725 * Fires when the remove value from the combobox array
14726 * @param {Roo.bootstrap.ComboBox} combo This combo box
14728 'afterremove' : true,
14730 * @event specialfilter
14731 * Fires when specialfilter
14732 * @param {Roo.bootstrap.ComboBox} combo This combo box
14734 'specialfilter' : true,
14737 * Fires when tick the element
14738 * @param {Roo.bootstrap.ComboBox} combo This combo box
14742 * @event touchviewdisplay
14743 * Fires when touch view require special display (default is using displayField)
14744 * @param {Roo.bootstrap.ComboBox} combo This combo box
14745 * @param {Object} cfg set html .
14747 'touchviewdisplay' : true
14752 this.tickItems = [];
14754 this.selectedIndex = -1;
14755 if(this.mode == 'local'){
14756 if(config.queryDelay === undefined){
14757 this.queryDelay = 10;
14759 if(config.minChars === undefined){
14765 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14768 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14769 * rendering into an Roo.Editor, defaults to false)
14772 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14773 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14776 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14779 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14780 * the dropdown list (defaults to undefined, with no header element)
14784 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
14788 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14790 listWidth: undefined,
14792 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14793 * mode = 'remote' or 'text' if mode = 'local')
14795 displayField: undefined,
14798 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14799 * mode = 'remote' or 'value' if mode = 'local').
14800 * Note: use of a valueField requires the user make a selection
14801 * in order for a value to be mapped.
14803 valueField: undefined,
14805 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14810 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14811 * field's data value (defaults to the underlying DOM element's name)
14813 hiddenName: undefined,
14815 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14819 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14821 selectedClass: 'active',
14824 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14828 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14829 * anchor positions (defaults to 'tl-bl')
14831 listAlign: 'tl-bl?',
14833 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14837 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
14838 * query specified by the allQuery config option (defaults to 'query')
14840 triggerAction: 'query',
14842 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14843 * (defaults to 4, does not apply if editable = false)
14847 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14848 * delay (typeAheadDelay) if it matches a known value (defaults to false)
14852 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14853 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14857 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14858 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
14862 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
14863 * when editable = true (defaults to false)
14865 selectOnFocus:false,
14867 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14869 queryParam: 'query',
14871 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
14872 * when mode = 'remote' (defaults to 'Loading...')
14874 loadingText: 'Loading...',
14876 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14880 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14884 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14885 * traditional select (defaults to true)
14889 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14893 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14897 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14898 * listWidth has a higher value)
14902 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14903 * allow the user to set arbitrary text into the field (defaults to false)
14905 forceSelection:false,
14907 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14908 * if typeAhead = true (defaults to 250)
14910 typeAheadDelay : 250,
14912 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14913 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14915 valueNotFoundText : undefined,
14917 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14919 blockFocus : false,
14922 * @cfg {Boolean} disableClear Disable showing of clear button.
14924 disableClear : false,
14926 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
14928 alwaysQuery : false,
14931 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
14936 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14938 invalidClass : "has-warning",
14941 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14943 validClass : "has-success",
14946 * @cfg {Boolean} specialFilter (true|false) special filter default false
14948 specialFilter : false,
14951 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14953 mobileTouchView : true,
14956 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14958 useNativeIOS : false,
14961 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14963 mobile_restrict_height : false,
14965 ios_options : false,
14977 btnPosition : 'right',
14978 triggerList : true,
14979 showToggleBtn : true,
14981 emptyResultText: 'Empty',
14982 triggerText : 'Select',
14985 // element that contains real text value.. (when hidden is used..)
14987 getAutoCreate : function()
14992 * Render classic select for iso
14995 if(Roo.isIOS && this.useNativeIOS){
14996 cfg = this.getAutoCreateNativeIOS();
15004 if(Roo.isTouch && this.mobileTouchView){
15005 cfg = this.getAutoCreateTouchView();
15012 if(!this.tickable){
15013 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15018 * ComboBox with tickable selections
15021 var align = this.labelAlign || this.parentLabelAlign();
15024 cls : 'form-group roo-combobox-tickable' //input-group
15027 var btn_text_select = '';
15028 var btn_text_done = '';
15029 var btn_text_cancel = '';
15031 if (this.btn_text_show) {
15032 btn_text_select = 'Select';
15033 btn_text_done = 'Done';
15034 btn_text_cancel = 'Cancel';
15039 cls : 'tickable-buttons',
15044 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15045 //html : this.triggerText
15046 html: btn_text_select
15052 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15054 html: btn_text_done
15060 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15062 html: btn_text_cancel
15068 buttons.cn.unshift({
15070 cls: 'roo-select2-search-field-input'
15076 Roo.each(buttons.cn, function(c){
15078 c.cls += ' btn-' + _this.size;
15081 if (_this.disabled) {
15088 style : 'display: contents',
15093 cls: 'form-hidden-field'
15097 cls: 'roo-select2-choices',
15101 cls: 'roo-select2-search-field',
15112 cls: 'roo-select2-container input-group roo-select2-container-multi',
15118 // cls: 'typeahead typeahead-long dropdown-menu',
15119 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15124 if(this.hasFeedback && !this.allowBlank){
15128 cls: 'glyphicon form-control-feedback'
15131 combobox.cn.push(feedback);
15138 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15139 tooltip : 'This field is required'
15141 if (Roo.bootstrap.version == 4) {
15144 style : 'display:none'
15147 if (align ==='left' && this.fieldLabel.length) {
15149 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15156 cls : 'control-label col-form-label',
15157 html : this.fieldLabel
15169 var labelCfg = cfg.cn[1];
15170 var contentCfg = cfg.cn[2];
15173 if(this.indicatorpos == 'right'){
15179 cls : 'control-label col-form-label',
15183 html : this.fieldLabel
15199 labelCfg = cfg.cn[0];
15200 contentCfg = cfg.cn[1];
15204 if(this.labelWidth > 12){
15205 labelCfg.style = "width: " + this.labelWidth + 'px';
15208 if(this.labelWidth < 13 && this.labelmd == 0){
15209 this.labelmd = this.labelWidth;
15212 if(this.labellg > 0){
15213 labelCfg.cls += ' col-lg-' + this.labellg;
15214 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15217 if(this.labelmd > 0){
15218 labelCfg.cls += ' col-md-' + this.labelmd;
15219 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15222 if(this.labelsm > 0){
15223 labelCfg.cls += ' col-sm-' + this.labelsm;
15224 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15227 if(this.labelxs > 0){
15228 labelCfg.cls += ' col-xs-' + this.labelxs;
15229 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15233 } else if ( this.fieldLabel.length) {
15234 // Roo.log(" label");
15239 //cls : 'input-group-addon',
15240 html : this.fieldLabel
15245 if(this.indicatorpos == 'right'){
15249 //cls : 'input-group-addon',
15250 html : this.fieldLabel
15260 // Roo.log(" no label && no align");
15267 ['xs','sm','md','lg'].map(function(size){
15268 if (settings[size]) {
15269 cfg.cls += ' col-' + size + '-' + settings[size];
15277 _initEventsCalled : false,
15280 initEvents: function()
15282 if (this._initEventsCalled) { // as we call render... prevent looping...
15285 this._initEventsCalled = true;
15288 throw "can not find store for combo";
15291 this.indicator = this.indicatorEl();
15293 this.store = Roo.factory(this.store, Roo.data);
15294 this.store.parent = this;
15296 // if we are building from html. then this element is so complex, that we can not really
15297 // use the rendered HTML.
15298 // so we have to trash and replace the previous code.
15299 if (Roo.XComponent.build_from_html) {
15300 // remove this element....
15301 var e = this.el.dom, k=0;
15302 while (e ) { e = e.previousSibling; ++k;}
15307 this.rendered = false;
15309 this.render(this.parent().getChildContainer(true), k);
15312 if(Roo.isIOS && this.useNativeIOS){
15313 this.initIOSView();
15321 if(Roo.isTouch && this.mobileTouchView){
15322 this.initTouchView();
15327 this.initTickableEvents();
15331 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15333 if(this.hiddenName){
15335 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15337 this.hiddenField.dom.value =
15338 this.hiddenValue !== undefined ? this.hiddenValue :
15339 this.value !== undefined ? this.value : '';
15341 // prevent input submission
15342 this.el.dom.removeAttribute('name');
15343 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15348 // this.el.dom.setAttribute('autocomplete', 'off');
15351 var cls = 'x-combo-list';
15353 //this.list = new Roo.Layer({
15354 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15360 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15361 _this.list.setWidth(lw);
15364 this.list.on('mouseover', this.onViewOver, this);
15365 this.list.on('mousemove', this.onViewMove, this);
15366 this.list.on('scroll', this.onViewScroll, this);
15369 this.list.swallowEvent('mousewheel');
15370 this.assetHeight = 0;
15373 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15374 this.assetHeight += this.header.getHeight();
15377 this.innerList = this.list.createChild({cls:cls+'-inner'});
15378 this.innerList.on('mouseover', this.onViewOver, this);
15379 this.innerList.on('mousemove', this.onViewMove, this);
15380 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15382 if(this.allowBlank && !this.pageSize && !this.disableClear){
15383 this.footer = this.list.createChild({cls:cls+'-ft'});
15384 this.pageTb = new Roo.Toolbar(this.footer);
15388 this.footer = this.list.createChild({cls:cls+'-ft'});
15389 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15390 {pageSize: this.pageSize});
15394 if (this.pageTb && this.allowBlank && !this.disableClear) {
15396 this.pageTb.add(new Roo.Toolbar.Fill(), {
15397 cls: 'x-btn-icon x-btn-clear',
15399 handler: function()
15402 _this.clearValue();
15403 _this.onSelect(false, -1);
15408 this.assetHeight += this.footer.getHeight();
15413 this.tpl = Roo.bootstrap.version == 4 ?
15414 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15415 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15418 this.view = new Roo.View(this.list, this.tpl, {
15419 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15421 //this.view.wrapEl.setDisplayed(false);
15422 this.view.on('click', this.onViewClick, this);
15425 this.store.on('beforeload', this.onBeforeLoad, this);
15426 this.store.on('load', this.onLoad, this);
15427 this.store.on('loadexception', this.onLoadException, this);
15429 if(this.resizable){
15430 this.resizer = new Roo.Resizable(this.list, {
15431 pinned:true, handles:'se'
15433 this.resizer.on('resize', function(r, w, h){
15434 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15435 this.listWidth = w;
15436 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15437 this.restrictHeight();
15439 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15442 if(!this.editable){
15443 this.editable = true;
15444 this.setEditable(false);
15449 if (typeof(this.events.add.listeners) != 'undefined') {
15451 this.addicon = this.wrap.createChild(
15452 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15454 this.addicon.on('click', function(e) {
15455 this.fireEvent('add', this);
15458 if (typeof(this.events.edit.listeners) != 'undefined') {
15460 this.editicon = this.wrap.createChild(
15461 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15462 if (this.addicon) {
15463 this.editicon.setStyle('margin-left', '40px');
15465 this.editicon.on('click', function(e) {
15467 // we fire even if inothing is selected..
15468 this.fireEvent('edit', this, this.lastData );
15474 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15475 "up" : function(e){
15476 this.inKeyMode = true;
15480 "down" : function(e){
15481 if(!this.isExpanded()){
15482 this.onTriggerClick();
15484 this.inKeyMode = true;
15489 "enter" : function(e){
15490 // this.onViewClick();
15494 if(this.fireEvent("specialkey", this, e)){
15495 this.onViewClick(false);
15501 "esc" : function(e){
15505 "tab" : function(e){
15508 if(this.fireEvent("specialkey", this, e)){
15509 this.onViewClick(false);
15517 doRelay : function(foo, bar, hname){
15518 if(hname == 'down' || this.scope.isExpanded()){
15519 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15528 this.queryDelay = Math.max(this.queryDelay || 10,
15529 this.mode == 'local' ? 10 : 250);
15532 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15534 if(this.typeAhead){
15535 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15537 if(this.editable !== false){
15538 this.inputEl().on("keyup", this.onKeyUp, this);
15540 if(this.forceSelection){
15541 this.inputEl().on('blur', this.doForce, this);
15545 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15546 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15550 initTickableEvents: function()
15554 if(this.hiddenName){
15556 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15558 this.hiddenField.dom.value =
15559 this.hiddenValue !== undefined ? this.hiddenValue :
15560 this.value !== undefined ? this.value : '';
15562 // prevent input submission
15563 this.el.dom.removeAttribute('name');
15564 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15569 // this.list = this.el.select('ul.dropdown-menu',true).first();
15571 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15572 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15573 if(this.triggerList){
15574 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15577 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15578 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15580 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15581 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15583 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15584 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15586 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15587 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15588 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15591 this.cancelBtn.hide();
15596 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15597 _this.list.setWidth(lw);
15600 this.list.on('mouseover', this.onViewOver, this);
15601 this.list.on('mousemove', this.onViewMove, this);
15603 this.list.on('scroll', this.onViewScroll, this);
15606 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15607 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15610 this.view = new Roo.View(this.list, this.tpl, {
15615 selectedClass: this.selectedClass
15618 //this.view.wrapEl.setDisplayed(false);
15619 this.view.on('click', this.onViewClick, this);
15623 this.store.on('beforeload', this.onBeforeLoad, this);
15624 this.store.on('load', this.onLoad, this);
15625 this.store.on('loadexception', this.onLoadException, this);
15628 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15629 "up" : function(e){
15630 this.inKeyMode = true;
15634 "down" : function(e){
15635 this.inKeyMode = true;
15639 "enter" : function(e){
15640 if(this.fireEvent("specialkey", this, e)){
15641 this.onViewClick(false);
15647 "esc" : function(e){
15648 this.onTickableFooterButtonClick(e, false, false);
15651 "tab" : function(e){
15652 this.fireEvent("specialkey", this, e);
15654 this.onTickableFooterButtonClick(e, false, false);
15661 doRelay : function(e, fn, key){
15662 if(this.scope.isExpanded()){
15663 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15672 this.queryDelay = Math.max(this.queryDelay || 10,
15673 this.mode == 'local' ? 10 : 250);
15676 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15678 if(this.typeAhead){
15679 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15682 if(this.editable !== false){
15683 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15686 this.indicator = this.indicatorEl();
15688 if(this.indicator){
15689 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15690 this.indicator.hide();
15695 onDestroy : function(){
15697 this.view.setStore(null);
15698 this.view.el.removeAllListeners();
15699 this.view.el.remove();
15700 this.view.purgeListeners();
15703 this.list.dom.innerHTML = '';
15707 this.store.un('beforeload', this.onBeforeLoad, this);
15708 this.store.un('load', this.onLoad, this);
15709 this.store.un('loadexception', this.onLoadException, this);
15711 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15715 fireKey : function(e){
15716 if(e.isNavKeyPress() && !this.list.isVisible()){
15717 this.fireEvent("specialkey", this, e);
15722 onResize: function(w, h){
15723 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15725 // if(typeof w != 'number'){
15726 // // we do not handle it!?!?
15729 // var tw = this.trigger.getWidth();
15730 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15731 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15733 // this.inputEl().setWidth( this.adjustWidth('input', x));
15735 // //this.trigger.setStyle('left', x+'px');
15737 // if(this.list && this.listWidth === undefined){
15738 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15739 // this.list.setWidth(lw);
15740 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15748 * Allow or prevent the user from directly editing the field text. If false is passed,
15749 * the user will only be able to select from the items defined in the dropdown list. This method
15750 * is the runtime equivalent of setting the 'editable' config option at config time.
15751 * @param {Boolean} value True to allow the user to directly edit the field text
15753 setEditable : function(value){
15754 if(value == this.editable){
15757 this.editable = value;
15759 this.inputEl().dom.setAttribute('readOnly', true);
15760 this.inputEl().on('mousedown', this.onTriggerClick, this);
15761 this.inputEl().addClass('x-combo-noedit');
15763 this.inputEl().dom.setAttribute('readOnly', false);
15764 this.inputEl().un('mousedown', this.onTriggerClick, this);
15765 this.inputEl().removeClass('x-combo-noedit');
15771 onBeforeLoad : function(combo,opts){
15772 if(!this.hasFocus){
15776 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15778 this.restrictHeight();
15779 this.selectedIndex = -1;
15783 onLoad : function(){
15785 this.hasQuery = false;
15787 if(!this.hasFocus){
15791 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15792 this.loading.hide();
15795 if(this.store.getCount() > 0){
15798 this.restrictHeight();
15799 if(this.lastQuery == this.allQuery){
15800 if(this.editable && !this.tickable){
15801 this.inputEl().dom.select();
15805 !this.selectByValue(this.value, true) &&
15808 !this.store.lastOptions ||
15809 typeof(this.store.lastOptions.add) == 'undefined' ||
15810 this.store.lastOptions.add != true
15813 this.select(0, true);
15816 if(this.autoFocus){
15819 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15820 this.taTask.delay(this.typeAheadDelay);
15824 this.onEmptyResults();
15830 onLoadException : function()
15832 this.hasQuery = false;
15834 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15835 this.loading.hide();
15838 if(this.tickable && this.editable){
15843 // only causes errors at present
15844 //Roo.log(this.store.reader.jsonData);
15845 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15847 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15853 onTypeAhead : function(){
15854 if(this.store.getCount() > 0){
15855 var r = this.store.getAt(0);
15856 var newValue = r.data[this.displayField];
15857 var len = newValue.length;
15858 var selStart = this.getRawValue().length;
15860 if(selStart != len){
15861 this.setRawValue(newValue);
15862 this.selectText(selStart, newValue.length);
15868 onSelect : function(record, index){
15870 if(this.fireEvent('beforeselect', this, record, index) !== false){
15872 this.setFromData(index > -1 ? record.data : false);
15875 this.fireEvent('select', this, record, index);
15880 * Returns the currently selected field value or empty string if no value is set.
15881 * @return {String} value The selected value
15883 getValue : function()
15885 if(Roo.isIOS && this.useNativeIOS){
15886 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15890 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15893 if(this.valueField){
15894 return typeof this.value != 'undefined' ? this.value : '';
15896 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15900 getRawValue : function()
15902 if(Roo.isIOS && this.useNativeIOS){
15903 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15906 var v = this.inputEl().getValue();
15912 * Clears any text/value currently set in the field
15914 clearValue : function(){
15916 if(this.hiddenField){
15917 this.hiddenField.dom.value = '';
15920 this.setRawValue('');
15921 this.lastSelectionText = '';
15922 this.lastData = false;
15924 var close = this.closeTriggerEl();
15935 * Sets the specified value into the field. If the value finds a match, the corresponding record text
15936 * will be displayed in the field. If the value does not match the data value of an existing item,
15937 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15938 * Otherwise the field will be blank (although the value will still be set).
15939 * @param {String} value The value to match
15941 setValue : function(v)
15943 if(Roo.isIOS && this.useNativeIOS){
15944 this.setIOSValue(v);
15954 if(this.valueField){
15955 var r = this.findRecord(this.valueField, v);
15957 text = r.data[this.displayField];
15958 }else if(this.valueNotFoundText !== undefined){
15959 text = this.valueNotFoundText;
15962 this.lastSelectionText = text;
15963 if(this.hiddenField){
15964 this.hiddenField.dom.value = v;
15966 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15969 var close = this.closeTriggerEl();
15972 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15978 * @property {Object} the last set data for the element
15983 * Sets the value of the field based on a object which is related to the record format for the store.
15984 * @param {Object} value the value to set as. or false on reset?
15986 setFromData : function(o){
15993 var dv = ''; // display value
15994 var vv = ''; // value value..
15996 if (this.displayField) {
15997 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15999 // this is an error condition!!!
16000 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16003 if(this.valueField){
16004 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16007 var close = this.closeTriggerEl();
16010 if(dv.length || vv * 1 > 0){
16012 this.blockFocus=true;
16018 if(this.hiddenField){
16019 this.hiddenField.dom.value = vv;
16021 this.lastSelectionText = dv;
16022 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16026 // no hidden field.. - we store the value in 'value', but still display
16027 // display field!!!!
16028 this.lastSelectionText = dv;
16029 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16036 reset : function(){
16037 // overridden so that last data is reset..
16044 this.setValue(this.originalValue);
16045 //this.clearInvalid();
16046 this.lastData = false;
16048 this.view.clearSelections();
16054 findRecord : function(prop, value){
16056 if(this.store.getCount() > 0){
16057 this.store.each(function(r){
16058 if(r.data[prop] == value){
16068 getName: function()
16070 // returns hidden if it's set..
16071 if (!this.rendered) {return ''};
16072 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16076 onViewMove : function(e, t){
16077 this.inKeyMode = false;
16081 onViewOver : function(e, t){
16082 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16085 var item = this.view.findItemFromChild(t);
16088 var index = this.view.indexOf(item);
16089 this.select(index, false);
16094 onViewClick : function(view, doFocus, el, e)
16096 var index = this.view.getSelectedIndexes()[0];
16098 var r = this.store.getAt(index);
16102 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16109 Roo.each(this.tickItems, function(v,k){
16111 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16113 _this.tickItems.splice(k, 1);
16115 if(typeof(e) == 'undefined' && view == false){
16116 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16128 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16129 this.tickItems.push(r.data);
16132 if(typeof(e) == 'undefined' && view == false){
16133 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16140 this.onSelect(r, index);
16142 if(doFocus !== false && !this.blockFocus){
16143 this.inputEl().focus();
16148 restrictHeight : function(){
16149 //this.innerList.dom.style.height = '';
16150 //var inner = this.innerList.dom;
16151 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16152 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16153 //this.list.beginUpdate();
16154 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16155 this.list.alignTo(this.inputEl(), this.listAlign);
16156 this.list.alignTo(this.inputEl(), this.listAlign);
16157 //this.list.endUpdate();
16161 onEmptyResults : function(){
16163 if(this.tickable && this.editable){
16164 this.hasFocus = false;
16165 this.restrictHeight();
16173 * Returns true if the dropdown list is expanded, else false.
16175 isExpanded : function(){
16176 return this.list.isVisible();
16180 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16181 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16182 * @param {String} value The data value of the item to select
16183 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16184 * selected item if it is not currently in view (defaults to true)
16185 * @return {Boolean} True if the value matched an item in the list, else false
16187 selectByValue : function(v, scrollIntoView){
16188 if(v !== undefined && v !== null){
16189 var r = this.findRecord(this.valueField || this.displayField, v);
16191 this.select(this.store.indexOf(r), scrollIntoView);
16199 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16200 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16201 * @param {Number} index The zero-based index of the list item to select
16202 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16203 * selected item if it is not currently in view (defaults to true)
16205 select : function(index, scrollIntoView){
16206 this.selectedIndex = index;
16207 this.view.select(index);
16208 if(scrollIntoView !== false){
16209 var el = this.view.getNode(index);
16211 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16214 this.list.scrollChildIntoView(el, false);
16220 selectNext : function(){
16221 var ct = this.store.getCount();
16223 if(this.selectedIndex == -1){
16225 }else if(this.selectedIndex < ct-1){
16226 this.select(this.selectedIndex+1);
16232 selectPrev : function(){
16233 var ct = this.store.getCount();
16235 if(this.selectedIndex == -1){
16237 }else if(this.selectedIndex != 0){
16238 this.select(this.selectedIndex-1);
16244 onKeyUp : function(e){
16245 if(this.editable !== false && !e.isSpecialKey()){
16246 this.lastKey = e.getKey();
16247 this.dqTask.delay(this.queryDelay);
16252 validateBlur : function(){
16253 return !this.list || !this.list.isVisible();
16257 initQuery : function(){
16259 var v = this.getRawValue();
16261 if(this.tickable && this.editable){
16262 v = this.tickableInputEl().getValue();
16269 doForce : function(){
16270 if(this.inputEl().dom.value.length > 0){
16271 this.inputEl().dom.value =
16272 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16278 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16279 * query allowing the query action to be canceled if needed.
16280 * @param {String} query The SQL query to execute
16281 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16282 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16283 * saved in the current store (defaults to false)
16285 doQuery : function(q, forceAll){
16287 if(q === undefined || q === null){
16292 forceAll: forceAll,
16296 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16301 forceAll = qe.forceAll;
16302 if(forceAll === true || (q.length >= this.minChars)){
16304 this.hasQuery = true;
16306 if(this.lastQuery != q || this.alwaysQuery){
16307 this.lastQuery = q;
16308 if(this.mode == 'local'){
16309 this.selectedIndex = -1;
16311 this.store.clearFilter();
16314 if(this.specialFilter){
16315 this.fireEvent('specialfilter', this);
16320 this.store.filter(this.displayField, q);
16323 this.store.fireEvent("datachanged", this.store);
16330 this.store.baseParams[this.queryParam] = q;
16332 var options = {params : this.getParams(q)};
16335 options.add = true;
16336 options.params.start = this.page * this.pageSize;
16339 this.store.load(options);
16342 * this code will make the page width larger, at the beginning, the list not align correctly,
16343 * we should expand the list on onLoad
16344 * so command out it
16349 this.selectedIndex = -1;
16354 this.loadNext = false;
16358 getParams : function(q){
16360 //p[this.queryParam] = q;
16364 p.limit = this.pageSize;
16370 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16372 collapse : function(){
16373 if(!this.isExpanded()){
16379 this.hasFocus = false;
16383 this.cancelBtn.hide();
16384 this.trigger.show();
16387 this.tickableInputEl().dom.value = '';
16388 this.tickableInputEl().blur();
16393 Roo.get(document).un('mousedown', this.collapseIf, this);
16394 Roo.get(document).un('mousewheel', this.collapseIf, this);
16395 if (!this.editable) {
16396 Roo.get(document).un('keydown', this.listKeyPress, this);
16398 this.fireEvent('collapse', this);
16404 collapseIf : function(e){
16405 var in_combo = e.within(this.el);
16406 var in_list = e.within(this.list);
16407 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16409 if (in_combo || in_list || is_list) {
16410 //e.stopPropagation();
16415 this.onTickableFooterButtonClick(e, false, false);
16423 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16425 expand : function(){
16427 if(this.isExpanded() || !this.hasFocus){
16431 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16432 this.list.setWidth(lw);
16438 this.restrictHeight();
16442 this.tickItems = Roo.apply([], this.item);
16445 this.cancelBtn.show();
16446 this.trigger.hide();
16449 this.tickableInputEl().focus();
16454 Roo.get(document).on('mousedown', this.collapseIf, this);
16455 Roo.get(document).on('mousewheel', this.collapseIf, this);
16456 if (!this.editable) {
16457 Roo.get(document).on('keydown', this.listKeyPress, this);
16460 this.fireEvent('expand', this);
16464 // Implements the default empty TriggerField.onTriggerClick function
16465 onTriggerClick : function(e)
16467 Roo.log('trigger click');
16469 if(this.disabled || !this.triggerList){
16474 this.loadNext = false;
16476 if(this.isExpanded()){
16478 if (!this.blockFocus) {
16479 this.inputEl().focus();
16483 this.hasFocus = true;
16484 if(this.triggerAction == 'all') {
16485 this.doQuery(this.allQuery, true);
16487 this.doQuery(this.getRawValue());
16489 if (!this.blockFocus) {
16490 this.inputEl().focus();
16495 onTickableTriggerClick : function(e)
16502 this.loadNext = false;
16503 this.hasFocus = true;
16505 if(this.triggerAction == 'all') {
16506 this.doQuery(this.allQuery, true);
16508 this.doQuery(this.getRawValue());
16512 onSearchFieldClick : function(e)
16514 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16515 this.onTickableFooterButtonClick(e, false, false);
16519 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16524 this.loadNext = false;
16525 this.hasFocus = true;
16527 if(this.triggerAction == 'all') {
16528 this.doQuery(this.allQuery, true);
16530 this.doQuery(this.getRawValue());
16534 listKeyPress : function(e)
16536 //Roo.log('listkeypress');
16537 // scroll to first matching element based on key pres..
16538 if (e.isSpecialKey()) {
16541 var k = String.fromCharCode(e.getKey()).toUpperCase();
16544 var csel = this.view.getSelectedNodes();
16545 var cselitem = false;
16547 var ix = this.view.indexOf(csel[0]);
16548 cselitem = this.store.getAt(ix);
16549 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16555 this.store.each(function(v) {
16557 // start at existing selection.
16558 if (cselitem.id == v.id) {
16564 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16565 match = this.store.indexOf(v);
16571 if (match === false) {
16572 return true; // no more action?
16575 this.view.select(match);
16576 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16577 sn.scrollIntoView(sn.dom.parentNode, false);
16580 onViewScroll : function(e, t){
16582 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){
16586 this.hasQuery = true;
16588 this.loading = this.list.select('.loading', true).first();
16590 if(this.loading === null){
16591 this.list.createChild({
16593 cls: 'loading roo-select2-more-results roo-select2-active',
16594 html: 'Loading more results...'
16597 this.loading = this.list.select('.loading', true).first();
16599 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16601 this.loading.hide();
16604 this.loading.show();
16609 this.loadNext = true;
16611 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16616 addItem : function(o)
16618 var dv = ''; // display value
16620 if (this.displayField) {
16621 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16623 // this is an error condition!!!
16624 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16631 var choice = this.choices.createChild({
16633 cls: 'roo-select2-search-choice',
16642 cls: 'roo-select2-search-choice-close fa fa-times',
16647 }, this.searchField);
16649 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16651 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16659 this.inputEl().dom.value = '';
16664 onRemoveItem : function(e, _self, o)
16666 e.preventDefault();
16668 this.lastItem = Roo.apply([], this.item);
16670 var index = this.item.indexOf(o.data) * 1;
16673 Roo.log('not this item?!');
16677 this.item.splice(index, 1);
16682 this.fireEvent('remove', this, e);
16688 syncValue : function()
16690 if(!this.item.length){
16697 Roo.each(this.item, function(i){
16698 if(_this.valueField){
16699 value.push(i[_this.valueField]);
16706 this.value = value.join(',');
16708 if(this.hiddenField){
16709 this.hiddenField.dom.value = this.value;
16712 this.store.fireEvent("datachanged", this.store);
16717 clearItem : function()
16719 if(!this.multiple){
16725 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16733 if(this.tickable && !Roo.isTouch){
16734 this.view.refresh();
16738 inputEl: function ()
16740 if(Roo.isIOS && this.useNativeIOS){
16741 return this.el.select('select.roo-ios-select', true).first();
16744 if(Roo.isTouch && this.mobileTouchView){
16745 return this.el.select('input.form-control',true).first();
16749 return this.searchField;
16752 return this.el.select('input.form-control',true).first();
16755 onTickableFooterButtonClick : function(e, btn, el)
16757 e.preventDefault();
16759 this.lastItem = Roo.apply([], this.item);
16761 if(btn && btn.name == 'cancel'){
16762 this.tickItems = Roo.apply([], this.item);
16771 Roo.each(this.tickItems, function(o){
16779 validate : function()
16781 if(this.getVisibilityEl().hasClass('hidden')){
16785 var v = this.getRawValue();
16788 v = this.getValue();
16791 if(this.disabled || this.allowBlank || v.length){
16796 this.markInvalid();
16800 tickableInputEl : function()
16802 if(!this.tickable || !this.editable){
16803 return this.inputEl();
16806 return this.inputEl().select('.roo-select2-search-field-input', true).first();
16810 getAutoCreateTouchView : function()
16815 cls: 'form-group' //input-group
16821 type : this.inputType,
16822 cls : 'form-control x-combo-noedit',
16823 autocomplete: 'new-password',
16824 placeholder : this.placeholder || '',
16829 input.name = this.name;
16833 input.cls += ' input-' + this.size;
16836 if (this.disabled) {
16837 input.disabled = true;
16848 inputblock.cls += ' input-group';
16850 inputblock.cn.unshift({
16852 cls : 'input-group-addon input-group-prepend input-group-text',
16857 if(this.removable && !this.multiple){
16858 inputblock.cls += ' roo-removable';
16860 inputblock.cn.push({
16863 cls : 'roo-combo-removable-btn close'
16867 if(this.hasFeedback && !this.allowBlank){
16869 inputblock.cls += ' has-feedback';
16871 inputblock.cn.push({
16873 cls: 'glyphicon form-control-feedback'
16880 inputblock.cls += (this.before) ? '' : ' input-group';
16882 inputblock.cn.push({
16884 cls : 'input-group-addon input-group-append input-group-text',
16890 var ibwrap = inputblock;
16895 cls: 'roo-select2-choices',
16899 cls: 'roo-select2-search-field',
16912 cls: 'roo-select2-container input-group roo-touchview-combobox ',
16917 cls: 'form-hidden-field'
16923 if(!this.multiple && this.showToggleBtn){
16929 if (this.caret != false) {
16932 cls: 'fa fa-' + this.caret
16939 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16941 Roo.bootstrap.version == 3 ? caret : '',
16944 cls: 'combobox-clear',
16958 combobox.cls += ' roo-select2-container-multi';
16961 var align = this.labelAlign || this.parentLabelAlign();
16963 if (align ==='left' && this.fieldLabel.length) {
16968 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16969 tooltip : 'This field is required'
16973 cls : 'control-label col-form-label',
16974 html : this.fieldLabel
16985 var labelCfg = cfg.cn[1];
16986 var contentCfg = cfg.cn[2];
16989 if(this.indicatorpos == 'right'){
16994 cls : 'control-label col-form-label',
16998 html : this.fieldLabel
17002 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17003 tooltip : 'This field is required'
17016 labelCfg = cfg.cn[0];
17017 contentCfg = cfg.cn[1];
17022 if(this.labelWidth > 12){
17023 labelCfg.style = "width: " + this.labelWidth + 'px';
17026 if(this.labelWidth < 13 && this.labelmd == 0){
17027 this.labelmd = this.labelWidth;
17030 if(this.labellg > 0){
17031 labelCfg.cls += ' col-lg-' + this.labellg;
17032 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17035 if(this.labelmd > 0){
17036 labelCfg.cls += ' col-md-' + this.labelmd;
17037 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17040 if(this.labelsm > 0){
17041 labelCfg.cls += ' col-sm-' + this.labelsm;
17042 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17045 if(this.labelxs > 0){
17046 labelCfg.cls += ' col-xs-' + this.labelxs;
17047 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17051 } else if ( this.fieldLabel.length) {
17055 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17056 tooltip : 'This field is required'
17060 cls : 'control-label',
17061 html : this.fieldLabel
17072 if(this.indicatorpos == 'right'){
17076 cls : 'control-label',
17077 html : this.fieldLabel,
17081 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17082 tooltip : 'This field is required'
17099 var settings = this;
17101 ['xs','sm','md','lg'].map(function(size){
17102 if (settings[size]) {
17103 cfg.cls += ' col-' + size + '-' + settings[size];
17110 initTouchView : function()
17112 this.renderTouchView();
17114 this.touchViewEl.on('scroll', function(){
17115 this.el.dom.scrollTop = 0;
17118 this.originalValue = this.getValue();
17120 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17122 this.inputEl().on("click", this.showTouchView, this);
17123 if (this.triggerEl) {
17124 this.triggerEl.on("click", this.showTouchView, this);
17128 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17129 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17131 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17133 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17134 this.store.on('load', this.onTouchViewLoad, this);
17135 this.store.on('loadexception', this.onTouchViewLoadException, this);
17137 if(this.hiddenName){
17139 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17141 this.hiddenField.dom.value =
17142 this.hiddenValue !== undefined ? this.hiddenValue :
17143 this.value !== undefined ? this.value : '';
17145 this.el.dom.removeAttribute('name');
17146 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17150 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17151 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17154 if(this.removable && !this.multiple){
17155 var close = this.closeTriggerEl();
17157 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17158 close.on('click', this.removeBtnClick, this, close);
17162 * fix the bug in Safari iOS8
17164 this.inputEl().on("focus", function(e){
17165 document.activeElement.blur();
17168 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17175 renderTouchView : function()
17177 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17178 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17180 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17181 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17183 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17184 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17185 this.touchViewBodyEl.setStyle('overflow', 'auto');
17187 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17188 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17190 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17191 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17195 showTouchView : function()
17201 this.touchViewHeaderEl.hide();
17203 if(this.modalTitle.length){
17204 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17205 this.touchViewHeaderEl.show();
17208 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17209 this.touchViewEl.show();
17211 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17213 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17214 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17216 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17218 if(this.modalTitle.length){
17219 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17222 this.touchViewBodyEl.setHeight(bodyHeight);
17226 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17228 this.touchViewEl.addClass('in');
17231 if(this._touchViewMask){
17232 Roo.get(document.body).addClass("x-body-masked");
17233 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17234 this._touchViewMask.setStyle('z-index', 10000);
17235 this._touchViewMask.addClass('show');
17238 this.doTouchViewQuery();
17242 hideTouchView : function()
17244 this.touchViewEl.removeClass('in');
17248 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17250 this.touchViewEl.setStyle('display', 'none');
17253 if(this._touchViewMask){
17254 this._touchViewMask.removeClass('show');
17255 Roo.get(document.body).removeClass("x-body-masked");
17259 setTouchViewValue : function()
17266 Roo.each(this.tickItems, function(o){
17271 this.hideTouchView();
17274 doTouchViewQuery : function()
17283 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17287 if(!this.alwaysQuery || this.mode == 'local'){
17288 this.onTouchViewLoad();
17295 onTouchViewBeforeLoad : function(combo,opts)
17301 onTouchViewLoad : function()
17303 if(this.store.getCount() < 1){
17304 this.onTouchViewEmptyResults();
17308 this.clearTouchView();
17310 var rawValue = this.getRawValue();
17312 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17314 this.tickItems = [];
17316 this.store.data.each(function(d, rowIndex){
17317 var row = this.touchViewListGroup.createChild(template);
17319 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17320 row.addClass(d.data.cls);
17323 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17326 html : d.data[this.displayField]
17329 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17330 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17333 row.removeClass('selected');
17334 if(!this.multiple && this.valueField &&
17335 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17338 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17339 row.addClass('selected');
17342 if(this.multiple && this.valueField &&
17343 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17347 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17348 this.tickItems.push(d.data);
17351 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17355 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17357 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17359 if(this.modalTitle.length){
17360 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17363 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17365 if(this.mobile_restrict_height && listHeight < bodyHeight){
17366 this.touchViewBodyEl.setHeight(listHeight);
17371 if(firstChecked && listHeight > bodyHeight){
17372 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17377 onTouchViewLoadException : function()
17379 this.hideTouchView();
17382 onTouchViewEmptyResults : function()
17384 this.clearTouchView();
17386 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17388 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17392 clearTouchView : function()
17394 this.touchViewListGroup.dom.innerHTML = '';
17397 onTouchViewClick : function(e, el, o)
17399 e.preventDefault();
17402 var rowIndex = o.rowIndex;
17404 var r = this.store.getAt(rowIndex);
17406 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17408 if(!this.multiple){
17409 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17410 c.dom.removeAttribute('checked');
17413 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17415 this.setFromData(r.data);
17417 var close = this.closeTriggerEl();
17423 this.hideTouchView();
17425 this.fireEvent('select', this, r, rowIndex);
17430 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17431 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17432 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17436 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17437 this.addItem(r.data);
17438 this.tickItems.push(r.data);
17442 getAutoCreateNativeIOS : function()
17445 cls: 'form-group' //input-group,
17450 cls : 'roo-ios-select'
17454 combobox.name = this.name;
17457 if (this.disabled) {
17458 combobox.disabled = true;
17461 var settings = this;
17463 ['xs','sm','md','lg'].map(function(size){
17464 if (settings[size]) {
17465 cfg.cls += ' col-' + size + '-' + settings[size];
17475 initIOSView : function()
17477 this.store.on('load', this.onIOSViewLoad, this);
17482 onIOSViewLoad : function()
17484 if(this.store.getCount() < 1){
17488 this.clearIOSView();
17490 if(this.allowBlank) {
17492 var default_text = '-- SELECT --';
17494 if(this.placeholder.length){
17495 default_text = this.placeholder;
17498 if(this.emptyTitle.length){
17499 default_text += ' - ' + this.emptyTitle + ' -';
17502 var opt = this.inputEl().createChild({
17505 html : default_text
17509 o[this.valueField] = 0;
17510 o[this.displayField] = default_text;
17512 this.ios_options.push({
17519 this.store.data.each(function(d, rowIndex){
17523 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17524 html = d.data[this.displayField];
17529 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17530 value = d.data[this.valueField];
17539 if(this.value == d.data[this.valueField]){
17540 option['selected'] = true;
17543 var opt = this.inputEl().createChild(option);
17545 this.ios_options.push({
17552 this.inputEl().on('change', function(){
17553 this.fireEvent('select', this);
17558 clearIOSView: function()
17560 this.inputEl().dom.innerHTML = '';
17562 this.ios_options = [];
17565 setIOSValue: function(v)
17569 if(!this.ios_options){
17573 Roo.each(this.ios_options, function(opts){
17575 opts.el.dom.removeAttribute('selected');
17577 if(opts.data[this.valueField] != v){
17581 opts.el.dom.setAttribute('selected', true);
17587 * @cfg {Boolean} grow
17591 * @cfg {Number} growMin
17595 * @cfg {Number} growMax
17604 Roo.apply(Roo.bootstrap.ComboBox, {
17608 cls: 'modal-header',
17630 cls: 'list-group-item',
17634 cls: 'roo-combobox-list-group-item-value'
17638 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17652 listItemCheckbox : {
17654 cls: 'list-group-item',
17658 cls: 'roo-combobox-list-group-item-value'
17662 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17678 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17683 cls: 'modal-footer',
17691 cls: 'col-xs-6 text-left',
17694 cls: 'btn btn-danger roo-touch-view-cancel',
17700 cls: 'col-xs-6 text-right',
17703 cls: 'btn btn-success roo-touch-view-ok',
17714 Roo.apply(Roo.bootstrap.ComboBox, {
17716 touchViewTemplate : {
17718 cls: 'modal fade roo-combobox-touch-view',
17722 cls: 'modal-dialog',
17723 style : 'position:fixed', // we have to fix position....
17727 cls: 'modal-content',
17729 Roo.bootstrap.ComboBox.header,
17730 Roo.bootstrap.ComboBox.body,
17731 Roo.bootstrap.ComboBox.footer
17740 * Ext JS Library 1.1.1
17741 * Copyright(c) 2006-2007, Ext JS, LLC.
17743 * Originally Released Under LGPL - original licence link has changed is not relivant.
17746 * <script type="text/javascript">
17751 * @extends Roo.util.Observable
17752 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17753 * This class also supports single and multi selection modes. <br>
17754 * Create a data model bound view:
17756 var store = new Roo.data.Store(...);
17758 var view = new Roo.View({
17760 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
17762 singleSelect: true,
17763 selectedClass: "ydataview-selected",
17767 // listen for node click?
17768 view.on("click", function(vw, index, node, e){
17769 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17773 dataModel.load("foobar.xml");
17775 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17777 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17778 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17780 * Note: old style constructor is still suported (container, template, config)
17783 * Create a new View
17784 * @param {Object} config The config object
17787 Roo.View = function(config, depreciated_tpl, depreciated_config){
17789 this.parent = false;
17791 if (typeof(depreciated_tpl) == 'undefined') {
17792 // new way.. - universal constructor.
17793 Roo.apply(this, config);
17794 this.el = Roo.get(this.el);
17797 this.el = Roo.get(config);
17798 this.tpl = depreciated_tpl;
17799 Roo.apply(this, depreciated_config);
17801 this.wrapEl = this.el.wrap().wrap();
17802 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17805 if(typeof(this.tpl) == "string"){
17806 this.tpl = new Roo.Template(this.tpl);
17808 // support xtype ctors..
17809 this.tpl = new Roo.factory(this.tpl, Roo);
17813 this.tpl.compile();
17818 * @event beforeclick
17819 * Fires before a click is processed. Returns false to cancel the default action.
17820 * @param {Roo.View} this
17821 * @param {Number} index The index of the target node
17822 * @param {HTMLElement} node The target node
17823 * @param {Roo.EventObject} e The raw event object
17825 "beforeclick" : true,
17828 * Fires when a template node is clicked.
17829 * @param {Roo.View} this
17830 * @param {Number} index The index of the target node
17831 * @param {HTMLElement} node The target node
17832 * @param {Roo.EventObject} e The raw event object
17837 * Fires when a template node is double clicked.
17838 * @param {Roo.View} this
17839 * @param {Number} index The index of the target node
17840 * @param {HTMLElement} node The target node
17841 * @param {Roo.EventObject} e The raw event object
17845 * @event contextmenu
17846 * Fires when a template node is right clicked.
17847 * @param {Roo.View} this
17848 * @param {Number} index The index of the target node
17849 * @param {HTMLElement} node The target node
17850 * @param {Roo.EventObject} e The raw event object
17852 "contextmenu" : true,
17854 * @event selectionchange
17855 * Fires when the selected nodes change.
17856 * @param {Roo.View} this
17857 * @param {Array} selections Array of the selected nodes
17859 "selectionchange" : true,
17862 * @event beforeselect
17863 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17864 * @param {Roo.View} this
17865 * @param {HTMLElement} node The node to be selected
17866 * @param {Array} selections Array of currently selected nodes
17868 "beforeselect" : true,
17870 * @event preparedata
17871 * Fires on every row to render, to allow you to change the data.
17872 * @param {Roo.View} this
17873 * @param {Object} data to be rendered (change this)
17875 "preparedata" : true
17883 "click": this.onClick,
17884 "dblclick": this.onDblClick,
17885 "contextmenu": this.onContextMenu,
17889 this.selections = [];
17891 this.cmp = new Roo.CompositeElementLite([]);
17893 this.store = Roo.factory(this.store, Roo.data);
17894 this.setStore(this.store, true);
17897 if ( this.footer && this.footer.xtype) {
17899 var fctr = this.wrapEl.appendChild(document.createElement("div"));
17901 this.footer.dataSource = this.store;
17902 this.footer.container = fctr;
17903 this.footer = Roo.factory(this.footer, Roo);
17904 fctr.insertFirst(this.el);
17906 // this is a bit insane - as the paging toolbar seems to detach the el..
17907 // dom.parentNode.parentNode.parentNode
17908 // they get detached?
17912 Roo.View.superclass.constructor.call(this);
17917 Roo.extend(Roo.View, Roo.util.Observable, {
17920 * @cfg {Roo.data.Store} store Data store to load data from.
17925 * @cfg {String|Roo.Element} el The container element.
17930 * @cfg {String|Roo.Template} tpl The template used by this View
17934 * @cfg {String} dataName the named area of the template to use as the data area
17935 * Works with domtemplates roo-name="name"
17939 * @cfg {String} selectedClass The css class to add to selected nodes
17941 selectedClass : "x-view-selected",
17943 * @cfg {String} emptyText The empty text to show when nothing is loaded.
17948 * @cfg {String} text to display on mask (default Loading)
17952 * @cfg {Boolean} multiSelect Allow multiple selection
17954 multiSelect : false,
17956 * @cfg {Boolean} singleSelect Allow single selection
17958 singleSelect: false,
17961 * @cfg {Boolean} toggleSelect - selecting
17963 toggleSelect : false,
17966 * @cfg {Boolean} tickable - selecting
17971 * Returns the element this view is bound to.
17972 * @return {Roo.Element}
17974 getEl : function(){
17975 return this.wrapEl;
17981 * Refreshes the view. - called by datachanged on the store. - do not call directly.
17983 refresh : function(){
17984 //Roo.log('refresh');
17987 // if we are using something like 'domtemplate', then
17988 // the what gets used is:
17989 // t.applySubtemplate(NAME, data, wrapping data..)
17990 // the outer template then get' applied with
17991 // the store 'extra data'
17992 // and the body get's added to the
17993 // roo-name="data" node?
17994 // <span class='roo-tpl-{name}'></span> ?????
17998 this.clearSelections();
17999 this.el.update("");
18001 var records = this.store.getRange();
18002 if(records.length < 1) {
18004 // is this valid?? = should it render a template??
18006 this.el.update(this.emptyText);
18010 if (this.dataName) {
18011 this.el.update(t.apply(this.store.meta)); //????
18012 el = this.el.child('.roo-tpl-' + this.dataName);
18015 for(var i = 0, len = records.length; i < len; i++){
18016 var data = this.prepareData(records[i].data, i, records[i]);
18017 this.fireEvent("preparedata", this, data, i, records[i]);
18019 var d = Roo.apply({}, data);
18022 Roo.apply(d, {'roo-id' : Roo.id()});
18026 Roo.each(this.parent.item, function(item){
18027 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18030 Roo.apply(d, {'roo-data-checked' : 'checked'});
18034 html[html.length] = Roo.util.Format.trim(
18036 t.applySubtemplate(this.dataName, d, this.store.meta) :
18043 el.update(html.join(""));
18044 this.nodes = el.dom.childNodes;
18045 this.updateIndexes(0);
18050 * Function to override to reformat the data that is sent to
18051 * the template for each node.
18052 * DEPRICATED - use the preparedata event handler.
18053 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18054 * a JSON object for an UpdateManager bound view).
18056 prepareData : function(data, index, record)
18058 this.fireEvent("preparedata", this, data, index, record);
18062 onUpdate : function(ds, record){
18063 // Roo.log('on update');
18064 this.clearSelections();
18065 var index = this.store.indexOf(record);
18066 var n = this.nodes[index];
18067 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18068 n.parentNode.removeChild(n);
18069 this.updateIndexes(index, index);
18075 onAdd : function(ds, records, index)
18077 //Roo.log(['on Add', ds, records, index] );
18078 this.clearSelections();
18079 if(this.nodes.length == 0){
18083 var n = this.nodes[index];
18084 for(var i = 0, len = records.length; i < len; i++){
18085 var d = this.prepareData(records[i].data, i, records[i]);
18087 this.tpl.insertBefore(n, d);
18090 this.tpl.append(this.el, d);
18093 this.updateIndexes(index);
18096 onRemove : function(ds, record, index){
18097 // Roo.log('onRemove');
18098 this.clearSelections();
18099 var el = this.dataName ?
18100 this.el.child('.roo-tpl-' + this.dataName) :
18103 el.dom.removeChild(this.nodes[index]);
18104 this.updateIndexes(index);
18108 * Refresh an individual node.
18109 * @param {Number} index
18111 refreshNode : function(index){
18112 this.onUpdate(this.store, this.store.getAt(index));
18115 updateIndexes : function(startIndex, endIndex){
18116 var ns = this.nodes;
18117 startIndex = startIndex || 0;
18118 endIndex = endIndex || ns.length - 1;
18119 for(var i = startIndex; i <= endIndex; i++){
18120 ns[i].nodeIndex = i;
18125 * Changes the data store this view uses and refresh the view.
18126 * @param {Store} store
18128 setStore : function(store, initial){
18129 if(!initial && this.store){
18130 this.store.un("datachanged", this.refresh);
18131 this.store.un("add", this.onAdd);
18132 this.store.un("remove", this.onRemove);
18133 this.store.un("update", this.onUpdate);
18134 this.store.un("clear", this.refresh);
18135 this.store.un("beforeload", this.onBeforeLoad);
18136 this.store.un("load", this.onLoad);
18137 this.store.un("loadexception", this.onLoad);
18141 store.on("datachanged", this.refresh, this);
18142 store.on("add", this.onAdd, this);
18143 store.on("remove", this.onRemove, this);
18144 store.on("update", this.onUpdate, this);
18145 store.on("clear", this.refresh, this);
18146 store.on("beforeload", this.onBeforeLoad, this);
18147 store.on("load", this.onLoad, this);
18148 store.on("loadexception", this.onLoad, this);
18156 * onbeforeLoad - masks the loading area.
18159 onBeforeLoad : function(store,opts)
18161 //Roo.log('onBeforeLoad');
18163 this.el.update("");
18165 this.el.mask(this.mask ? this.mask : "Loading" );
18167 onLoad : function ()
18174 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18175 * @param {HTMLElement} node
18176 * @return {HTMLElement} The template node
18178 findItemFromChild : function(node){
18179 var el = this.dataName ?
18180 this.el.child('.roo-tpl-' + this.dataName,true) :
18183 if(!node || node.parentNode == el){
18186 var p = node.parentNode;
18187 while(p && p != el){
18188 if(p.parentNode == el){
18197 onClick : function(e){
18198 var item = this.findItemFromChild(e.getTarget());
18200 var index = this.indexOf(item);
18201 if(this.onItemClick(item, index, e) !== false){
18202 this.fireEvent("click", this, index, item, e);
18205 this.clearSelections();
18210 onContextMenu : function(e){
18211 var item = this.findItemFromChild(e.getTarget());
18213 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18218 onDblClick : function(e){
18219 var item = this.findItemFromChild(e.getTarget());
18221 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18225 onItemClick : function(item, index, e)
18227 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18230 if (this.toggleSelect) {
18231 var m = this.isSelected(item) ? 'unselect' : 'select';
18234 _t[m](item, true, false);
18237 if(this.multiSelect || this.singleSelect){
18238 if(this.multiSelect && e.shiftKey && this.lastSelection){
18239 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18241 this.select(item, this.multiSelect && e.ctrlKey);
18242 this.lastSelection = item;
18245 if(!this.tickable){
18246 e.preventDefault();
18254 * Get the number of selected nodes.
18257 getSelectionCount : function(){
18258 return this.selections.length;
18262 * Get the currently selected nodes.
18263 * @return {Array} An array of HTMLElements
18265 getSelectedNodes : function(){
18266 return this.selections;
18270 * Get the indexes of the selected nodes.
18273 getSelectedIndexes : function(){
18274 var indexes = [], s = this.selections;
18275 for(var i = 0, len = s.length; i < len; i++){
18276 indexes.push(s[i].nodeIndex);
18282 * Clear all selections
18283 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18285 clearSelections : function(suppressEvent){
18286 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18287 this.cmp.elements = this.selections;
18288 this.cmp.removeClass(this.selectedClass);
18289 this.selections = [];
18290 if(!suppressEvent){
18291 this.fireEvent("selectionchange", this, this.selections);
18297 * Returns true if the passed node is selected
18298 * @param {HTMLElement/Number} node The node or node index
18299 * @return {Boolean}
18301 isSelected : function(node){
18302 var s = this.selections;
18306 node = this.getNode(node);
18307 return s.indexOf(node) !== -1;
18312 * @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
18313 * @param {Boolean} keepExisting (optional) true to keep existing selections
18314 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18316 select : function(nodeInfo, keepExisting, suppressEvent){
18317 if(nodeInfo instanceof Array){
18319 this.clearSelections(true);
18321 for(var i = 0, len = nodeInfo.length; i < len; i++){
18322 this.select(nodeInfo[i], true, true);
18326 var node = this.getNode(nodeInfo);
18327 if(!node || this.isSelected(node)){
18328 return; // already selected.
18331 this.clearSelections(true);
18334 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18335 Roo.fly(node).addClass(this.selectedClass);
18336 this.selections.push(node);
18337 if(!suppressEvent){
18338 this.fireEvent("selectionchange", this, this.selections);
18346 * @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
18347 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18348 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18350 unselect : function(nodeInfo, keepExisting, suppressEvent)
18352 if(nodeInfo instanceof Array){
18353 Roo.each(this.selections, function(s) {
18354 this.unselect(s, nodeInfo);
18358 var node = this.getNode(nodeInfo);
18359 if(!node || !this.isSelected(node)){
18360 //Roo.log("not selected");
18361 return; // not selected.
18365 Roo.each(this.selections, function(s) {
18367 Roo.fly(node).removeClass(this.selectedClass);
18374 this.selections= ns;
18375 this.fireEvent("selectionchange", this, this.selections);
18379 * Gets a template node.
18380 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18381 * @return {HTMLElement} The node or null if it wasn't found
18383 getNode : function(nodeInfo){
18384 if(typeof nodeInfo == "string"){
18385 return document.getElementById(nodeInfo);
18386 }else if(typeof nodeInfo == "number"){
18387 return this.nodes[nodeInfo];
18393 * Gets a range template nodes.
18394 * @param {Number} startIndex
18395 * @param {Number} endIndex
18396 * @return {Array} An array of nodes
18398 getNodes : function(start, end){
18399 var ns = this.nodes;
18400 start = start || 0;
18401 end = typeof end == "undefined" ? ns.length - 1 : end;
18404 for(var i = start; i <= end; i++){
18408 for(var i = start; i >= end; i--){
18416 * Finds the index of the passed node
18417 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18418 * @return {Number} The index of the node or -1
18420 indexOf : function(node){
18421 node = this.getNode(node);
18422 if(typeof node.nodeIndex == "number"){
18423 return node.nodeIndex;
18425 var ns = this.nodes;
18426 for(var i = 0, len = ns.length; i < len; i++){
18437 * based on jquery fullcalendar
18441 Roo.bootstrap = Roo.bootstrap || {};
18443 * @class Roo.bootstrap.Calendar
18444 * @extends Roo.bootstrap.Component
18445 * Bootstrap Calendar class
18446 * @cfg {Boolean} loadMask (true|false) default false
18447 * @cfg {Object} header generate the user specific header of the calendar, default false
18450 * Create a new Container
18451 * @param {Object} config The config object
18456 Roo.bootstrap.Calendar = function(config){
18457 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18461 * Fires when a date is selected
18462 * @param {DatePicker} this
18463 * @param {Date} date The selected date
18467 * @event monthchange
18468 * Fires when the displayed month changes
18469 * @param {DatePicker} this
18470 * @param {Date} date The selected month
18472 'monthchange': true,
18474 * @event evententer
18475 * Fires when mouse over an event
18476 * @param {Calendar} this
18477 * @param {event} Event
18479 'evententer': true,
18481 * @event eventleave
18482 * Fires when the mouse leaves an
18483 * @param {Calendar} this
18486 'eventleave': true,
18488 * @event eventclick
18489 * Fires when the mouse click an
18490 * @param {Calendar} this
18499 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18502 * @cfg {Number} startDay
18503 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18511 getAutoCreate : function(){
18514 var fc_button = function(name, corner, style, content ) {
18515 return Roo.apply({},{
18517 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18519 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18522 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18533 style : 'width:100%',
18540 cls : 'fc-header-left',
18542 fc_button('prev', 'left', 'arrow', '‹' ),
18543 fc_button('next', 'right', 'arrow', '›' ),
18544 { tag: 'span', cls: 'fc-header-space' },
18545 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18553 cls : 'fc-header-center',
18557 cls: 'fc-header-title',
18560 html : 'month / year'
18568 cls : 'fc-header-right',
18570 /* fc_button('month', 'left', '', 'month' ),
18571 fc_button('week', '', '', 'week' ),
18572 fc_button('day', 'right', '', 'day' )
18584 header = this.header;
18587 var cal_heads = function() {
18589 // fixme - handle this.
18591 for (var i =0; i < Date.dayNames.length; i++) {
18592 var d = Date.dayNames[i];
18595 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18596 html : d.substring(0,3)
18600 ret[0].cls += ' fc-first';
18601 ret[6].cls += ' fc-last';
18604 var cal_cell = function(n) {
18607 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18612 cls: 'fc-day-number',
18616 cls: 'fc-day-content',
18620 style: 'position: relative;' // height: 17px;
18632 var cal_rows = function() {
18635 for (var r = 0; r < 6; r++) {
18642 for (var i =0; i < Date.dayNames.length; i++) {
18643 var d = Date.dayNames[i];
18644 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18647 row.cn[0].cls+=' fc-first';
18648 row.cn[0].cn[0].style = 'min-height:90px';
18649 row.cn[6].cls+=' fc-last';
18653 ret[0].cls += ' fc-first';
18654 ret[4].cls += ' fc-prev-last';
18655 ret[5].cls += ' fc-last';
18662 cls: 'fc-border-separate',
18663 style : 'width:100%',
18671 cls : 'fc-first fc-last',
18689 cls : 'fc-content',
18690 style : "position: relative;",
18693 cls : 'fc-view fc-view-month fc-grid',
18694 style : 'position: relative',
18695 unselectable : 'on',
18698 cls : 'fc-event-container',
18699 style : 'position:absolute;z-index:8;top:0;left:0;'
18717 initEvents : function()
18720 throw "can not find store for calendar";
18726 style: "text-align:center",
18730 style: "background-color:white;width:50%;margin:250 auto",
18734 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18745 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18747 var size = this.el.select('.fc-content', true).first().getSize();
18748 this.maskEl.setSize(size.width, size.height);
18749 this.maskEl.enableDisplayMode("block");
18750 if(!this.loadMask){
18751 this.maskEl.hide();
18754 this.store = Roo.factory(this.store, Roo.data);
18755 this.store.on('load', this.onLoad, this);
18756 this.store.on('beforeload', this.onBeforeLoad, this);
18760 this.cells = this.el.select('.fc-day',true);
18761 //Roo.log(this.cells);
18762 this.textNodes = this.el.query('.fc-day-number');
18763 this.cells.addClassOnOver('fc-state-hover');
18765 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18766 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18767 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18768 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18770 this.on('monthchange', this.onMonthChange, this);
18772 this.update(new Date().clearTime());
18775 resize : function() {
18776 var sz = this.el.getSize();
18778 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18779 this.el.select('.fc-day-content div',true).setHeight(34);
18784 showPrevMonth : function(e){
18785 this.update(this.activeDate.add("mo", -1));
18787 showToday : function(e){
18788 this.update(new Date().clearTime());
18791 showNextMonth : function(e){
18792 this.update(this.activeDate.add("mo", 1));
18796 showPrevYear : function(){
18797 this.update(this.activeDate.add("y", -1));
18801 showNextYear : function(){
18802 this.update(this.activeDate.add("y", 1));
18807 update : function(date)
18809 var vd = this.activeDate;
18810 this.activeDate = date;
18811 // if(vd && this.el){
18812 // var t = date.getTime();
18813 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18814 // Roo.log('using add remove');
18816 // this.fireEvent('monthchange', this, date);
18818 // this.cells.removeClass("fc-state-highlight");
18819 // this.cells.each(function(c){
18820 // if(c.dateValue == t){
18821 // c.addClass("fc-state-highlight");
18822 // setTimeout(function(){
18823 // try{c.dom.firstChild.focus();}catch(e){}
18833 var days = date.getDaysInMonth();
18835 var firstOfMonth = date.getFirstDateOfMonth();
18836 var startingPos = firstOfMonth.getDay()-this.startDay;
18838 if(startingPos < this.startDay){
18842 var pm = date.add(Date.MONTH, -1);
18843 var prevStart = pm.getDaysInMonth()-startingPos;
18845 this.cells = this.el.select('.fc-day',true);
18846 this.textNodes = this.el.query('.fc-day-number');
18847 this.cells.addClassOnOver('fc-state-hover');
18849 var cells = this.cells.elements;
18850 var textEls = this.textNodes;
18852 Roo.each(cells, function(cell){
18853 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18856 days += startingPos;
18858 // convert everything to numbers so it's fast
18859 var day = 86400000;
18860 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18863 //Roo.log(prevStart);
18865 var today = new Date().clearTime().getTime();
18866 var sel = date.clearTime().getTime();
18867 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18868 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18869 var ddMatch = this.disabledDatesRE;
18870 var ddText = this.disabledDatesText;
18871 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18872 var ddaysText = this.disabledDaysText;
18873 var format = this.format;
18875 var setCellClass = function(cal, cell){
18879 //Roo.log('set Cell Class');
18881 var t = d.getTime();
18885 cell.dateValue = t;
18887 cell.className += " fc-today";
18888 cell.className += " fc-state-highlight";
18889 cell.title = cal.todayText;
18892 // disable highlight in other month..
18893 //cell.className += " fc-state-highlight";
18898 cell.className = " fc-state-disabled";
18899 cell.title = cal.minText;
18903 cell.className = " fc-state-disabled";
18904 cell.title = cal.maxText;
18908 if(ddays.indexOf(d.getDay()) != -1){
18909 cell.title = ddaysText;
18910 cell.className = " fc-state-disabled";
18913 if(ddMatch && format){
18914 var fvalue = d.dateFormat(format);
18915 if(ddMatch.test(fvalue)){
18916 cell.title = ddText.replace("%0", fvalue);
18917 cell.className = " fc-state-disabled";
18921 if (!cell.initialClassName) {
18922 cell.initialClassName = cell.dom.className;
18925 cell.dom.className = cell.initialClassName + ' ' + cell.className;
18930 for(; i < startingPos; i++) {
18931 textEls[i].innerHTML = (++prevStart);
18932 d.setDate(d.getDate()+1);
18934 cells[i].className = "fc-past fc-other-month";
18935 setCellClass(this, cells[i]);
18940 for(; i < days; i++){
18941 intDay = i - startingPos + 1;
18942 textEls[i].innerHTML = (intDay);
18943 d.setDate(d.getDate()+1);
18945 cells[i].className = ''; // "x-date-active";
18946 setCellClass(this, cells[i]);
18950 for(; i < 42; i++) {
18951 textEls[i].innerHTML = (++extraDays);
18952 d.setDate(d.getDate()+1);
18954 cells[i].className = "fc-future fc-other-month";
18955 setCellClass(this, cells[i]);
18958 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18960 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18962 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18963 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18965 if(totalRows != 6){
18966 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18967 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18970 this.fireEvent('monthchange', this, date);
18974 if(!this.internalRender){
18975 var main = this.el.dom.firstChild;
18976 var w = main.offsetWidth;
18977 this.el.setWidth(w + this.el.getBorderWidth("lr"));
18978 Roo.fly(main).setWidth(w);
18979 this.internalRender = true;
18980 // opera does not respect the auto grow header center column
18981 // then, after it gets a width opera refuses to recalculate
18982 // without a second pass
18983 if(Roo.isOpera && !this.secondPass){
18984 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18985 this.secondPass = true;
18986 this.update.defer(10, this, [date]);
18993 findCell : function(dt) {
18994 dt = dt.clearTime().getTime();
18996 this.cells.each(function(c){
18997 //Roo.log("check " +c.dateValue + '?=' + dt);
18998 if(c.dateValue == dt){
19008 findCells : function(ev) {
19009 var s = ev.start.clone().clearTime().getTime();
19011 var e= ev.end.clone().clearTime().getTime();
19014 this.cells.each(function(c){
19015 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19017 if(c.dateValue > e){
19020 if(c.dateValue < s){
19029 // findBestRow: function(cells)
19033 // for (var i =0 ; i < cells.length;i++) {
19034 // ret = Math.max(cells[i].rows || 0,ret);
19041 addItem : function(ev)
19043 // look for vertical location slot in
19044 var cells = this.findCells(ev);
19046 // ev.row = this.findBestRow(cells);
19048 // work out the location.
19052 for(var i =0; i < cells.length; i++) {
19054 cells[i].row = cells[0].row;
19057 cells[i].row = cells[i].row + 1;
19067 if (crow.start.getY() == cells[i].getY()) {
19069 crow.end = cells[i];
19086 cells[0].events.push(ev);
19088 this.calevents.push(ev);
19091 clearEvents: function() {
19093 if(!this.calevents){
19097 Roo.each(this.cells.elements, function(c){
19103 Roo.each(this.calevents, function(e) {
19104 Roo.each(e.els, function(el) {
19105 el.un('mouseenter' ,this.onEventEnter, this);
19106 el.un('mouseleave' ,this.onEventLeave, this);
19111 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19117 renderEvents: function()
19121 this.cells.each(function(c) {
19130 if(c.row != c.events.length){
19131 r = 4 - (4 - (c.row - c.events.length));
19134 c.events = ev.slice(0, r);
19135 c.more = ev.slice(r);
19137 if(c.more.length && c.more.length == 1){
19138 c.events.push(c.more.pop());
19141 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19145 this.cells.each(function(c) {
19147 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19150 for (var e = 0; e < c.events.length; e++){
19151 var ev = c.events[e];
19152 var rows = ev.rows;
19154 for(var i = 0; i < rows.length; i++) {
19156 // how many rows should it span..
19159 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19160 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19162 unselectable : "on",
19165 cls: 'fc-event-inner',
19169 // cls: 'fc-event-time',
19170 // html : cells.length > 1 ? '' : ev.time
19174 cls: 'fc-event-title',
19175 html : String.format('{0}', ev.title)
19182 cls: 'ui-resizable-handle ui-resizable-e',
19183 html : '  '
19190 cfg.cls += ' fc-event-start';
19192 if ((i+1) == rows.length) {
19193 cfg.cls += ' fc-event-end';
19196 var ctr = _this.el.select('.fc-event-container',true).first();
19197 var cg = ctr.createChild(cfg);
19199 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19200 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19202 var r = (c.more.length) ? 1 : 0;
19203 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19204 cg.setWidth(ebox.right - sbox.x -2);
19206 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19207 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19208 cg.on('click', _this.onEventClick, _this, ev);
19219 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19220 style : 'position: absolute',
19221 unselectable : "on",
19224 cls: 'fc-event-inner',
19228 cls: 'fc-event-title',
19236 cls: 'ui-resizable-handle ui-resizable-e',
19237 html : '  '
19243 var ctr = _this.el.select('.fc-event-container',true).first();
19244 var cg = ctr.createChild(cfg);
19246 var sbox = c.select('.fc-day-content',true).first().getBox();
19247 var ebox = c.select('.fc-day-content',true).first().getBox();
19249 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19250 cg.setWidth(ebox.right - sbox.x -2);
19252 cg.on('click', _this.onMoreEventClick, _this, c.more);
19262 onEventEnter: function (e, el,event,d) {
19263 this.fireEvent('evententer', this, el, event);
19266 onEventLeave: function (e, el,event,d) {
19267 this.fireEvent('eventleave', this, el, event);
19270 onEventClick: function (e, el,event,d) {
19271 this.fireEvent('eventclick', this, el, event);
19274 onMonthChange: function () {
19278 onMoreEventClick: function(e, el, more)
19282 this.calpopover.placement = 'right';
19283 this.calpopover.setTitle('More');
19285 this.calpopover.setContent('');
19287 var ctr = this.calpopover.el.select('.popover-content', true).first();
19289 Roo.each(more, function(m){
19291 cls : 'fc-event-hori fc-event-draggable',
19294 var cg = ctr.createChild(cfg);
19296 cg.on('click', _this.onEventClick, _this, m);
19299 this.calpopover.show(el);
19304 onLoad: function ()
19306 this.calevents = [];
19309 if(this.store.getCount() > 0){
19310 this.store.data.each(function(d){
19313 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19314 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19315 time : d.data.start_time,
19316 title : d.data.title,
19317 description : d.data.description,
19318 venue : d.data.venue
19323 this.renderEvents();
19325 if(this.calevents.length && this.loadMask){
19326 this.maskEl.hide();
19330 onBeforeLoad: function()
19332 this.clearEvents();
19334 this.maskEl.show();
19348 * @class Roo.bootstrap.Popover
19349 * @extends Roo.bootstrap.Component
19350 * Bootstrap Popover class
19351 * @cfg {String} html contents of the popover (or false to use children..)
19352 * @cfg {String} title of popover (or false to hide)
19353 * @cfg {String} placement how it is placed
19354 * @cfg {String} trigger click || hover (or false to trigger manually)
19355 * @cfg {String} over what (parent or false to trigger manually.)
19356 * @cfg {Number} delay - delay before showing
19359 * Create a new Popover
19360 * @param {Object} config The config object
19363 Roo.bootstrap.Popover = function(config){
19364 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19370 * After the popover show
19372 * @param {Roo.bootstrap.Popover} this
19377 * After the popover hide
19379 * @param {Roo.bootstrap.Popover} this
19385 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19387 title: 'Fill in a title',
19390 placement : 'right',
19391 trigger : 'hover', // hover
19397 can_build_overlaid : false,
19399 getChildContainer : function()
19401 return this.el.select('.popover-content',true).first();
19404 getAutoCreate : function(){
19407 cls : 'popover roo-dynamic',
19408 style: 'display:block',
19414 cls : 'popover-inner',
19418 cls: 'popover-title popover-header',
19422 cls : 'popover-content popover-body',
19433 setTitle: function(str)
19436 this.el.select('.popover-title',true).first().dom.innerHTML = str;
19438 setContent: function(str)
19441 this.el.select('.popover-content',true).first().dom.innerHTML = str;
19443 // as it get's added to the bottom of the page.
19444 onRender : function(ct, position)
19446 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19448 var cfg = Roo.apply({}, this.getAutoCreate());
19452 cfg.cls += ' ' + this.cls;
19455 cfg.style = this.style;
19457 //Roo.log("adding to ");
19458 this.el = Roo.get(document.body).createChild(cfg, position);
19459 // Roo.log(this.el);
19464 initEvents : function()
19466 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19467 this.el.enableDisplayMode('block');
19469 if (this.over === false) {
19472 if (this.triggers === false) {
19475 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19476 var triggers = this.trigger ? this.trigger.split(' ') : [];
19477 Roo.each(triggers, function(trigger) {
19479 if (trigger == 'click') {
19480 on_el.on('click', this.toggle, this);
19481 } else if (trigger != 'manual') {
19482 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19483 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19485 on_el.on(eventIn ,this.enter, this);
19486 on_el.on(eventOut, this.leave, this);
19497 toggle : function () {
19498 this.hoverState == 'in' ? this.leave() : this.enter();
19501 enter : function () {
19503 clearTimeout(this.timeout);
19505 this.hoverState = 'in';
19507 if (!this.delay || !this.delay.show) {
19512 this.timeout = setTimeout(function () {
19513 if (_t.hoverState == 'in') {
19516 }, this.delay.show)
19519 leave : function() {
19520 clearTimeout(this.timeout);
19522 this.hoverState = 'out';
19524 if (!this.delay || !this.delay.hide) {
19529 this.timeout = setTimeout(function () {
19530 if (_t.hoverState == 'out') {
19533 }, this.delay.hide)
19536 show : function (on_el)
19539 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19543 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19544 if (this.html !== false) {
19545 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19547 this.el.removeClass([
19548 'fade','top','bottom', 'left', 'right','in',
19549 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19551 if (!this.title.length) {
19552 this.el.select('.popover-title',true).hide();
19555 var placement = typeof this.placement == 'function' ?
19556 this.placement.call(this, this.el, on_el) :
19559 var autoToken = /\s?auto?\s?/i;
19560 var autoPlace = autoToken.test(placement);
19562 placement = placement.replace(autoToken, '') || 'top';
19566 //this.el.setXY([0,0]);
19568 this.el.dom.style.display='block';
19569 this.el.addClass(placement);
19571 //this.el.appendTo(on_el);
19573 var p = this.getPosition();
19574 var box = this.el.getBox();
19579 var align = Roo.bootstrap.Popover.alignment[placement];
19582 this.el.alignTo(on_el, align[0],align[1]);
19583 //var arrow = this.el.select('.arrow',true).first();
19584 //arrow.set(align[2],
19586 this.el.addClass('in');
19589 if (this.el.hasClass('fade')) {
19593 this.hoverState = 'in';
19595 this.fireEvent('show', this);
19600 this.el.setXY([0,0]);
19601 this.el.removeClass('in');
19603 this.hoverState = null;
19605 this.fireEvent('hide', this);
19610 Roo.bootstrap.Popover.alignment = {
19611 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19612 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19613 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19614 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19625 * @class Roo.bootstrap.Progress
19626 * @extends Roo.bootstrap.Component
19627 * Bootstrap Progress class
19628 * @cfg {Boolean} striped striped of the progress bar
19629 * @cfg {Boolean} active animated of the progress bar
19633 * Create a new Progress
19634 * @param {Object} config The config object
19637 Roo.bootstrap.Progress = function(config){
19638 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19641 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19646 getAutoCreate : function(){
19654 cfg.cls += ' progress-striped';
19658 cfg.cls += ' active';
19677 * @class Roo.bootstrap.ProgressBar
19678 * @extends Roo.bootstrap.Component
19679 * Bootstrap ProgressBar class
19680 * @cfg {Number} aria_valuenow aria-value now
19681 * @cfg {Number} aria_valuemin aria-value min
19682 * @cfg {Number} aria_valuemax aria-value max
19683 * @cfg {String} label label for the progress bar
19684 * @cfg {String} panel (success | info | warning | danger )
19685 * @cfg {String} role role of the progress bar
19686 * @cfg {String} sr_only text
19690 * Create a new ProgressBar
19691 * @param {Object} config The config object
19694 Roo.bootstrap.ProgressBar = function(config){
19695 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19698 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19702 aria_valuemax : 100,
19708 getAutoCreate : function()
19713 cls: 'progress-bar',
19714 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19726 cfg.role = this.role;
19729 if(this.aria_valuenow){
19730 cfg['aria-valuenow'] = this.aria_valuenow;
19733 if(this.aria_valuemin){
19734 cfg['aria-valuemin'] = this.aria_valuemin;
19737 if(this.aria_valuemax){
19738 cfg['aria-valuemax'] = this.aria_valuemax;
19741 if(this.label && !this.sr_only){
19742 cfg.html = this.label;
19746 cfg.cls += ' progress-bar-' + this.panel;
19752 update : function(aria_valuenow)
19754 this.aria_valuenow = aria_valuenow;
19756 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19771 * @class Roo.bootstrap.TabGroup
19772 * @extends Roo.bootstrap.Column
19773 * Bootstrap Column class
19774 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19775 * @cfg {Boolean} carousel true to make the group behave like a carousel
19776 * @cfg {Boolean} bullets show bullets for the panels
19777 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19778 * @cfg {Number} timer auto slide timer .. default 0 millisecond
19779 * @cfg {Boolean} showarrow (true|false) show arrow default true
19782 * Create a new TabGroup
19783 * @param {Object} config The config object
19786 Roo.bootstrap.TabGroup = function(config){
19787 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19789 this.navId = Roo.id();
19792 Roo.bootstrap.TabGroup.register(this);
19796 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
19799 transition : false,
19804 slideOnTouch : false,
19807 getAutoCreate : function()
19809 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19811 cfg.cls += ' tab-content';
19813 if (this.carousel) {
19814 cfg.cls += ' carousel slide';
19817 cls : 'carousel-inner',
19821 if(this.bullets && !Roo.isTouch){
19824 cls : 'carousel-bullets',
19828 if(this.bullets_cls){
19829 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19836 cfg.cn[0].cn.push(bullets);
19839 if(this.showarrow){
19840 cfg.cn[0].cn.push({
19842 class : 'carousel-arrow',
19846 class : 'carousel-prev',
19850 class : 'fa fa-chevron-left'
19856 class : 'carousel-next',
19860 class : 'fa fa-chevron-right'
19873 initEvents: function()
19875 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19876 // this.el.on("touchstart", this.onTouchStart, this);
19879 if(this.autoslide){
19882 this.slideFn = window.setInterval(function() {
19883 _this.showPanelNext();
19887 if(this.showarrow){
19888 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19889 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19895 // onTouchStart : function(e, el, o)
19897 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19901 // this.showPanelNext();
19905 getChildContainer : function()
19907 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19911 * register a Navigation item
19912 * @param {Roo.bootstrap.NavItem} the navitem to add
19914 register : function(item)
19916 this.tabs.push( item);
19917 item.navId = this.navId; // not really needed..
19922 getActivePanel : function()
19925 Roo.each(this.tabs, function(t) {
19935 getPanelByName : function(n)
19938 Roo.each(this.tabs, function(t) {
19939 if (t.tabId == n) {
19947 indexOfPanel : function(p)
19950 Roo.each(this.tabs, function(t,i) {
19951 if (t.tabId == p.tabId) {
19960 * show a specific panel
19961 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19962 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19964 showPanel : function (pan)
19966 if(this.transition || typeof(pan) == 'undefined'){
19967 Roo.log("waiting for the transitionend");
19971 if (typeof(pan) == 'number') {
19972 pan = this.tabs[pan];
19975 if (typeof(pan) == 'string') {
19976 pan = this.getPanelByName(pan);
19979 var cur = this.getActivePanel();
19982 Roo.log('pan or acitve pan is undefined');
19986 if (pan.tabId == this.getActivePanel().tabId) {
19990 if (false === cur.fireEvent('beforedeactivate')) {
19994 if(this.bullets > 0 && !Roo.isTouch){
19995 this.setActiveBullet(this.indexOfPanel(pan));
19998 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20000 //class="carousel-item carousel-item-next carousel-item-left"
20002 this.transition = true;
20003 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20004 var lr = dir == 'next' ? 'left' : 'right';
20005 pan.el.addClass(dir); // or prev
20006 pan.el.addClass('carousel-item-' + dir); // or prev
20007 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20008 cur.el.addClass(lr); // or right
20009 pan.el.addClass(lr);
20010 cur.el.addClass('carousel-item-' +lr); // or right
20011 pan.el.addClass('carousel-item-' +lr);
20015 cur.el.on('transitionend', function() {
20016 Roo.log("trans end?");
20018 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20019 pan.setActive(true);
20021 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20022 cur.setActive(false);
20024 _this.transition = false;
20026 }, this, { single: true } );
20031 cur.setActive(false);
20032 pan.setActive(true);
20037 showPanelNext : function()
20039 var i = this.indexOfPanel(this.getActivePanel());
20041 if (i >= this.tabs.length - 1 && !this.autoslide) {
20045 if (i >= this.tabs.length - 1 && this.autoslide) {
20049 this.showPanel(this.tabs[i+1]);
20052 showPanelPrev : function()
20054 var i = this.indexOfPanel(this.getActivePanel());
20056 if (i < 1 && !this.autoslide) {
20060 if (i < 1 && this.autoslide) {
20061 i = this.tabs.length;
20064 this.showPanel(this.tabs[i-1]);
20068 addBullet: function()
20070 if(!this.bullets || Roo.isTouch){
20073 var ctr = this.el.select('.carousel-bullets',true).first();
20074 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20075 var bullet = ctr.createChild({
20076 cls : 'bullet bullet-' + i
20077 },ctr.dom.lastChild);
20082 bullet.on('click', (function(e, el, o, ii, t){
20084 e.preventDefault();
20086 this.showPanel(ii);
20088 if(this.autoslide && this.slideFn){
20089 clearInterval(this.slideFn);
20090 this.slideFn = window.setInterval(function() {
20091 _this.showPanelNext();
20095 }).createDelegate(this, [i, bullet], true));
20100 setActiveBullet : function(i)
20106 Roo.each(this.el.select('.bullet', true).elements, function(el){
20107 el.removeClass('selected');
20110 var bullet = this.el.select('.bullet-' + i, true).first();
20116 bullet.addClass('selected');
20127 Roo.apply(Roo.bootstrap.TabGroup, {
20131 * register a Navigation Group
20132 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20134 register : function(navgrp)
20136 this.groups[navgrp.navId] = navgrp;
20140 * fetch a Navigation Group based on the navigation ID
20141 * if one does not exist , it will get created.
20142 * @param {string} the navgroup to add
20143 * @returns {Roo.bootstrap.NavGroup} the navgroup
20145 get: function(navId) {
20146 if (typeof(this.groups[navId]) == 'undefined') {
20147 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20149 return this.groups[navId] ;
20164 * @class Roo.bootstrap.TabPanel
20165 * @extends Roo.bootstrap.Component
20166 * Bootstrap TabPanel class
20167 * @cfg {Boolean} active panel active
20168 * @cfg {String} html panel content
20169 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20170 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20171 * @cfg {String} href click to link..
20175 * Create a new TabPanel
20176 * @param {Object} config The config object
20179 Roo.bootstrap.TabPanel = function(config){
20180 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20184 * Fires when the active status changes
20185 * @param {Roo.bootstrap.TabPanel} this
20186 * @param {Boolean} state the new state
20191 * @event beforedeactivate
20192 * Fires before a tab is de-activated - can be used to do validation on a form.
20193 * @param {Roo.bootstrap.TabPanel} this
20194 * @return {Boolean} false if there is an error
20197 'beforedeactivate': true
20200 this.tabId = this.tabId || Roo.id();
20204 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20212 getAutoCreate : function(){
20217 // item is needed for carousel - not sure if it has any effect otherwise
20218 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20219 html: this.html || ''
20223 cfg.cls += ' active';
20227 cfg.tabId = this.tabId;
20235 initEvents: function()
20237 var p = this.parent();
20239 this.navId = this.navId || p.navId;
20241 if (typeof(this.navId) != 'undefined') {
20242 // not really needed.. but just in case.. parent should be a NavGroup.
20243 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20247 var i = tg.tabs.length - 1;
20249 if(this.active && tg.bullets > 0 && i < tg.bullets){
20250 tg.setActiveBullet(i);
20254 this.el.on('click', this.onClick, this);
20257 this.el.on("touchstart", this.onTouchStart, this);
20258 this.el.on("touchmove", this.onTouchMove, this);
20259 this.el.on("touchend", this.onTouchEnd, this);
20264 onRender : function(ct, position)
20266 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20269 setActive : function(state)
20271 Roo.log("panel - set active " + this.tabId + "=" + state);
20273 this.active = state;
20275 this.el.removeClass('active');
20277 } else if (!this.el.hasClass('active')) {
20278 this.el.addClass('active');
20281 this.fireEvent('changed', this, state);
20284 onClick : function(e)
20286 e.preventDefault();
20288 if(!this.href.length){
20292 window.location.href = this.href;
20301 onTouchStart : function(e)
20303 this.swiping = false;
20305 this.startX = e.browserEvent.touches[0].clientX;
20306 this.startY = e.browserEvent.touches[0].clientY;
20309 onTouchMove : function(e)
20311 this.swiping = true;
20313 this.endX = e.browserEvent.touches[0].clientX;
20314 this.endY = e.browserEvent.touches[0].clientY;
20317 onTouchEnd : function(e)
20324 var tabGroup = this.parent();
20326 if(this.endX > this.startX){ // swiping right
20327 tabGroup.showPanelPrev();
20331 if(this.startX > this.endX){ // swiping left
20332 tabGroup.showPanelNext();
20351 * @class Roo.bootstrap.DateField
20352 * @extends Roo.bootstrap.Input
20353 * Bootstrap DateField class
20354 * @cfg {Number} weekStart default 0
20355 * @cfg {String} viewMode default empty, (months|years)
20356 * @cfg {String} minViewMode default empty, (months|years)
20357 * @cfg {Number} startDate default -Infinity
20358 * @cfg {Number} endDate default Infinity
20359 * @cfg {Boolean} todayHighlight default false
20360 * @cfg {Boolean} todayBtn default false
20361 * @cfg {Boolean} calendarWeeks default false
20362 * @cfg {Object} daysOfWeekDisabled default empty
20363 * @cfg {Boolean} singleMode default false (true | false)
20365 * @cfg {Boolean} keyboardNavigation default true
20366 * @cfg {String} language default en
20369 * Create a new DateField
20370 * @param {Object} config The config object
20373 Roo.bootstrap.DateField = function(config){
20374 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20378 * Fires when this field show.
20379 * @param {Roo.bootstrap.DateField} this
20380 * @param {Mixed} date The date value
20385 * Fires when this field hide.
20386 * @param {Roo.bootstrap.DateField} this
20387 * @param {Mixed} date The date value
20392 * Fires when select a date.
20393 * @param {Roo.bootstrap.DateField} this
20394 * @param {Mixed} date The date value
20398 * @event beforeselect
20399 * Fires when before select a date.
20400 * @param {Roo.bootstrap.DateField} this
20401 * @param {Mixed} date The date value
20403 beforeselect : true
20407 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20410 * @cfg {String} format
20411 * The default date format string which can be overriden for localization support. The format must be
20412 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20416 * @cfg {String} altFormats
20417 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20418 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20420 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20428 todayHighlight : false,
20434 keyboardNavigation: true,
20436 calendarWeeks: false,
20438 startDate: -Infinity,
20442 daysOfWeekDisabled: [],
20446 singleMode : false,
20448 UTCDate: function()
20450 return new Date(Date.UTC.apply(Date, arguments));
20453 UTCToday: function()
20455 var today = new Date();
20456 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20459 getDate: function() {
20460 var d = this.getUTCDate();
20461 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20464 getUTCDate: function() {
20468 setDate: function(d) {
20469 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20472 setUTCDate: function(d) {
20474 this.setValue(this.formatDate(this.date));
20477 onRender: function(ct, position)
20480 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20482 this.language = this.language || 'en';
20483 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20484 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20486 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20487 this.format = this.format || 'm/d/y';
20488 this.isInline = false;
20489 this.isInput = true;
20490 this.component = this.el.select('.add-on', true).first() || false;
20491 this.component = (this.component && this.component.length === 0) ? false : this.component;
20492 this.hasInput = this.component && this.inputEl().length;
20494 if (typeof(this.minViewMode === 'string')) {
20495 switch (this.minViewMode) {
20497 this.minViewMode = 1;
20500 this.minViewMode = 2;
20503 this.minViewMode = 0;
20508 if (typeof(this.viewMode === 'string')) {
20509 switch (this.viewMode) {
20522 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20524 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20526 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20528 this.picker().on('mousedown', this.onMousedown, this);
20529 this.picker().on('click', this.onClick, this);
20531 this.picker().addClass('datepicker-dropdown');
20533 this.startViewMode = this.viewMode;
20535 if(this.singleMode){
20536 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20537 v.setVisibilityMode(Roo.Element.DISPLAY);
20541 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20542 v.setStyle('width', '189px');
20546 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20547 if(!this.calendarWeeks){
20552 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20553 v.attr('colspan', function(i, val){
20554 return parseInt(val) + 1;
20559 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20561 this.setStartDate(this.startDate);
20562 this.setEndDate(this.endDate);
20564 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20571 if(this.isInline) {
20576 picker : function()
20578 return this.pickerEl;
20579 // return this.el.select('.datepicker', true).first();
20582 fillDow: function()
20584 var dowCnt = this.weekStart;
20593 if(this.calendarWeeks){
20601 while (dowCnt < this.weekStart + 7) {
20605 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20609 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20612 fillMonths: function()
20615 var months = this.picker().select('>.datepicker-months td', true).first();
20617 months.dom.innerHTML = '';
20623 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20626 months.createChild(month);
20633 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;
20635 if (this.date < this.startDate) {
20636 this.viewDate = new Date(this.startDate);
20637 } else if (this.date > this.endDate) {
20638 this.viewDate = new Date(this.endDate);
20640 this.viewDate = new Date(this.date);
20648 var d = new Date(this.viewDate),
20649 year = d.getUTCFullYear(),
20650 month = d.getUTCMonth(),
20651 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20652 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20653 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20654 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20655 currentDate = this.date && this.date.valueOf(),
20656 today = this.UTCToday();
20658 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20660 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20662 // this.picker.select('>tfoot th.today').
20663 // .text(dates[this.language].today)
20664 // .toggle(this.todayBtn !== false);
20666 this.updateNavArrows();
20669 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20671 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20673 prevMonth.setUTCDate(day);
20675 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20677 var nextMonth = new Date(prevMonth);
20679 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20681 nextMonth = nextMonth.valueOf();
20683 var fillMonths = false;
20685 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20687 while(prevMonth.valueOf() <= nextMonth) {
20690 if (prevMonth.getUTCDay() === this.weekStart) {
20692 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20700 if(this.calendarWeeks){
20701 // ISO 8601: First week contains first thursday.
20702 // ISO also states week starts on Monday, but we can be more abstract here.
20704 // Start of current week: based on weekstart/current date
20705 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20706 // Thursday of this week
20707 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20708 // First Thursday of year, year from thursday
20709 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20710 // Calendar week: ms between thursdays, div ms per day, div 7 days
20711 calWeek = (th - yth) / 864e5 / 7 + 1;
20713 fillMonths.cn.push({
20721 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20723 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20726 if (this.todayHighlight &&
20727 prevMonth.getUTCFullYear() == today.getFullYear() &&
20728 prevMonth.getUTCMonth() == today.getMonth() &&
20729 prevMonth.getUTCDate() == today.getDate()) {
20730 clsName += ' today';
20733 if (currentDate && prevMonth.valueOf() === currentDate) {
20734 clsName += ' active';
20737 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20738 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20739 clsName += ' disabled';
20742 fillMonths.cn.push({
20744 cls: 'day ' + clsName,
20745 html: prevMonth.getDate()
20748 prevMonth.setDate(prevMonth.getDate()+1);
20751 var currentYear = this.date && this.date.getUTCFullYear();
20752 var currentMonth = this.date && this.date.getUTCMonth();
20754 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20756 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20757 v.removeClass('active');
20759 if(currentYear === year && k === currentMonth){
20760 v.addClass('active');
20763 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20764 v.addClass('disabled');
20770 year = parseInt(year/10, 10) * 10;
20772 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20774 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20777 for (var i = -1; i < 11; i++) {
20778 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20780 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20788 showMode: function(dir)
20791 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20794 Roo.each(this.picker().select('>div',true).elements, function(v){
20795 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20798 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20803 if(this.isInline) {
20807 this.picker().removeClass(['bottom', 'top']);
20809 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20811 * place to the top of element!
20815 this.picker().addClass('top');
20816 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20821 this.picker().addClass('bottom');
20823 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20826 parseDate : function(value)
20828 if(!value || value instanceof Date){
20831 var v = Date.parseDate(value, this.format);
20832 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20833 v = Date.parseDate(value, 'Y-m-d');
20835 if(!v && this.altFormats){
20836 if(!this.altFormatsArray){
20837 this.altFormatsArray = this.altFormats.split("|");
20839 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20840 v = Date.parseDate(value, this.altFormatsArray[i]);
20846 formatDate : function(date, fmt)
20848 return (!date || !(date instanceof Date)) ?
20849 date : date.dateFormat(fmt || this.format);
20852 onFocus : function()
20854 Roo.bootstrap.DateField.superclass.onFocus.call(this);
20858 onBlur : function()
20860 Roo.bootstrap.DateField.superclass.onBlur.call(this);
20862 var d = this.inputEl().getValue();
20869 showPopup : function()
20871 this.picker().show();
20875 this.fireEvent('showpopup', this, this.date);
20878 hidePopup : function()
20880 if(this.isInline) {
20883 this.picker().hide();
20884 this.viewMode = this.startViewMode;
20887 this.fireEvent('hidepopup', this, this.date);
20891 onMousedown: function(e)
20893 e.stopPropagation();
20894 e.preventDefault();
20899 Roo.bootstrap.DateField.superclass.keyup.call(this);
20903 setValue: function(v)
20905 if(this.fireEvent('beforeselect', this, v) !== false){
20906 var d = new Date(this.parseDate(v) ).clearTime();
20908 if(isNaN(d.getTime())){
20909 this.date = this.viewDate = '';
20910 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20914 v = this.formatDate(d);
20916 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20918 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20922 this.fireEvent('select', this, this.date);
20926 getValue: function()
20928 return this.formatDate(this.date);
20931 fireKey: function(e)
20933 if (!this.picker().isVisible()){
20934 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20940 var dateChanged = false,
20942 newDate, newViewDate;
20947 e.preventDefault();
20951 if (!this.keyboardNavigation) {
20954 dir = e.keyCode == 37 ? -1 : 1;
20957 newDate = this.moveYear(this.date, dir);
20958 newViewDate = this.moveYear(this.viewDate, dir);
20959 } else if (e.shiftKey){
20960 newDate = this.moveMonth(this.date, dir);
20961 newViewDate = this.moveMonth(this.viewDate, dir);
20963 newDate = new Date(this.date);
20964 newDate.setUTCDate(this.date.getUTCDate() + dir);
20965 newViewDate = new Date(this.viewDate);
20966 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20968 if (this.dateWithinRange(newDate)){
20969 this.date = newDate;
20970 this.viewDate = newViewDate;
20971 this.setValue(this.formatDate(this.date));
20973 e.preventDefault();
20974 dateChanged = true;
20979 if (!this.keyboardNavigation) {
20982 dir = e.keyCode == 38 ? -1 : 1;
20984 newDate = this.moveYear(this.date, dir);
20985 newViewDate = this.moveYear(this.viewDate, dir);
20986 } else if (e.shiftKey){
20987 newDate = this.moveMonth(this.date, dir);
20988 newViewDate = this.moveMonth(this.viewDate, dir);
20990 newDate = new Date(this.date);
20991 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20992 newViewDate = new Date(this.viewDate);
20993 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20995 if (this.dateWithinRange(newDate)){
20996 this.date = newDate;
20997 this.viewDate = newViewDate;
20998 this.setValue(this.formatDate(this.date));
21000 e.preventDefault();
21001 dateChanged = true;
21005 this.setValue(this.formatDate(this.date));
21007 e.preventDefault();
21010 this.setValue(this.formatDate(this.date));
21024 onClick: function(e)
21026 e.stopPropagation();
21027 e.preventDefault();
21029 var target = e.getTarget();
21031 if(target.nodeName.toLowerCase() === 'i'){
21032 target = Roo.get(target).dom.parentNode;
21035 var nodeName = target.nodeName;
21036 var className = target.className;
21037 var html = target.innerHTML;
21038 //Roo.log(nodeName);
21040 switch(nodeName.toLowerCase()) {
21042 switch(className) {
21048 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21049 switch(this.viewMode){
21051 this.viewDate = this.moveMonth(this.viewDate, dir);
21055 this.viewDate = this.moveYear(this.viewDate, dir);
21061 var date = new Date();
21062 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21064 this.setValue(this.formatDate(this.date));
21071 if (className.indexOf('disabled') < 0) {
21072 this.viewDate.setUTCDate(1);
21073 if (className.indexOf('month') > -1) {
21074 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21076 var year = parseInt(html, 10) || 0;
21077 this.viewDate.setUTCFullYear(year);
21081 if(this.singleMode){
21082 this.setValue(this.formatDate(this.viewDate));
21093 //Roo.log(className);
21094 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21095 var day = parseInt(html, 10) || 1;
21096 var year = this.viewDate.getUTCFullYear(),
21097 month = this.viewDate.getUTCMonth();
21099 if (className.indexOf('old') > -1) {
21106 } else if (className.indexOf('new') > -1) {
21114 //Roo.log([year,month,day]);
21115 this.date = this.UTCDate(year, month, day,0,0,0,0);
21116 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21118 //Roo.log(this.formatDate(this.date));
21119 this.setValue(this.formatDate(this.date));
21126 setStartDate: function(startDate)
21128 this.startDate = startDate || -Infinity;
21129 if (this.startDate !== -Infinity) {
21130 this.startDate = this.parseDate(this.startDate);
21133 this.updateNavArrows();
21136 setEndDate: function(endDate)
21138 this.endDate = endDate || Infinity;
21139 if (this.endDate !== Infinity) {
21140 this.endDate = this.parseDate(this.endDate);
21143 this.updateNavArrows();
21146 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21148 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21149 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21150 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21152 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21153 return parseInt(d, 10);
21156 this.updateNavArrows();
21159 updateNavArrows: function()
21161 if(this.singleMode){
21165 var d = new Date(this.viewDate),
21166 year = d.getUTCFullYear(),
21167 month = d.getUTCMonth();
21169 Roo.each(this.picker().select('.prev', true).elements, function(v){
21171 switch (this.viewMode) {
21174 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21180 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21187 Roo.each(this.picker().select('.next', true).elements, function(v){
21189 switch (this.viewMode) {
21192 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21198 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21206 moveMonth: function(date, dir)
21211 var new_date = new Date(date.valueOf()),
21212 day = new_date.getUTCDate(),
21213 month = new_date.getUTCMonth(),
21214 mag = Math.abs(dir),
21216 dir = dir > 0 ? 1 : -1;
21219 // If going back one month, make sure month is not current month
21220 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21222 return new_date.getUTCMonth() == month;
21224 // If going forward one month, make sure month is as expected
21225 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21227 return new_date.getUTCMonth() != new_month;
21229 new_month = month + dir;
21230 new_date.setUTCMonth(new_month);
21231 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21232 if (new_month < 0 || new_month > 11) {
21233 new_month = (new_month + 12) % 12;
21236 // For magnitudes >1, move one month at a time...
21237 for (var i=0; i<mag; i++) {
21238 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21239 new_date = this.moveMonth(new_date, dir);
21241 // ...then reset the day, keeping it in the new month
21242 new_month = new_date.getUTCMonth();
21243 new_date.setUTCDate(day);
21245 return new_month != new_date.getUTCMonth();
21248 // Common date-resetting loop -- if date is beyond end of month, make it
21251 new_date.setUTCDate(--day);
21252 new_date.setUTCMonth(new_month);
21257 moveYear: function(date, dir)
21259 return this.moveMonth(date, dir*12);
21262 dateWithinRange: function(date)
21264 return date >= this.startDate && date <= this.endDate;
21270 this.picker().remove();
21273 validateValue : function(value)
21275 if(this.getVisibilityEl().hasClass('hidden')){
21279 if(value.length < 1) {
21280 if(this.allowBlank){
21286 if(value.length < this.minLength){
21289 if(value.length > this.maxLength){
21293 var vt = Roo.form.VTypes;
21294 if(!vt[this.vtype](value, this)){
21298 if(typeof this.validator == "function"){
21299 var msg = this.validator(value);
21305 if(this.regex && !this.regex.test(value)){
21309 if(typeof(this.parseDate(value)) == 'undefined'){
21313 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21317 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21327 this.date = this.viewDate = '';
21329 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21334 Roo.apply(Roo.bootstrap.DateField, {
21345 html: '<i class="fa fa-arrow-left"/>'
21355 html: '<i class="fa fa-arrow-right"/>'
21397 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21398 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21399 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21400 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21401 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21414 navFnc: 'FullYear',
21419 navFnc: 'FullYear',
21424 Roo.apply(Roo.bootstrap.DateField, {
21428 cls: 'datepicker dropdown-menu roo-dynamic',
21432 cls: 'datepicker-days',
21436 cls: 'table-condensed',
21438 Roo.bootstrap.DateField.head,
21442 Roo.bootstrap.DateField.footer
21449 cls: 'datepicker-months',
21453 cls: 'table-condensed',
21455 Roo.bootstrap.DateField.head,
21456 Roo.bootstrap.DateField.content,
21457 Roo.bootstrap.DateField.footer
21464 cls: 'datepicker-years',
21468 cls: 'table-condensed',
21470 Roo.bootstrap.DateField.head,
21471 Roo.bootstrap.DateField.content,
21472 Roo.bootstrap.DateField.footer
21491 * @class Roo.bootstrap.TimeField
21492 * @extends Roo.bootstrap.Input
21493 * Bootstrap DateField class
21497 * Create a new TimeField
21498 * @param {Object} config The config object
21501 Roo.bootstrap.TimeField = function(config){
21502 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21506 * Fires when this field show.
21507 * @param {Roo.bootstrap.DateField} thisthis
21508 * @param {Mixed} date The date value
21513 * Fires when this field hide.
21514 * @param {Roo.bootstrap.DateField} this
21515 * @param {Mixed} date The date value
21520 * Fires when select a date.
21521 * @param {Roo.bootstrap.DateField} this
21522 * @param {Mixed} date The date value
21528 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21531 * @cfg {String} format
21532 * The default time format string which can be overriden for localization support. The format must be
21533 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21537 onRender: function(ct, position)
21540 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21542 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21544 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21546 this.pop = this.picker().select('>.datepicker-time',true).first();
21547 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21549 this.picker().on('mousedown', this.onMousedown, this);
21550 this.picker().on('click', this.onClick, this);
21552 this.picker().addClass('datepicker-dropdown');
21557 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21558 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21559 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21560 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21561 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21562 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21566 fireKey: function(e){
21567 if (!this.picker().isVisible()){
21568 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21574 e.preventDefault();
21582 this.onTogglePeriod();
21585 this.onIncrementMinutes();
21588 this.onDecrementMinutes();
21597 onClick: function(e) {
21598 e.stopPropagation();
21599 e.preventDefault();
21602 picker : function()
21604 return this.el.select('.datepicker', true).first();
21607 fillTime: function()
21609 var time = this.pop.select('tbody', true).first();
21611 time.dom.innerHTML = '';
21626 cls: 'hours-up glyphicon glyphicon-chevron-up'
21646 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21667 cls: 'timepicker-hour',
21682 cls: 'timepicker-minute',
21697 cls: 'btn btn-primary period',
21719 cls: 'hours-down glyphicon glyphicon-chevron-down'
21739 cls: 'minutes-down glyphicon glyphicon-chevron-down'
21757 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21764 var hours = this.time.getHours();
21765 var minutes = this.time.getMinutes();
21778 hours = hours - 12;
21782 hours = '0' + hours;
21786 minutes = '0' + minutes;
21789 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21790 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21791 this.pop.select('button', true).first().dom.innerHTML = period;
21797 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21799 var cls = ['bottom'];
21801 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21808 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21813 this.picker().addClass(cls.join('-'));
21817 Roo.each(cls, function(c){
21819 _this.picker().setTop(_this.inputEl().getHeight());
21823 _this.picker().setTop(0 - _this.picker().getHeight());
21828 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21832 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21839 onFocus : function()
21841 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21845 onBlur : function()
21847 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21853 this.picker().show();
21858 this.fireEvent('show', this, this.date);
21863 this.picker().hide();
21866 this.fireEvent('hide', this, this.date);
21869 setTime : function()
21872 this.setValue(this.time.format(this.format));
21874 this.fireEvent('select', this, this.date);
21879 onMousedown: function(e){
21880 e.stopPropagation();
21881 e.preventDefault();
21884 onIncrementHours: function()
21886 Roo.log('onIncrementHours');
21887 this.time = this.time.add(Date.HOUR, 1);
21892 onDecrementHours: function()
21894 Roo.log('onDecrementHours');
21895 this.time = this.time.add(Date.HOUR, -1);
21899 onIncrementMinutes: function()
21901 Roo.log('onIncrementMinutes');
21902 this.time = this.time.add(Date.MINUTE, 1);
21906 onDecrementMinutes: function()
21908 Roo.log('onDecrementMinutes');
21909 this.time = this.time.add(Date.MINUTE, -1);
21913 onTogglePeriod: function()
21915 Roo.log('onTogglePeriod');
21916 this.time = this.time.add(Date.HOUR, 12);
21923 Roo.apply(Roo.bootstrap.TimeField, {
21953 cls: 'btn btn-info ok',
21965 Roo.apply(Roo.bootstrap.TimeField, {
21969 cls: 'datepicker dropdown-menu',
21973 cls: 'datepicker-time',
21977 cls: 'table-condensed',
21979 Roo.bootstrap.TimeField.content,
21980 Roo.bootstrap.TimeField.footer
21999 * @class Roo.bootstrap.MonthField
22000 * @extends Roo.bootstrap.Input
22001 * Bootstrap MonthField class
22003 * @cfg {String} language default en
22006 * Create a new MonthField
22007 * @param {Object} config The config object
22010 Roo.bootstrap.MonthField = function(config){
22011 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22016 * Fires when this field show.
22017 * @param {Roo.bootstrap.MonthField} this
22018 * @param {Mixed} date The date value
22023 * Fires when this field hide.
22024 * @param {Roo.bootstrap.MonthField} this
22025 * @param {Mixed} date The date value
22030 * Fires when select a date.
22031 * @param {Roo.bootstrap.MonthField} this
22032 * @param {String} oldvalue The old value
22033 * @param {String} newvalue The new value
22039 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22041 onRender: function(ct, position)
22044 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22046 this.language = this.language || 'en';
22047 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22048 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22050 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22051 this.isInline = false;
22052 this.isInput = true;
22053 this.component = this.el.select('.add-on', true).first() || false;
22054 this.component = (this.component && this.component.length === 0) ? false : this.component;
22055 this.hasInput = this.component && this.inputEL().length;
22057 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22059 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22061 this.picker().on('mousedown', this.onMousedown, this);
22062 this.picker().on('click', this.onClick, this);
22064 this.picker().addClass('datepicker-dropdown');
22066 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22067 v.setStyle('width', '189px');
22074 if(this.isInline) {
22080 setValue: function(v, suppressEvent)
22082 var o = this.getValue();
22084 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22088 if(suppressEvent !== true){
22089 this.fireEvent('select', this, o, v);
22094 getValue: function()
22099 onClick: function(e)
22101 e.stopPropagation();
22102 e.preventDefault();
22104 var target = e.getTarget();
22106 if(target.nodeName.toLowerCase() === 'i'){
22107 target = Roo.get(target).dom.parentNode;
22110 var nodeName = target.nodeName;
22111 var className = target.className;
22112 var html = target.innerHTML;
22114 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22118 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22120 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22126 picker : function()
22128 return this.pickerEl;
22131 fillMonths: function()
22134 var months = this.picker().select('>.datepicker-months td', true).first();
22136 months.dom.innerHTML = '';
22142 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22145 months.createChild(month);
22154 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22155 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22158 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22159 e.removeClass('active');
22161 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22162 e.addClass('active');
22169 if(this.isInline) {
22173 this.picker().removeClass(['bottom', 'top']);
22175 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22177 * place to the top of element!
22181 this.picker().addClass('top');
22182 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22187 this.picker().addClass('bottom');
22189 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22192 onFocus : function()
22194 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22198 onBlur : function()
22200 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22202 var d = this.inputEl().getValue();
22211 this.picker().show();
22212 this.picker().select('>.datepicker-months', true).first().show();
22216 this.fireEvent('show', this, this.date);
22221 if(this.isInline) {
22224 this.picker().hide();
22225 this.fireEvent('hide', this, this.date);
22229 onMousedown: function(e)
22231 e.stopPropagation();
22232 e.preventDefault();
22237 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22241 fireKey: function(e)
22243 if (!this.picker().isVisible()){
22244 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22255 e.preventDefault();
22259 dir = e.keyCode == 37 ? -1 : 1;
22261 this.vIndex = this.vIndex + dir;
22263 if(this.vIndex < 0){
22267 if(this.vIndex > 11){
22271 if(isNaN(this.vIndex)){
22275 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22281 dir = e.keyCode == 38 ? -1 : 1;
22283 this.vIndex = this.vIndex + dir * 4;
22285 if(this.vIndex < 0){
22289 if(this.vIndex > 11){
22293 if(isNaN(this.vIndex)){
22297 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22302 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22303 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22307 e.preventDefault();
22310 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22311 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22327 this.picker().remove();
22332 Roo.apply(Roo.bootstrap.MonthField, {
22351 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22352 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22357 Roo.apply(Roo.bootstrap.MonthField, {
22361 cls: 'datepicker dropdown-menu roo-dynamic',
22365 cls: 'datepicker-months',
22369 cls: 'table-condensed',
22371 Roo.bootstrap.DateField.content
22391 * @class Roo.bootstrap.CheckBox
22392 * @extends Roo.bootstrap.Input
22393 * Bootstrap CheckBox class
22395 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22396 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22397 * @cfg {String} boxLabel The text that appears beside the checkbox
22398 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22399 * @cfg {Boolean} checked initnal the element
22400 * @cfg {Boolean} inline inline the element (default false)
22401 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22402 * @cfg {String} tooltip label tooltip
22405 * Create a new CheckBox
22406 * @param {Object} config The config object
22409 Roo.bootstrap.CheckBox = function(config){
22410 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22415 * Fires when the element is checked or unchecked.
22416 * @param {Roo.bootstrap.CheckBox} this This input
22417 * @param {Boolean} checked The new checked value
22422 * Fires when the element is click.
22423 * @param {Roo.bootstrap.CheckBox} this This input
22430 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22432 inputType: 'checkbox',
22441 // checkbox success does not make any sense really..
22446 getAutoCreate : function()
22448 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22454 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22457 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22463 type : this.inputType,
22464 value : this.inputValue,
22465 cls : 'roo-' + this.inputType, //'form-box',
22466 placeholder : this.placeholder || ''
22470 if(this.inputType != 'radio'){
22474 cls : 'roo-hidden-value',
22475 value : this.checked ? this.inputValue : this.valueOff
22480 if (this.weight) { // Validity check?
22481 cfg.cls += " " + this.inputType + "-" + this.weight;
22484 if (this.disabled) {
22485 input.disabled=true;
22489 input.checked = this.checked;
22494 input.name = this.name;
22496 if(this.inputType != 'radio'){
22497 hidden.name = this.name;
22498 input.name = '_hidden_' + this.name;
22503 input.cls += ' input-' + this.size;
22508 ['xs','sm','md','lg'].map(function(size){
22509 if (settings[size]) {
22510 cfg.cls += ' col-' + size + '-' + settings[size];
22514 var inputblock = input;
22516 if (this.before || this.after) {
22519 cls : 'input-group',
22524 inputblock.cn.push({
22526 cls : 'input-group-addon',
22531 inputblock.cn.push(input);
22533 if(this.inputType != 'radio'){
22534 inputblock.cn.push(hidden);
22538 inputblock.cn.push({
22540 cls : 'input-group-addon',
22546 var boxLabelCfg = false;
22552 //'for': id, // box label is handled by onclick - so no for...
22554 html: this.boxLabel
22557 boxLabelCfg.tooltip = this.tooltip;
22563 if (align ==='left' && this.fieldLabel.length) {
22564 // Roo.log("left and has label");
22569 cls : 'control-label',
22570 html : this.fieldLabel
22581 cfg.cn[1].cn.push(boxLabelCfg);
22584 if(this.labelWidth > 12){
22585 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22588 if(this.labelWidth < 13 && this.labelmd == 0){
22589 this.labelmd = this.labelWidth;
22592 if(this.labellg > 0){
22593 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22594 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22597 if(this.labelmd > 0){
22598 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22599 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22602 if(this.labelsm > 0){
22603 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22604 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22607 if(this.labelxs > 0){
22608 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22609 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22612 } else if ( this.fieldLabel.length) {
22613 // Roo.log(" label");
22617 tag: this.boxLabel ? 'span' : 'label',
22619 cls: 'control-label box-input-label',
22620 //cls : 'input-group-addon',
22621 html : this.fieldLabel
22628 cfg.cn.push(boxLabelCfg);
22633 // Roo.log(" no label && no align");
22634 cfg.cn = [ inputblock ] ;
22636 cfg.cn.push(boxLabelCfg);
22644 if(this.inputType != 'radio'){
22645 cfg.cn.push(hidden);
22653 * return the real input element.
22655 inputEl: function ()
22657 return this.el.select('input.roo-' + this.inputType,true).first();
22659 hiddenEl: function ()
22661 return this.el.select('input.roo-hidden-value',true).first();
22664 labelEl: function()
22666 return this.el.select('label.control-label',true).first();
22668 /* depricated... */
22672 return this.labelEl();
22675 boxLabelEl: function()
22677 return this.el.select('label.box-label',true).first();
22680 initEvents : function()
22682 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22684 this.inputEl().on('click', this.onClick, this);
22686 if (this.boxLabel) {
22687 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22690 this.startValue = this.getValue();
22693 Roo.bootstrap.CheckBox.register(this);
22697 onClick : function(e)
22699 if(this.fireEvent('click', this, e) !== false){
22700 this.setChecked(!this.checked);
22705 setChecked : function(state,suppressEvent)
22707 this.startValue = this.getValue();
22709 if(this.inputType == 'radio'){
22711 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22712 e.dom.checked = false;
22715 this.inputEl().dom.checked = true;
22717 this.inputEl().dom.value = this.inputValue;
22719 if(suppressEvent !== true){
22720 this.fireEvent('check', this, true);
22728 this.checked = state;
22730 this.inputEl().dom.checked = state;
22733 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22735 if(suppressEvent !== true){
22736 this.fireEvent('check', this, state);
22742 getValue : function()
22744 if(this.inputType == 'radio'){
22745 return this.getGroupValue();
22748 return this.hiddenEl().dom.value;
22752 getGroupValue : function()
22754 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22758 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22761 setValue : function(v,suppressEvent)
22763 if(this.inputType == 'radio'){
22764 this.setGroupValue(v, suppressEvent);
22768 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22773 setGroupValue : function(v, suppressEvent)
22775 this.startValue = this.getValue();
22777 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22778 e.dom.checked = false;
22780 if(e.dom.value == v){
22781 e.dom.checked = true;
22785 if(suppressEvent !== true){
22786 this.fireEvent('check', this, true);
22794 validate : function()
22796 if(this.getVisibilityEl().hasClass('hidden')){
22802 (this.inputType == 'radio' && this.validateRadio()) ||
22803 (this.inputType == 'checkbox' && this.validateCheckbox())
22809 this.markInvalid();
22813 validateRadio : function()
22815 if(this.getVisibilityEl().hasClass('hidden')){
22819 if(this.allowBlank){
22825 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22826 if(!e.dom.checked){
22838 validateCheckbox : function()
22841 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22842 //return (this.getValue() == this.inputValue) ? true : false;
22845 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22853 for(var i in group){
22854 if(group[i].el.isVisible(true)){
22862 for(var i in group){
22867 r = (group[i].getValue() == group[i].inputValue) ? true : false;
22874 * Mark this field as valid
22876 markValid : function()
22880 this.fireEvent('valid', this);
22882 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22885 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22892 if(this.inputType == 'radio'){
22893 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22894 var fg = e.findParent('.form-group', false, true);
22895 if (Roo.bootstrap.version == 3) {
22896 fg.removeClass([_this.invalidClass, _this.validClass]);
22897 fg.addClass(_this.validClass);
22899 fg.removeClass(['is-valid', 'is-invalid']);
22900 fg.addClass('is-valid');
22908 var fg = this.el.findParent('.form-group', false, true);
22909 if (Roo.bootstrap.version == 3) {
22910 fg.removeClass([this.invalidClass, this.validClass]);
22911 fg.addClass(this.validClass);
22913 fg.removeClass(['is-valid', 'is-invalid']);
22914 fg.addClass('is-valid');
22919 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22925 for(var i in group){
22926 var fg = group[i].el.findParent('.form-group', false, true);
22927 if (Roo.bootstrap.version == 3) {
22928 fg.removeClass([this.invalidClass, this.validClass]);
22929 fg.addClass(this.validClass);
22931 fg.removeClass(['is-valid', 'is-invalid']);
22932 fg.addClass('is-valid');
22938 * Mark this field as invalid
22939 * @param {String} msg The validation message
22941 markInvalid : function(msg)
22943 if(this.allowBlank){
22949 this.fireEvent('invalid', this, msg);
22951 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22954 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22958 label.markInvalid();
22961 if(this.inputType == 'radio'){
22963 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22964 var fg = e.findParent('.form-group', false, true);
22965 if (Roo.bootstrap.version == 3) {
22966 fg.removeClass([_this.invalidClass, _this.validClass]);
22967 fg.addClass(_this.invalidClass);
22969 fg.removeClass(['is-invalid', 'is-valid']);
22970 fg.addClass('is-invalid');
22978 var fg = this.el.findParent('.form-group', false, true);
22979 if (Roo.bootstrap.version == 3) {
22980 fg.removeClass([_this.invalidClass, _this.validClass]);
22981 fg.addClass(_this.invalidClass);
22983 fg.removeClass(['is-invalid', 'is-valid']);
22984 fg.addClass('is-invalid');
22989 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22995 for(var i in group){
22996 var fg = group[i].el.findParent('.form-group', false, true);
22997 if (Roo.bootstrap.version == 3) {
22998 fg.removeClass([_this.invalidClass, _this.validClass]);
22999 fg.addClass(_this.invalidClass);
23001 fg.removeClass(['is-invalid', 'is-valid']);
23002 fg.addClass('is-invalid');
23008 clearInvalid : function()
23010 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23012 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23014 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23016 if (label && label.iconEl) {
23017 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23018 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23022 disable : function()
23024 if(this.inputType != 'radio'){
23025 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23032 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23033 _this.getActionEl().addClass(this.disabledClass);
23034 e.dom.disabled = true;
23038 this.disabled = true;
23039 this.fireEvent("disable", this);
23043 enable : function()
23045 if(this.inputType != 'radio'){
23046 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23053 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23054 _this.getActionEl().removeClass(this.disabledClass);
23055 e.dom.disabled = false;
23059 this.disabled = false;
23060 this.fireEvent("enable", this);
23064 setBoxLabel : function(v)
23069 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23075 Roo.apply(Roo.bootstrap.CheckBox, {
23080 * register a CheckBox Group
23081 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23083 register : function(checkbox)
23085 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23086 this.groups[checkbox.groupId] = {};
23089 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23093 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23097 * fetch a CheckBox Group based on the group ID
23098 * @param {string} the group ID
23099 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23101 get: function(groupId) {
23102 if (typeof(this.groups[groupId]) == 'undefined') {
23106 return this.groups[groupId] ;
23119 * @class Roo.bootstrap.Radio
23120 * @extends Roo.bootstrap.Component
23121 * Bootstrap Radio class
23122 * @cfg {String} boxLabel - the label associated
23123 * @cfg {String} value - the value of radio
23126 * Create a new Radio
23127 * @param {Object} config The config object
23129 Roo.bootstrap.Radio = function(config){
23130 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23134 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23140 getAutoCreate : function()
23144 cls : 'form-group radio',
23149 html : this.boxLabel
23157 initEvents : function()
23159 this.parent().register(this);
23161 this.el.on('click', this.onClick, this);
23165 onClick : function(e)
23167 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23168 this.setChecked(true);
23172 setChecked : function(state, suppressEvent)
23174 this.parent().setValue(this.value, suppressEvent);
23178 setBoxLabel : function(v)
23183 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23198 * @class Roo.bootstrap.SecurePass
23199 * @extends Roo.bootstrap.Input
23200 * Bootstrap SecurePass class
23204 * Create a new SecurePass
23205 * @param {Object} config The config object
23208 Roo.bootstrap.SecurePass = function (config) {
23209 // these go here, so the translation tool can replace them..
23211 PwdEmpty: "Please type a password, and then retype it to confirm.",
23212 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23213 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23214 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23215 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23216 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23217 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23218 TooWeak: "Your password is Too Weak."
23220 this.meterLabel = "Password strength:";
23221 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23222 this.meterClass = [
23223 "roo-password-meter-tooweak",
23224 "roo-password-meter-weak",
23225 "roo-password-meter-medium",
23226 "roo-password-meter-strong",
23227 "roo-password-meter-grey"
23232 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23235 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23237 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23239 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23240 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23241 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23242 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23243 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23244 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23245 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23255 * @cfg {String/Object} Label for the strength meter (defaults to
23256 * 'Password strength:')
23261 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23262 * ['Weak', 'Medium', 'Strong'])
23265 pwdStrengths: false,
23278 initEvents: function ()
23280 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23282 if (this.el.is('input[type=password]') && Roo.isSafari) {
23283 this.el.on('keydown', this.SafariOnKeyDown, this);
23286 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23289 onRender: function (ct, position)
23291 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23292 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23293 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23295 this.trigger.createChild({
23300 cls: 'roo-password-meter-grey col-xs-12',
23303 //width: this.meterWidth + 'px'
23307 cls: 'roo-password-meter-text'
23313 if (this.hideTrigger) {
23314 this.trigger.setDisplayed(false);
23316 this.setSize(this.width || '', this.height || '');
23319 onDestroy: function ()
23321 if (this.trigger) {
23322 this.trigger.removeAllListeners();
23323 this.trigger.remove();
23326 this.wrap.remove();
23328 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23331 checkStrength: function ()
23333 var pwd = this.inputEl().getValue();
23334 if (pwd == this._lastPwd) {
23339 if (this.ClientSideStrongPassword(pwd)) {
23341 } else if (this.ClientSideMediumPassword(pwd)) {
23343 } else if (this.ClientSideWeakPassword(pwd)) {
23349 Roo.log('strength1: ' + strength);
23351 //var pm = this.trigger.child('div/div/div').dom;
23352 var pm = this.trigger.child('div/div');
23353 pm.removeClass(this.meterClass);
23354 pm.addClass(this.meterClass[strength]);
23357 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23359 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23361 this._lastPwd = pwd;
23365 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23367 this._lastPwd = '';
23369 var pm = this.trigger.child('div/div');
23370 pm.removeClass(this.meterClass);
23371 pm.addClass('roo-password-meter-grey');
23374 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23377 this.inputEl().dom.type='password';
23380 validateValue: function (value)
23383 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23386 if (value.length == 0) {
23387 if (this.allowBlank) {
23388 this.clearInvalid();
23392 this.markInvalid(this.errors.PwdEmpty);
23393 this.errorMsg = this.errors.PwdEmpty;
23401 if ('[\x21-\x7e]*'.match(value)) {
23402 this.markInvalid(this.errors.PwdBadChar);
23403 this.errorMsg = this.errors.PwdBadChar;
23406 if (value.length < 6) {
23407 this.markInvalid(this.errors.PwdShort);
23408 this.errorMsg = this.errors.PwdShort;
23411 if (value.length > 16) {
23412 this.markInvalid(this.errors.PwdLong);
23413 this.errorMsg = this.errors.PwdLong;
23417 if (this.ClientSideStrongPassword(value)) {
23419 } else if (this.ClientSideMediumPassword(value)) {
23421 } else if (this.ClientSideWeakPassword(value)) {
23428 if (strength < 2) {
23429 //this.markInvalid(this.errors.TooWeak);
23430 this.errorMsg = this.errors.TooWeak;
23435 console.log('strength2: ' + strength);
23437 //var pm = this.trigger.child('div/div/div').dom;
23439 var pm = this.trigger.child('div/div');
23440 pm.removeClass(this.meterClass);
23441 pm.addClass(this.meterClass[strength]);
23443 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23445 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23447 this.errorMsg = '';
23451 CharacterSetChecks: function (type)
23454 this.fResult = false;
23457 isctype: function (character, type)
23460 case this.kCapitalLetter:
23461 if (character >= 'A' && character <= 'Z') {
23466 case this.kSmallLetter:
23467 if (character >= 'a' && character <= 'z') {
23473 if (character >= '0' && character <= '9') {
23478 case this.kPunctuation:
23479 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23490 IsLongEnough: function (pwd, size)
23492 return !(pwd == null || isNaN(size) || pwd.length < size);
23495 SpansEnoughCharacterSets: function (word, nb)
23497 if (!this.IsLongEnough(word, nb))
23502 var characterSetChecks = new Array(
23503 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23504 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23507 for (var index = 0; index < word.length; ++index) {
23508 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23509 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23510 characterSetChecks[nCharSet].fResult = true;
23517 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23518 if (characterSetChecks[nCharSet].fResult) {
23523 if (nCharSets < nb) {
23529 ClientSideStrongPassword: function (pwd)
23531 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23534 ClientSideMediumPassword: function (pwd)
23536 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23539 ClientSideWeakPassword: function (pwd)
23541 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23544 })//<script type="text/javascript">
23547 * Based Ext JS Library 1.1.1
23548 * Copyright(c) 2006-2007, Ext JS, LLC.
23554 * @class Roo.HtmlEditorCore
23555 * @extends Roo.Component
23556 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23558 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23561 Roo.HtmlEditorCore = function(config){
23564 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23569 * @event initialize
23570 * Fires when the editor is fully initialized (including the iframe)
23571 * @param {Roo.HtmlEditorCore} this
23576 * Fires when the editor is first receives the focus. Any insertion must wait
23577 * until after this event.
23578 * @param {Roo.HtmlEditorCore} this
23582 * @event beforesync
23583 * Fires before the textarea is updated with content from the editor iframe. Return false
23584 * to cancel the sync.
23585 * @param {Roo.HtmlEditorCore} this
23586 * @param {String} html
23590 * @event beforepush
23591 * Fires before the iframe editor is updated with content from the textarea. Return false
23592 * to cancel the push.
23593 * @param {Roo.HtmlEditorCore} this
23594 * @param {String} html
23599 * Fires when the textarea is updated with content from the editor iframe.
23600 * @param {Roo.HtmlEditorCore} this
23601 * @param {String} html
23606 * Fires when the iframe editor is updated with content from the textarea.
23607 * @param {Roo.HtmlEditorCore} this
23608 * @param {String} html
23613 * @event editorevent
23614 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23615 * @param {Roo.HtmlEditorCore} this
23621 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23623 // defaults : white / black...
23624 this.applyBlacklists();
23631 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23635 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23641 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23646 * @cfg {Number} height (in pixels)
23650 * @cfg {Number} width (in pixels)
23655 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23658 stylesheets: false,
23663 // private properties
23664 validationEvent : false,
23666 initialized : false,
23668 sourceEditMode : false,
23669 onFocus : Roo.emptyFn,
23671 hideMode:'offsets',
23675 // blacklist + whitelisted elements..
23682 * Protected method that will not generally be called directly. It
23683 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23684 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23686 getDocMarkup : function(){
23690 // inherit styels from page...??
23691 if (this.stylesheets === false) {
23693 Roo.get(document.head).select('style').each(function(node) {
23694 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23697 Roo.get(document.head).select('link').each(function(node) {
23698 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23701 } else if (!this.stylesheets.length) {
23703 st = '<style type="text/css">' +
23704 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23707 st = '<style type="text/css">' +
23712 st += '<style type="text/css">' +
23713 'IMG { cursor: pointer } ' +
23716 var cls = 'roo-htmleditor-body';
23718 if(this.bodyCls.length){
23719 cls += ' ' + this.bodyCls;
23722 return '<html><head>' + st +
23723 //<style type="text/css">' +
23724 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23726 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23730 onRender : function(ct, position)
23733 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23734 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23737 this.el.dom.style.border = '0 none';
23738 this.el.dom.setAttribute('tabIndex', -1);
23739 this.el.addClass('x-hidden hide');
23743 if(Roo.isIE){ // fix IE 1px bogus margin
23744 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23748 this.frameId = Roo.id();
23752 var iframe = this.owner.wrap.createChild({
23754 cls: 'form-control', // bootstrap..
23756 name: this.frameId,
23757 frameBorder : 'no',
23758 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
23763 this.iframe = iframe.dom;
23765 this.assignDocWin();
23767 this.doc.designMode = 'on';
23770 this.doc.write(this.getDocMarkup());
23774 var task = { // must defer to wait for browser to be ready
23776 //console.log("run task?" + this.doc.readyState);
23777 this.assignDocWin();
23778 if(this.doc.body || this.doc.readyState == 'complete'){
23780 this.doc.designMode="on";
23784 Roo.TaskMgr.stop(task);
23785 this.initEditor.defer(10, this);
23792 Roo.TaskMgr.start(task);
23797 onResize : function(w, h)
23799 Roo.log('resize: ' +w + ',' + h );
23800 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23804 if(typeof w == 'number'){
23806 this.iframe.style.width = w + 'px';
23808 if(typeof h == 'number'){
23810 this.iframe.style.height = h + 'px';
23812 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23819 * Toggles the editor between standard and source edit mode.
23820 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23822 toggleSourceEdit : function(sourceEditMode){
23824 this.sourceEditMode = sourceEditMode === true;
23826 if(this.sourceEditMode){
23828 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
23831 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23832 //this.iframe.className = '';
23835 //this.setSize(this.owner.wrap.getSize());
23836 //this.fireEvent('editmodechange', this, this.sourceEditMode);
23843 * Protected method that will not generally be called directly. If you need/want
23844 * custom HTML cleanup, this is the method you should override.
23845 * @param {String} html The HTML to be cleaned
23846 * return {String} The cleaned HTML
23848 cleanHtml : function(html){
23849 html = String(html);
23850 if(html.length > 5){
23851 if(Roo.isSafari){ // strip safari nonsense
23852 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23855 if(html == ' '){
23862 * HTML Editor -> Textarea
23863 * Protected method that will not generally be called directly. Syncs the contents
23864 * of the editor iframe with the textarea.
23866 syncValue : function(){
23867 if(this.initialized){
23868 var bd = (this.doc.body || this.doc.documentElement);
23869 //this.cleanUpPaste(); -- this is done else where and causes havoc..
23870 var html = bd.innerHTML;
23872 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23873 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23875 html = '<div style="'+m[0]+'">' + html + '</div>';
23878 html = this.cleanHtml(html);
23879 // fix up the special chars.. normaly like back quotes in word...
23880 // however we do not want to do this with chinese..
23881 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23883 var cc = match.charCodeAt();
23885 // Get the character value, handling surrogate pairs
23886 if (match.length == 2) {
23887 // It's a surrogate pair, calculate the Unicode code point
23888 var high = match.charCodeAt(0) - 0xD800;
23889 var low = match.charCodeAt(1) - 0xDC00;
23890 cc = (high * 0x400) + low + 0x10000;
23892 (cc >= 0x4E00 && cc < 0xA000 ) ||
23893 (cc >= 0x3400 && cc < 0x4E00 ) ||
23894 (cc >= 0xf900 && cc < 0xfb00 )
23899 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23900 return "&#" + cc + ";";
23907 if(this.owner.fireEvent('beforesync', this, html) !== false){
23908 this.el.dom.value = html;
23909 this.owner.fireEvent('sync', this, html);
23915 * Protected method that will not generally be called directly. Pushes the value of the textarea
23916 * into the iframe editor.
23918 pushValue : function(){
23919 if(this.initialized){
23920 var v = this.el.dom.value.trim();
23922 // if(v.length < 1){
23926 if(this.owner.fireEvent('beforepush', this, v) !== false){
23927 var d = (this.doc.body || this.doc.documentElement);
23929 this.cleanUpPaste();
23930 this.el.dom.value = d.innerHTML;
23931 this.owner.fireEvent('push', this, v);
23937 deferFocus : function(){
23938 this.focus.defer(10, this);
23942 focus : function(){
23943 if(this.win && !this.sourceEditMode){
23950 assignDocWin: function()
23952 var iframe = this.iframe;
23955 this.doc = iframe.contentWindow.document;
23956 this.win = iframe.contentWindow;
23958 // if (!Roo.get(this.frameId)) {
23961 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23962 // this.win = Roo.get(this.frameId).dom.contentWindow;
23964 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23968 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23969 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23974 initEditor : function(){
23975 //console.log("INIT EDITOR");
23976 this.assignDocWin();
23980 this.doc.designMode="on";
23982 this.doc.write(this.getDocMarkup());
23985 var dbody = (this.doc.body || this.doc.documentElement);
23986 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23987 // this copies styles from the containing element into thsi one..
23988 // not sure why we need all of this..
23989 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23991 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23992 //ss['background-attachment'] = 'fixed'; // w3c
23993 dbody.bgProperties = 'fixed'; // ie
23994 //Roo.DomHelper.applyStyles(dbody, ss);
23995 Roo.EventManager.on(this.doc, {
23996 //'mousedown': this.onEditorEvent,
23997 'mouseup': this.onEditorEvent,
23998 'dblclick': this.onEditorEvent,
23999 'click': this.onEditorEvent,
24000 'keyup': this.onEditorEvent,
24005 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24007 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24008 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24010 this.initialized = true;
24012 this.owner.fireEvent('initialize', this);
24017 onDestroy : function(){
24023 //for (var i =0; i < this.toolbars.length;i++) {
24024 // // fixme - ask toolbars for heights?
24025 // this.toolbars[i].onDestroy();
24028 //this.wrap.dom.innerHTML = '';
24029 //this.wrap.remove();
24034 onFirstFocus : function(){
24036 this.assignDocWin();
24039 this.activated = true;
24042 if(Roo.isGecko){ // prevent silly gecko errors
24044 var s = this.win.getSelection();
24045 if(!s.focusNode || s.focusNode.nodeType != 3){
24046 var r = s.getRangeAt(0);
24047 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24052 this.execCmd('useCSS', true);
24053 this.execCmd('styleWithCSS', false);
24056 this.owner.fireEvent('activate', this);
24060 adjustFont: function(btn){
24061 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24062 //if(Roo.isSafari){ // safari
24065 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24066 if(Roo.isSafari){ // safari
24067 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24068 v = (v < 10) ? 10 : v;
24069 v = (v > 48) ? 48 : v;
24070 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24075 v = Math.max(1, v+adjust);
24077 this.execCmd('FontSize', v );
24080 onEditorEvent : function(e)
24082 this.owner.fireEvent('editorevent', this, e);
24083 // this.updateToolbar();
24084 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24087 insertTag : function(tg)
24089 // could be a bit smarter... -> wrap the current selected tRoo..
24090 if (tg.toLowerCase() == 'span' ||
24091 tg.toLowerCase() == 'code' ||
24092 tg.toLowerCase() == 'sup' ||
24093 tg.toLowerCase() == 'sub'
24096 range = this.createRange(this.getSelection());
24097 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24098 wrappingNode.appendChild(range.extractContents());
24099 range.insertNode(wrappingNode);
24106 this.execCmd("formatblock", tg);
24110 insertText : function(txt)
24114 var range = this.createRange();
24115 range.deleteContents();
24116 //alert(Sender.getAttribute('label'));
24118 range.insertNode(this.doc.createTextNode(txt));
24124 * Executes a Midas editor command on the editor document and performs necessary focus and
24125 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24126 * @param {String} cmd The Midas command
24127 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24129 relayCmd : function(cmd, value){
24131 this.execCmd(cmd, value);
24132 this.owner.fireEvent('editorevent', this);
24133 //this.updateToolbar();
24134 this.owner.deferFocus();
24138 * Executes a Midas editor command directly on the editor document.
24139 * For visual commands, you should use {@link #relayCmd} instead.
24140 * <b>This should only be called after the editor is initialized.</b>
24141 * @param {String} cmd The Midas command
24142 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24144 execCmd : function(cmd, value){
24145 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24152 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24154 * @param {String} text | dom node..
24156 insertAtCursor : function(text)
24159 if(!this.activated){
24165 var r = this.doc.selection.createRange();
24176 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24180 // from jquery ui (MIT licenced)
24182 var win = this.win;
24184 if (win.getSelection && win.getSelection().getRangeAt) {
24185 range = win.getSelection().getRangeAt(0);
24186 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24187 range.insertNode(node);
24188 } else if (win.document.selection && win.document.selection.createRange) {
24189 // no firefox support
24190 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24191 win.document.selection.createRange().pasteHTML(txt);
24193 // no firefox support
24194 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24195 this.execCmd('InsertHTML', txt);
24204 mozKeyPress : function(e){
24206 var c = e.getCharCode(), cmd;
24209 c = String.fromCharCode(c).toLowerCase();
24223 this.cleanUpPaste.defer(100, this);
24231 e.preventDefault();
24239 fixKeys : function(){ // load time branching for fastest keydown performance
24241 return function(e){
24242 var k = e.getKey(), r;
24245 r = this.doc.selection.createRange();
24248 r.pasteHTML('    ');
24255 r = this.doc.selection.createRange();
24257 var target = r.parentElement();
24258 if(!target || target.tagName.toLowerCase() != 'li'){
24260 r.pasteHTML('<br />');
24266 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24267 this.cleanUpPaste.defer(100, this);
24273 }else if(Roo.isOpera){
24274 return function(e){
24275 var k = e.getKey();
24279 this.execCmd('InsertHTML','    ');
24282 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24283 this.cleanUpPaste.defer(100, this);
24288 }else if(Roo.isSafari){
24289 return function(e){
24290 var k = e.getKey();
24294 this.execCmd('InsertText','\t');
24298 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24299 this.cleanUpPaste.defer(100, this);
24307 getAllAncestors: function()
24309 var p = this.getSelectedNode();
24312 a.push(p); // push blank onto stack..
24313 p = this.getParentElement();
24317 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24321 a.push(this.doc.body);
24325 lastSelNode : false,
24328 getSelection : function()
24330 this.assignDocWin();
24331 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24334 getSelectedNode: function()
24336 // this may only work on Gecko!!!
24338 // should we cache this!!!!
24343 var range = this.createRange(this.getSelection()).cloneRange();
24346 var parent = range.parentElement();
24348 var testRange = range.duplicate();
24349 testRange.moveToElementText(parent);
24350 if (testRange.inRange(range)) {
24353 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24356 parent = parent.parentElement;
24361 // is ancestor a text element.
24362 var ac = range.commonAncestorContainer;
24363 if (ac.nodeType == 3) {
24364 ac = ac.parentNode;
24367 var ar = ac.childNodes;
24370 var other_nodes = [];
24371 var has_other_nodes = false;
24372 for (var i=0;i<ar.length;i++) {
24373 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24376 // fullly contained node.
24378 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24383 // probably selected..
24384 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24385 other_nodes.push(ar[i]);
24389 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24394 has_other_nodes = true;
24396 if (!nodes.length && other_nodes.length) {
24397 nodes= other_nodes;
24399 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24405 createRange: function(sel)
24407 // this has strange effects when using with
24408 // top toolbar - not sure if it's a great idea.
24409 //this.editor.contentWindow.focus();
24410 if (typeof sel != "undefined") {
24412 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24414 return this.doc.createRange();
24417 return this.doc.createRange();
24420 getParentElement: function()
24423 this.assignDocWin();
24424 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24426 var range = this.createRange(sel);
24429 var p = range.commonAncestorContainer;
24430 while (p.nodeType == 3) { // text node
24441 * Range intersection.. the hard stuff...
24445 * [ -- selected range --- ]
24449 * if end is before start or hits it. fail.
24450 * if start is after end or hits it fail.
24452 * if either hits (but other is outside. - then it's not
24458 // @see http://www.thismuchiknow.co.uk/?p=64.
24459 rangeIntersectsNode : function(range, node)
24461 var nodeRange = node.ownerDocument.createRange();
24463 nodeRange.selectNode(node);
24465 nodeRange.selectNodeContents(node);
24468 var rangeStartRange = range.cloneRange();
24469 rangeStartRange.collapse(true);
24471 var rangeEndRange = range.cloneRange();
24472 rangeEndRange.collapse(false);
24474 var nodeStartRange = nodeRange.cloneRange();
24475 nodeStartRange.collapse(true);
24477 var nodeEndRange = nodeRange.cloneRange();
24478 nodeEndRange.collapse(false);
24480 return rangeStartRange.compareBoundaryPoints(
24481 Range.START_TO_START, nodeEndRange) == -1 &&
24482 rangeEndRange.compareBoundaryPoints(
24483 Range.START_TO_START, nodeStartRange) == 1;
24487 rangeCompareNode : function(range, node)
24489 var nodeRange = node.ownerDocument.createRange();
24491 nodeRange.selectNode(node);
24493 nodeRange.selectNodeContents(node);
24497 range.collapse(true);
24499 nodeRange.collapse(true);
24501 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24502 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24504 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24506 var nodeIsBefore = ss == 1;
24507 var nodeIsAfter = ee == -1;
24509 if (nodeIsBefore && nodeIsAfter) {
24512 if (!nodeIsBefore && nodeIsAfter) {
24513 return 1; //right trailed.
24516 if (nodeIsBefore && !nodeIsAfter) {
24517 return 2; // left trailed.
24523 // private? - in a new class?
24524 cleanUpPaste : function()
24526 // cleans up the whole document..
24527 Roo.log('cleanuppaste');
24529 this.cleanUpChildren(this.doc.body);
24530 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24531 if (clean != this.doc.body.innerHTML) {
24532 this.doc.body.innerHTML = clean;
24537 cleanWordChars : function(input) {// change the chars to hex code
24538 var he = Roo.HtmlEditorCore;
24540 var output = input;
24541 Roo.each(he.swapCodes, function(sw) {
24542 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24544 output = output.replace(swapper, sw[1]);
24551 cleanUpChildren : function (n)
24553 if (!n.childNodes.length) {
24556 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24557 this.cleanUpChild(n.childNodes[i]);
24564 cleanUpChild : function (node)
24567 //console.log(node);
24568 if (node.nodeName == "#text") {
24569 // clean up silly Windows -- stuff?
24572 if (node.nodeName == "#comment") {
24573 node.parentNode.removeChild(node);
24574 // clean up silly Windows -- stuff?
24577 var lcname = node.tagName.toLowerCase();
24578 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24579 // whitelist of tags..
24581 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24583 node.parentNode.removeChild(node);
24588 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24590 // spans with no attributes - just remove them..
24591 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24592 remove_keep_children = true;
24595 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24596 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24598 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24599 // remove_keep_children = true;
24602 if (remove_keep_children) {
24603 this.cleanUpChildren(node);
24604 // inserts everything just before this node...
24605 while (node.childNodes.length) {
24606 var cn = node.childNodes[0];
24607 node.removeChild(cn);
24608 node.parentNode.insertBefore(cn, node);
24610 node.parentNode.removeChild(node);
24614 if (!node.attributes || !node.attributes.length) {
24619 this.cleanUpChildren(node);
24623 function cleanAttr(n,v)
24626 if (v.match(/^\./) || v.match(/^\//)) {
24629 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24632 if (v.match(/^#/)) {
24635 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24636 node.removeAttribute(n);
24640 var cwhite = this.cwhite;
24641 var cblack = this.cblack;
24643 function cleanStyle(n,v)
24645 if (v.match(/expression/)) { //XSS?? should we even bother..
24646 node.removeAttribute(n);
24650 var parts = v.split(/;/);
24653 Roo.each(parts, function(p) {
24654 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24658 var l = p.split(':').shift().replace(/\s+/g,'');
24659 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24661 if ( cwhite.length && cblack.indexOf(l) > -1) {
24662 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24663 //node.removeAttribute(n);
24667 // only allow 'c whitelisted system attributes'
24668 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24669 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24670 //node.removeAttribute(n);
24680 if (clean.length) {
24681 node.setAttribute(n, clean.join(';'));
24683 node.removeAttribute(n);
24689 for (var i = node.attributes.length-1; i > -1 ; i--) {
24690 var a = node.attributes[i];
24693 if (a.name.toLowerCase().substr(0,2)=='on') {
24694 node.removeAttribute(a.name);
24697 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24698 node.removeAttribute(a.name);
24701 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24702 cleanAttr(a.name,a.value); // fixme..
24705 if (a.name == 'style') {
24706 cleanStyle(a.name,a.value);
24709 /// clean up MS crap..
24710 // tecnically this should be a list of valid class'es..
24713 if (a.name == 'class') {
24714 if (a.value.match(/^Mso/)) {
24715 node.removeAttribute('class');
24718 if (a.value.match(/^body$/)) {
24719 node.removeAttribute('class');
24730 this.cleanUpChildren(node);
24736 * Clean up MS wordisms...
24738 cleanWord : function(node)
24741 this.cleanWord(this.doc.body);
24746 node.nodeName == 'SPAN' &&
24747 !node.hasAttributes() &&
24748 node.childNodes.length == 1 &&
24749 node.firstChild.nodeName == "#text"
24751 var textNode = node.firstChild;
24752 node.removeChild(textNode);
24753 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24754 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24756 node.parentNode.insertBefore(textNode, node);
24757 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24758 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24760 node.parentNode.removeChild(node);
24763 if (node.nodeName == "#text") {
24764 // clean up silly Windows -- stuff?
24767 if (node.nodeName == "#comment") {
24768 node.parentNode.removeChild(node);
24769 // clean up silly Windows -- stuff?
24773 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24774 node.parentNode.removeChild(node);
24777 //Roo.log(node.tagName);
24778 // remove - but keep children..
24779 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24780 //Roo.log('-- removed');
24781 while (node.childNodes.length) {
24782 var cn = node.childNodes[0];
24783 node.removeChild(cn);
24784 node.parentNode.insertBefore(cn, node);
24785 // move node to parent - and clean it..
24786 this.cleanWord(cn);
24788 node.parentNode.removeChild(node);
24789 /// no need to iterate chidlren = it's got none..
24790 //this.iterateChildren(node, this.cleanWord);
24794 if (node.className.length) {
24796 var cn = node.className.split(/\W+/);
24798 Roo.each(cn, function(cls) {
24799 if (cls.match(/Mso[a-zA-Z]+/)) {
24804 node.className = cna.length ? cna.join(' ') : '';
24806 node.removeAttribute("class");
24810 if (node.hasAttribute("lang")) {
24811 node.removeAttribute("lang");
24814 if (node.hasAttribute("style")) {
24816 var styles = node.getAttribute("style").split(";");
24818 Roo.each(styles, function(s) {
24819 if (!s.match(/:/)) {
24822 var kv = s.split(":");
24823 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24826 // what ever is left... we allow.
24829 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24830 if (!nstyle.length) {
24831 node.removeAttribute('style');
24834 this.iterateChildren(node, this.cleanWord);
24840 * iterateChildren of a Node, calling fn each time, using this as the scole..
24841 * @param {DomNode} node node to iterate children of.
24842 * @param {Function} fn method of this class to call on each item.
24844 iterateChildren : function(node, fn)
24846 if (!node.childNodes.length) {
24849 for (var i = node.childNodes.length-1; i > -1 ; i--) {
24850 fn.call(this, node.childNodes[i])
24856 * cleanTableWidths.
24858 * Quite often pasting from word etc.. results in tables with column and widths.
24859 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24862 cleanTableWidths : function(node)
24867 this.cleanTableWidths(this.doc.body);
24872 if (node.nodeName == "#text" || node.nodeName == "#comment") {
24875 Roo.log(node.tagName);
24876 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24877 this.iterateChildren(node, this.cleanTableWidths);
24880 if (node.hasAttribute('width')) {
24881 node.removeAttribute('width');
24885 if (node.hasAttribute("style")) {
24888 var styles = node.getAttribute("style").split(";");
24890 Roo.each(styles, function(s) {
24891 if (!s.match(/:/)) {
24894 var kv = s.split(":");
24895 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24898 // what ever is left... we allow.
24901 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24902 if (!nstyle.length) {
24903 node.removeAttribute('style');
24907 this.iterateChildren(node, this.cleanTableWidths);
24915 domToHTML : function(currentElement, depth, nopadtext) {
24917 depth = depth || 0;
24918 nopadtext = nopadtext || false;
24920 if (!currentElement) {
24921 return this.domToHTML(this.doc.body);
24924 //Roo.log(currentElement);
24926 var allText = false;
24927 var nodeName = currentElement.nodeName;
24928 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24930 if (nodeName == '#text') {
24932 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24937 if (nodeName != 'BODY') {
24940 // Prints the node tagName, such as <A>, <IMG>, etc
24943 for(i = 0; i < currentElement.attributes.length;i++) {
24945 var aname = currentElement.attributes.item(i).name;
24946 if (!currentElement.attributes.item(i).value.length) {
24949 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24952 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24961 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24964 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24969 // Traverse the tree
24971 var currentElementChild = currentElement.childNodes.item(i);
24972 var allText = true;
24973 var innerHTML = '';
24975 while (currentElementChild) {
24976 // Formatting code (indent the tree so it looks nice on the screen)
24977 var nopad = nopadtext;
24978 if (lastnode == 'SPAN') {
24982 if (currentElementChild.nodeName == '#text') {
24983 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24984 toadd = nopadtext ? toadd : toadd.trim();
24985 if (!nopad && toadd.length > 80) {
24986 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
24988 innerHTML += toadd;
24991 currentElementChild = currentElement.childNodes.item(i);
24997 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
24999 // Recursively traverse the tree structure of the child node
25000 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25001 lastnode = currentElementChild.nodeName;
25003 currentElementChild=currentElement.childNodes.item(i);
25009 // The remaining code is mostly for formatting the tree
25010 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25015 ret+= "</"+tagName+">";
25021 applyBlacklists : function()
25023 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25024 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25028 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25029 if (b.indexOf(tag) > -1) {
25032 this.white.push(tag);
25036 Roo.each(w, function(tag) {
25037 if (b.indexOf(tag) > -1) {
25040 if (this.white.indexOf(tag) > -1) {
25043 this.white.push(tag);
25048 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25049 if (w.indexOf(tag) > -1) {
25052 this.black.push(tag);
25056 Roo.each(b, function(tag) {
25057 if (w.indexOf(tag) > -1) {
25060 if (this.black.indexOf(tag) > -1) {
25063 this.black.push(tag);
25068 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25069 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25073 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25074 if (b.indexOf(tag) > -1) {
25077 this.cwhite.push(tag);
25081 Roo.each(w, function(tag) {
25082 if (b.indexOf(tag) > -1) {
25085 if (this.cwhite.indexOf(tag) > -1) {
25088 this.cwhite.push(tag);
25093 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25094 if (w.indexOf(tag) > -1) {
25097 this.cblack.push(tag);
25101 Roo.each(b, function(tag) {
25102 if (w.indexOf(tag) > -1) {
25105 if (this.cblack.indexOf(tag) > -1) {
25108 this.cblack.push(tag);
25113 setStylesheets : function(stylesheets)
25115 if(typeof(stylesheets) == 'string'){
25116 Roo.get(this.iframe.contentDocument.head).createChild({
25118 rel : 'stylesheet',
25127 Roo.each(stylesheets, function(s) {
25132 Roo.get(_this.iframe.contentDocument.head).createChild({
25134 rel : 'stylesheet',
25143 removeStylesheets : function()
25147 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25152 setStyle : function(style)
25154 Roo.get(this.iframe.contentDocument.head).createChild({
25163 // hide stuff that is not compatible
25177 * @event specialkey
25181 * @cfg {String} fieldClass @hide
25184 * @cfg {String} focusClass @hide
25187 * @cfg {String} autoCreate @hide
25190 * @cfg {String} inputType @hide
25193 * @cfg {String} invalidClass @hide
25196 * @cfg {String} invalidText @hide
25199 * @cfg {String} msgFx @hide
25202 * @cfg {String} validateOnBlur @hide
25206 Roo.HtmlEditorCore.white = [
25207 'area', 'br', 'img', 'input', 'hr', 'wbr',
25209 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25210 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25211 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25212 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25213 'table', 'ul', 'xmp',
25215 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25218 'dir', 'menu', 'ol', 'ul', 'dl',
25224 Roo.HtmlEditorCore.black = [
25225 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25227 'base', 'basefont', 'bgsound', 'blink', 'body',
25228 'frame', 'frameset', 'head', 'html', 'ilayer',
25229 'iframe', 'layer', 'link', 'meta', 'object',
25230 'script', 'style' ,'title', 'xml' // clean later..
25232 Roo.HtmlEditorCore.clean = [
25233 'script', 'style', 'title', 'xml'
25235 Roo.HtmlEditorCore.remove = [
25240 Roo.HtmlEditorCore.ablack = [
25244 Roo.HtmlEditorCore.aclean = [
25245 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25249 Roo.HtmlEditorCore.pwhite= [
25250 'http', 'https', 'mailto'
25253 // white listed style attributes.
25254 Roo.HtmlEditorCore.cwhite= [
25255 // 'text-align', /// default is to allow most things..
25261 // black listed style attributes.
25262 Roo.HtmlEditorCore.cblack= [
25263 // 'font-size' -- this can be set by the project
25267 Roo.HtmlEditorCore.swapCodes =[
25286 * @class Roo.bootstrap.HtmlEditor
25287 * @extends Roo.bootstrap.TextArea
25288 * Bootstrap HtmlEditor class
25291 * Create a new HtmlEditor
25292 * @param {Object} config The config object
25295 Roo.bootstrap.HtmlEditor = function(config){
25296 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25297 if (!this.toolbars) {
25298 this.toolbars = [];
25301 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25304 * @event initialize
25305 * Fires when the editor is fully initialized (including the iframe)
25306 * @param {HtmlEditor} this
25311 * Fires when the editor is first receives the focus. Any insertion must wait
25312 * until after this event.
25313 * @param {HtmlEditor} this
25317 * @event beforesync
25318 * Fires before the textarea is updated with content from the editor iframe. Return false
25319 * to cancel the sync.
25320 * @param {HtmlEditor} this
25321 * @param {String} html
25325 * @event beforepush
25326 * Fires before the iframe editor is updated with content from the textarea. Return false
25327 * to cancel the push.
25328 * @param {HtmlEditor} this
25329 * @param {String} html
25334 * Fires when the textarea is updated with content from the editor iframe.
25335 * @param {HtmlEditor} this
25336 * @param {String} html
25341 * Fires when the iframe editor is updated with content from the textarea.
25342 * @param {HtmlEditor} this
25343 * @param {String} html
25347 * @event editmodechange
25348 * Fires when the editor switches edit modes
25349 * @param {HtmlEditor} this
25350 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25352 editmodechange: true,
25354 * @event editorevent
25355 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25356 * @param {HtmlEditor} this
25360 * @event firstfocus
25361 * Fires when on first focus - needed by toolbars..
25362 * @param {HtmlEditor} this
25367 * Auto save the htmlEditor value as a file into Events
25368 * @param {HtmlEditor} this
25372 * @event savedpreview
25373 * preview the saved version of htmlEditor
25374 * @param {HtmlEditor} this
25381 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25385 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25390 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25395 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25400 * @cfg {Number} height (in pixels)
25404 * @cfg {Number} width (in pixels)
25409 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25412 stylesheets: false,
25417 // private properties
25418 validationEvent : false,
25420 initialized : false,
25423 onFocus : Roo.emptyFn,
25425 hideMode:'offsets',
25427 tbContainer : false,
25431 toolbarContainer :function() {
25432 return this.wrap.select('.x-html-editor-tb',true).first();
25436 * Protected method that will not generally be called directly. It
25437 * is called when the editor creates its toolbar. Override this method if you need to
25438 * add custom toolbar buttons.
25439 * @param {HtmlEditor} editor
25441 createToolbar : function(){
25442 Roo.log('renewing');
25443 Roo.log("create toolbars");
25445 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25446 this.toolbars[0].render(this.toolbarContainer());
25450 // if (!editor.toolbars || !editor.toolbars.length) {
25451 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25454 // for (var i =0 ; i < editor.toolbars.length;i++) {
25455 // editor.toolbars[i] = Roo.factory(
25456 // typeof(editor.toolbars[i]) == 'string' ?
25457 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25458 // Roo.bootstrap.HtmlEditor);
25459 // editor.toolbars[i].init(editor);
25465 onRender : function(ct, position)
25467 // Roo.log("Call onRender: " + this.xtype);
25469 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25471 this.wrap = this.inputEl().wrap({
25472 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25475 this.editorcore.onRender(ct, position);
25477 if (this.resizable) {
25478 this.resizeEl = new Roo.Resizable(this.wrap, {
25482 minHeight : this.height,
25483 height: this.height,
25484 handles : this.resizable,
25487 resize : function(r, w, h) {
25488 _t.onResize(w,h); // -something
25494 this.createToolbar(this);
25497 if(!this.width && this.resizable){
25498 this.setSize(this.wrap.getSize());
25500 if (this.resizeEl) {
25501 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25502 // should trigger onReize..
25508 onResize : function(w, h)
25510 Roo.log('resize: ' +w + ',' + h );
25511 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25515 if(this.inputEl() ){
25516 if(typeof w == 'number'){
25517 var aw = w - this.wrap.getFrameWidth('lr');
25518 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25521 if(typeof h == 'number'){
25522 var tbh = -11; // fixme it needs to tool bar size!
25523 for (var i =0; i < this.toolbars.length;i++) {
25524 // fixme - ask toolbars for heights?
25525 tbh += this.toolbars[i].el.getHeight();
25526 //if (this.toolbars[i].footer) {
25527 // tbh += this.toolbars[i].footer.el.getHeight();
25535 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25536 ah -= 5; // knock a few pixes off for look..
25537 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25541 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25542 this.editorcore.onResize(ew,eh);
25547 * Toggles the editor between standard and source edit mode.
25548 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25550 toggleSourceEdit : function(sourceEditMode)
25552 this.editorcore.toggleSourceEdit(sourceEditMode);
25554 if(this.editorcore.sourceEditMode){
25555 Roo.log('editor - showing textarea');
25558 // Roo.log(this.syncValue());
25560 this.inputEl().removeClass(['hide', 'x-hidden']);
25561 this.inputEl().dom.removeAttribute('tabIndex');
25562 this.inputEl().focus();
25564 Roo.log('editor - hiding textarea');
25566 // Roo.log(this.pushValue());
25569 this.inputEl().addClass(['hide', 'x-hidden']);
25570 this.inputEl().dom.setAttribute('tabIndex', -1);
25571 //this.deferFocus();
25574 if(this.resizable){
25575 this.setSize(this.wrap.getSize());
25578 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25581 // private (for BoxComponent)
25582 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25584 // private (for BoxComponent)
25585 getResizeEl : function(){
25589 // private (for BoxComponent)
25590 getPositionEl : function(){
25595 initEvents : function(){
25596 this.originalValue = this.getValue();
25600 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25603 // markInvalid : Roo.emptyFn,
25605 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25608 // clearInvalid : Roo.emptyFn,
25610 setValue : function(v){
25611 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25612 this.editorcore.pushValue();
25617 deferFocus : function(){
25618 this.focus.defer(10, this);
25622 focus : function(){
25623 this.editorcore.focus();
25629 onDestroy : function(){
25635 for (var i =0; i < this.toolbars.length;i++) {
25636 // fixme - ask toolbars for heights?
25637 this.toolbars[i].onDestroy();
25640 this.wrap.dom.innerHTML = '';
25641 this.wrap.remove();
25646 onFirstFocus : function(){
25647 //Roo.log("onFirstFocus");
25648 this.editorcore.onFirstFocus();
25649 for (var i =0; i < this.toolbars.length;i++) {
25650 this.toolbars[i].onFirstFocus();
25656 syncValue : function()
25658 this.editorcore.syncValue();
25661 pushValue : function()
25663 this.editorcore.pushValue();
25667 // hide stuff that is not compatible
25681 * @event specialkey
25685 * @cfg {String} fieldClass @hide
25688 * @cfg {String} focusClass @hide
25691 * @cfg {String} autoCreate @hide
25694 * @cfg {String} inputType @hide
25698 * @cfg {String} invalidText @hide
25701 * @cfg {String} msgFx @hide
25704 * @cfg {String} validateOnBlur @hide
25713 Roo.namespace('Roo.bootstrap.htmleditor');
25715 * @class Roo.bootstrap.HtmlEditorToolbar1
25721 new Roo.bootstrap.HtmlEditor({
25724 new Roo.bootstrap.HtmlEditorToolbar1({
25725 disable : { fonts: 1 , format: 1, ..., ... , ...],
25731 * @cfg {Object} disable List of elements to disable..
25732 * @cfg {Array} btns List of additional buttons.
25736 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25739 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25742 Roo.apply(this, config);
25744 // default disabled, based on 'good practice'..
25745 this.disable = this.disable || {};
25746 Roo.applyIf(this.disable, {
25749 specialElements : true
25751 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25753 this.editor = config.editor;
25754 this.editorcore = config.editor.editorcore;
25756 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25758 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25759 // dont call parent... till later.
25761 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
25766 editorcore : false,
25771 "h1","h2","h3","h4","h5","h6",
25773 "abbr", "acronym", "address", "cite", "samp", "var",
25777 onRender : function(ct, position)
25779 // Roo.log("Call onRender: " + this.xtype);
25781 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25783 this.el.dom.style.marginBottom = '0';
25785 var editorcore = this.editorcore;
25786 var editor= this.editor;
25789 var btn = function(id,cmd , toggle, handler, html){
25791 var event = toggle ? 'toggle' : 'click';
25796 xns: Roo.bootstrap,
25800 enableToggle:toggle !== false,
25802 pressed : toggle ? false : null,
25805 a.listeners[toggle ? 'toggle' : 'click'] = function() {
25806 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
25812 // var cb_box = function...
25817 xns: Roo.bootstrap,
25822 xns: Roo.bootstrap,
25826 Roo.each(this.formats, function(f) {
25827 style.menu.items.push({
25829 xns: Roo.bootstrap,
25830 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25835 editorcore.insertTag(this.tagname);
25842 children.push(style);
25844 btn('bold',false,true);
25845 btn('italic',false,true);
25846 btn('align-left', 'justifyleft',true);
25847 btn('align-center', 'justifycenter',true);
25848 btn('align-right' , 'justifyright',true);
25849 btn('link', false, false, function(btn) {
25850 //Roo.log("create link?");
25851 var url = prompt(this.createLinkText, this.defaultLinkValue);
25852 if(url && url != 'http:/'+'/'){
25853 this.editorcore.relayCmd('createlink', url);
25856 btn('list','insertunorderedlist',true);
25857 btn('pencil', false,true, function(btn){
25859 this.toggleSourceEdit(btn.pressed);
25862 if (this.editor.btns.length > 0) {
25863 for (var i = 0; i<this.editor.btns.length; i++) {
25864 children.push(this.editor.btns[i]);
25872 xns: Roo.bootstrap,
25877 xns: Roo.bootstrap,
25882 cog.menu.items.push({
25884 xns: Roo.bootstrap,
25885 html : Clean styles,
25890 editorcore.insertTag(this.tagname);
25899 this.xtype = 'NavSimplebar';
25901 for(var i=0;i< children.length;i++) {
25903 this.buttons.add(this.addxtypeChild(children[i]));
25907 editor.on('editorevent', this.updateToolbar, this);
25909 onBtnClick : function(id)
25911 this.editorcore.relayCmd(id);
25912 this.editorcore.focus();
25916 * Protected method that will not generally be called directly. It triggers
25917 * a toolbar update by reading the markup state of the current selection in the editor.
25919 updateToolbar: function(){
25921 if(!this.editorcore.activated){
25922 this.editor.onFirstFocus(); // is this neeed?
25926 var btns = this.buttons;
25927 var doc = this.editorcore.doc;
25928 btns.get('bold').setActive(doc.queryCommandState('bold'));
25929 btns.get('italic').setActive(doc.queryCommandState('italic'));
25930 //btns.get('underline').setActive(doc.queryCommandState('underline'));
25932 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25933 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25934 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25936 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25937 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25940 var ans = this.editorcore.getAllAncestors();
25941 if (this.formatCombo) {
25944 var store = this.formatCombo.store;
25945 this.formatCombo.setValue("");
25946 for (var i =0; i < ans.length;i++) {
25947 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25949 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25957 // hides menus... - so this cant be on a menu...
25958 Roo.bootstrap.MenuMgr.hideAll();
25960 Roo.bootstrap.MenuMgr.hideAll();
25961 //this.editorsyncValue();
25963 onFirstFocus: function() {
25964 this.buttons.each(function(item){
25968 toggleSourceEdit : function(sourceEditMode){
25971 if(sourceEditMode){
25972 Roo.log("disabling buttons");
25973 this.buttons.each( function(item){
25974 if(item.cmd != 'pencil'){
25980 Roo.log("enabling buttons");
25981 if(this.editorcore.initialized){
25982 this.buttons.each( function(item){
25988 Roo.log("calling toggole on editor");
25989 // tell the editor that it's been pressed..
25990 this.editor.toggleSourceEdit(sourceEditMode);
26000 * @class Roo.bootstrap.Table.AbstractSelectionModel
26001 * @extends Roo.util.Observable
26002 * Abstract base class for grid SelectionModels. It provides the interface that should be
26003 * implemented by descendant classes. This class should not be directly instantiated.
26006 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26007 this.locked = false;
26008 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26012 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26013 /** @ignore Called by the grid automatically. Do not call directly. */
26014 init : function(grid){
26020 * Locks the selections.
26023 this.locked = true;
26027 * Unlocks the selections.
26029 unlock : function(){
26030 this.locked = false;
26034 * Returns true if the selections are locked.
26035 * @return {Boolean}
26037 isLocked : function(){
26038 return this.locked;
26042 initEvents : function ()
26048 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26049 * @class Roo.bootstrap.Table.RowSelectionModel
26050 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26051 * It supports multiple selections and keyboard selection/navigation.
26053 * @param {Object} config
26056 Roo.bootstrap.Table.RowSelectionModel = function(config){
26057 Roo.apply(this, config);
26058 this.selections = new Roo.util.MixedCollection(false, function(o){
26063 this.lastActive = false;
26067 * @event selectionchange
26068 * Fires when the selection changes
26069 * @param {SelectionModel} this
26071 "selectionchange" : true,
26073 * @event afterselectionchange
26074 * Fires after the selection changes (eg. by key press or clicking)
26075 * @param {SelectionModel} this
26077 "afterselectionchange" : true,
26079 * @event beforerowselect
26080 * Fires when a row is selected being selected, return false to cancel.
26081 * @param {SelectionModel} this
26082 * @param {Number} rowIndex The selected index
26083 * @param {Boolean} keepExisting False if other selections will be cleared
26085 "beforerowselect" : true,
26088 * Fires when a row is selected.
26089 * @param {SelectionModel} this
26090 * @param {Number} rowIndex The selected index
26091 * @param {Roo.data.Record} r The record
26093 "rowselect" : true,
26095 * @event rowdeselect
26096 * Fires when a row is deselected.
26097 * @param {SelectionModel} this
26098 * @param {Number} rowIndex The selected index
26100 "rowdeselect" : true
26102 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26103 this.locked = false;
26106 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26108 * @cfg {Boolean} singleSelect
26109 * True to allow selection of only one row at a time (defaults to false)
26111 singleSelect : false,
26114 initEvents : function()
26117 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26118 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26119 //}else{ // allow click to work like normal
26120 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26122 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26123 this.grid.on("rowclick", this.handleMouseDown, this);
26125 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26126 "up" : function(e){
26128 this.selectPrevious(e.shiftKey);
26129 }else if(this.last !== false && this.lastActive !== false){
26130 var last = this.last;
26131 this.selectRange(this.last, this.lastActive-1);
26132 this.grid.getView().focusRow(this.lastActive);
26133 if(last !== false){
26137 this.selectFirstRow();
26139 this.fireEvent("afterselectionchange", this);
26141 "down" : function(e){
26143 this.selectNext(e.shiftKey);
26144 }else if(this.last !== false && this.lastActive !== false){
26145 var last = this.last;
26146 this.selectRange(this.last, this.lastActive+1);
26147 this.grid.getView().focusRow(this.lastActive);
26148 if(last !== false){
26152 this.selectFirstRow();
26154 this.fireEvent("afterselectionchange", this);
26158 this.grid.store.on('load', function(){
26159 this.selections.clear();
26162 var view = this.grid.view;
26163 view.on("refresh", this.onRefresh, this);
26164 view.on("rowupdated", this.onRowUpdated, this);
26165 view.on("rowremoved", this.onRemove, this);
26170 onRefresh : function()
26172 var ds = this.grid.store, i, v = this.grid.view;
26173 var s = this.selections;
26174 s.each(function(r){
26175 if((i = ds.indexOfId(r.id)) != -1){
26184 onRemove : function(v, index, r){
26185 this.selections.remove(r);
26189 onRowUpdated : function(v, index, r){
26190 if(this.isSelected(r)){
26191 v.onRowSelect(index);
26197 * @param {Array} records The records to select
26198 * @param {Boolean} keepExisting (optional) True to keep existing selections
26200 selectRecords : function(records, keepExisting)
26203 this.clearSelections();
26205 var ds = this.grid.store;
26206 for(var i = 0, len = records.length; i < len; i++){
26207 this.selectRow(ds.indexOf(records[i]), true);
26212 * Gets the number of selected rows.
26215 getCount : function(){
26216 return this.selections.length;
26220 * Selects the first row in the grid.
26222 selectFirstRow : function(){
26227 * Select the last row.
26228 * @param {Boolean} keepExisting (optional) True to keep existing selections
26230 selectLastRow : function(keepExisting){
26231 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26232 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26236 * Selects the row immediately following the last selected row.
26237 * @param {Boolean} keepExisting (optional) True to keep existing selections
26239 selectNext : function(keepExisting)
26241 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26242 this.selectRow(this.last+1, keepExisting);
26243 this.grid.getView().focusRow(this.last);
26248 * Selects the row that precedes the last selected row.
26249 * @param {Boolean} keepExisting (optional) True to keep existing selections
26251 selectPrevious : function(keepExisting){
26253 this.selectRow(this.last-1, keepExisting);
26254 this.grid.getView().focusRow(this.last);
26259 * Returns the selected records
26260 * @return {Array} Array of selected records
26262 getSelections : function(){
26263 return [].concat(this.selections.items);
26267 * Returns the first selected record.
26270 getSelected : function(){
26271 return this.selections.itemAt(0);
26276 * Clears all selections.
26278 clearSelections : function(fast)
26284 var ds = this.grid.store;
26285 var s = this.selections;
26286 s.each(function(r){
26287 this.deselectRow(ds.indexOfId(r.id));
26291 this.selections.clear();
26298 * Selects all rows.
26300 selectAll : function(){
26304 this.selections.clear();
26305 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26306 this.selectRow(i, true);
26311 * Returns True if there is a selection.
26312 * @return {Boolean}
26314 hasSelection : function(){
26315 return this.selections.length > 0;
26319 * Returns True if the specified row is selected.
26320 * @param {Number/Record} record The record or index of the record to check
26321 * @return {Boolean}
26323 isSelected : function(index){
26324 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26325 return (r && this.selections.key(r.id) ? true : false);
26329 * Returns True if the specified record id is selected.
26330 * @param {String} id The id of record to check
26331 * @return {Boolean}
26333 isIdSelected : function(id){
26334 return (this.selections.key(id) ? true : false);
26339 handleMouseDBClick : function(e, t){
26343 handleMouseDown : function(e, t)
26345 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26346 if(this.isLocked() || rowIndex < 0 ){
26349 if(e.shiftKey && this.last !== false){
26350 var last = this.last;
26351 this.selectRange(last, rowIndex, e.ctrlKey);
26352 this.last = last; // reset the last
26356 var isSelected = this.isSelected(rowIndex);
26357 //Roo.log("select row:" + rowIndex);
26359 this.deselectRow(rowIndex);
26361 this.selectRow(rowIndex, true);
26365 if(e.button !== 0 && isSelected){
26366 alert('rowIndex 2: ' + rowIndex);
26367 view.focusRow(rowIndex);
26368 }else if(e.ctrlKey && isSelected){
26369 this.deselectRow(rowIndex);
26370 }else if(!isSelected){
26371 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26372 view.focusRow(rowIndex);
26376 this.fireEvent("afterselectionchange", this);
26379 handleDragableRowClick : function(grid, rowIndex, e)
26381 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26382 this.selectRow(rowIndex, false);
26383 grid.view.focusRow(rowIndex);
26384 this.fireEvent("afterselectionchange", this);
26389 * Selects multiple rows.
26390 * @param {Array} rows Array of the indexes of the row to select
26391 * @param {Boolean} keepExisting (optional) True to keep existing selections
26393 selectRows : function(rows, keepExisting){
26395 this.clearSelections();
26397 for(var i = 0, len = rows.length; i < len; i++){
26398 this.selectRow(rows[i], true);
26403 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26404 * @param {Number} startRow The index of the first row in the range
26405 * @param {Number} endRow The index of the last row in the range
26406 * @param {Boolean} keepExisting (optional) True to retain existing selections
26408 selectRange : function(startRow, endRow, keepExisting){
26413 this.clearSelections();
26415 if(startRow <= endRow){
26416 for(var i = startRow; i <= endRow; i++){
26417 this.selectRow(i, true);
26420 for(var i = startRow; i >= endRow; i--){
26421 this.selectRow(i, true);
26427 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26428 * @param {Number} startRow The index of the first row in the range
26429 * @param {Number} endRow The index of the last row in the range
26431 deselectRange : function(startRow, endRow, preventViewNotify){
26435 for(var i = startRow; i <= endRow; i++){
26436 this.deselectRow(i, preventViewNotify);
26442 * @param {Number} row The index of the row to select
26443 * @param {Boolean} keepExisting (optional) True to keep existing selections
26445 selectRow : function(index, keepExisting, preventViewNotify)
26447 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26450 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26451 if(!keepExisting || this.singleSelect){
26452 this.clearSelections();
26455 var r = this.grid.store.getAt(index);
26456 //console.log('selectRow - record id :' + r.id);
26458 this.selections.add(r);
26459 this.last = this.lastActive = index;
26460 if(!preventViewNotify){
26461 var proxy = new Roo.Element(
26462 this.grid.getRowDom(index)
26464 proxy.addClass('bg-info info');
26466 this.fireEvent("rowselect", this, index, r);
26467 this.fireEvent("selectionchange", this);
26473 * @param {Number} row The index of the row to deselect
26475 deselectRow : function(index, preventViewNotify)
26480 if(this.last == index){
26483 if(this.lastActive == index){
26484 this.lastActive = false;
26487 var r = this.grid.store.getAt(index);
26492 this.selections.remove(r);
26493 //.console.log('deselectRow - record id :' + r.id);
26494 if(!preventViewNotify){
26496 var proxy = new Roo.Element(
26497 this.grid.getRowDom(index)
26499 proxy.removeClass('bg-info info');
26501 this.fireEvent("rowdeselect", this, index);
26502 this.fireEvent("selectionchange", this);
26506 restoreLast : function(){
26508 this.last = this._last;
26513 acceptsNav : function(row, col, cm){
26514 return !cm.isHidden(col) && cm.isCellEditable(col, row);
26518 onEditorKey : function(field, e){
26519 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26524 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26526 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26528 }else if(k == e.ENTER && !e.ctrlKey){
26532 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26534 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26536 }else if(k == e.ESC){
26540 g.startEditing(newCell[0], newCell[1]);
26546 * Ext JS Library 1.1.1
26547 * Copyright(c) 2006-2007, Ext JS, LLC.
26549 * Originally Released Under LGPL - original licence link has changed is not relivant.
26552 * <script type="text/javascript">
26556 * @class Roo.bootstrap.PagingToolbar
26557 * @extends Roo.bootstrap.NavSimplebar
26558 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26560 * Create a new PagingToolbar
26561 * @param {Object} config The config object
26562 * @param {Roo.data.Store} store
26564 Roo.bootstrap.PagingToolbar = function(config)
26566 // old args format still supported... - xtype is prefered..
26567 // created from xtype...
26569 this.ds = config.dataSource;
26571 if (config.store && !this.ds) {
26572 this.store= Roo.factory(config.store, Roo.data);
26573 this.ds = this.store;
26574 this.ds.xmodule = this.xmodule || false;
26577 this.toolbarItems = [];
26578 if (config.items) {
26579 this.toolbarItems = config.items;
26582 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26587 this.bind(this.ds);
26590 if (Roo.bootstrap.version == 4) {
26591 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26593 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26598 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26600 * @cfg {Roo.data.Store} dataSource
26601 * The underlying data store providing the paged data
26604 * @cfg {String/HTMLElement/Element} container
26605 * container The id or element that will contain the toolbar
26608 * @cfg {Boolean} displayInfo
26609 * True to display the displayMsg (defaults to false)
26612 * @cfg {Number} pageSize
26613 * The number of records to display per page (defaults to 20)
26617 * @cfg {String} displayMsg
26618 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26620 displayMsg : 'Displaying {0} - {1} of {2}',
26622 * @cfg {String} emptyMsg
26623 * The message to display when no records are found (defaults to "No data to display")
26625 emptyMsg : 'No data to display',
26627 * Customizable piece of the default paging text (defaults to "Page")
26630 beforePageText : "Page",
26632 * Customizable piece of the default paging text (defaults to "of %0")
26635 afterPageText : "of {0}",
26637 * Customizable piece of the default paging text (defaults to "First Page")
26640 firstText : "First Page",
26642 * Customizable piece of the default paging text (defaults to "Previous Page")
26645 prevText : "Previous Page",
26647 * Customizable piece of the default paging text (defaults to "Next Page")
26650 nextText : "Next Page",
26652 * Customizable piece of the default paging text (defaults to "Last Page")
26655 lastText : "Last Page",
26657 * Customizable piece of the default paging text (defaults to "Refresh")
26660 refreshText : "Refresh",
26664 onRender : function(ct, position)
26666 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26667 this.navgroup.parentId = this.id;
26668 this.navgroup.onRender(this.el, null);
26669 // add the buttons to the navgroup
26671 if(this.displayInfo){
26672 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26673 this.displayEl = this.el.select('.x-paging-info', true).first();
26674 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26675 // this.displayEl = navel.el.select('span',true).first();
26681 Roo.each(_this.buttons, function(e){ // this might need to use render????
26682 Roo.factory(e).render(_this.el);
26686 Roo.each(_this.toolbarItems, function(e) {
26687 _this.navgroup.addItem(e);
26691 this.first = this.navgroup.addItem({
26692 tooltip: this.firstText,
26693 cls: "prev btn-outline-secondary",
26694 html : ' <i class="fa fa-step-backward"></i>',
26696 preventDefault: true,
26697 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26700 this.prev = this.navgroup.addItem({
26701 tooltip: this.prevText,
26702 cls: "prev btn-outline-secondary",
26703 html : ' <i class="fa fa-backward"></i>',
26705 preventDefault: true,
26706 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
26708 //this.addSeparator();
26711 var field = this.navgroup.addItem( {
26713 cls : 'x-paging-position btn-outline-secondary',
26715 html : this.beforePageText +
26716 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26717 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
26720 this.field = field.el.select('input', true).first();
26721 this.field.on("keydown", this.onPagingKeydown, this);
26722 this.field.on("focus", function(){this.dom.select();});
26725 this.afterTextEl = field.el.select('.x-paging-after',true).first();
26726 //this.field.setHeight(18);
26727 //this.addSeparator();
26728 this.next = this.navgroup.addItem({
26729 tooltip: this.nextText,
26730 cls: "next btn-outline-secondary",
26731 html : ' <i class="fa fa-forward"></i>',
26733 preventDefault: true,
26734 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
26736 this.last = this.navgroup.addItem({
26737 tooltip: this.lastText,
26738 html : ' <i class="fa fa-step-forward"></i>',
26739 cls: "next btn-outline-secondary",
26741 preventDefault: true,
26742 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
26744 //this.addSeparator();
26745 this.loading = this.navgroup.addItem({
26746 tooltip: this.refreshText,
26747 cls: "btn-outline-secondary",
26748 html : ' <i class="fa fa-refresh"></i>',
26749 preventDefault: true,
26750 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26756 updateInfo : function(){
26757 if(this.displayEl){
26758 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26759 var msg = count == 0 ?
26763 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
26765 this.displayEl.update(msg);
26770 onLoad : function(ds, r, o)
26772 this.cursor = o.params.start ? o.params.start : 0;
26774 var d = this.getPageData(),
26779 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26780 this.field.dom.value = ap;
26781 this.first.setDisabled(ap == 1);
26782 this.prev.setDisabled(ap == 1);
26783 this.next.setDisabled(ap == ps);
26784 this.last.setDisabled(ap == ps);
26785 this.loading.enable();
26790 getPageData : function(){
26791 var total = this.ds.getTotalCount();
26794 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26795 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26800 onLoadError : function(){
26801 this.loading.enable();
26805 onPagingKeydown : function(e){
26806 var k = e.getKey();
26807 var d = this.getPageData();
26809 var v = this.field.dom.value, pageNum;
26810 if(!v || isNaN(pageNum = parseInt(v, 10))){
26811 this.field.dom.value = d.activePage;
26814 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26815 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26818 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))
26820 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26821 this.field.dom.value = pageNum;
26822 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26825 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26827 var v = this.field.dom.value, pageNum;
26828 var increment = (e.shiftKey) ? 10 : 1;
26829 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26832 if(!v || isNaN(pageNum = parseInt(v, 10))) {
26833 this.field.dom.value = d.activePage;
26836 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26838 this.field.dom.value = parseInt(v, 10) + increment;
26839 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26840 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26847 beforeLoad : function(){
26849 this.loading.disable();
26854 onClick : function(which){
26863 ds.load({params:{start: 0, limit: this.pageSize}});
26866 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26869 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26872 var total = ds.getTotalCount();
26873 var extra = total % this.pageSize;
26874 var lastStart = extra ? (total - extra) : total-this.pageSize;
26875 ds.load({params:{start: lastStart, limit: this.pageSize}});
26878 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26884 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26885 * @param {Roo.data.Store} store The data store to unbind
26887 unbind : function(ds){
26888 ds.un("beforeload", this.beforeLoad, this);
26889 ds.un("load", this.onLoad, this);
26890 ds.un("loadexception", this.onLoadError, this);
26891 ds.un("remove", this.updateInfo, this);
26892 ds.un("add", this.updateInfo, this);
26893 this.ds = undefined;
26897 * Binds the paging toolbar to the specified {@link Roo.data.Store}
26898 * @param {Roo.data.Store} store The data store to bind
26900 bind : function(ds){
26901 ds.on("beforeload", this.beforeLoad, this);
26902 ds.on("load", this.onLoad, this);
26903 ds.on("loadexception", this.onLoadError, this);
26904 ds.on("remove", this.updateInfo, this);
26905 ds.on("add", this.updateInfo, this);
26916 * @class Roo.bootstrap.MessageBar
26917 * @extends Roo.bootstrap.Component
26918 * Bootstrap MessageBar class
26919 * @cfg {String} html contents of the MessageBar
26920 * @cfg {String} weight (info | success | warning | danger) default info
26921 * @cfg {String} beforeClass insert the bar before the given class
26922 * @cfg {Boolean} closable (true | false) default false
26923 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26926 * Create a new Element
26927 * @param {Object} config The config object
26930 Roo.bootstrap.MessageBar = function(config){
26931 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26934 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
26940 beforeClass: 'bootstrap-sticky-wrap',
26942 getAutoCreate : function(){
26946 cls: 'alert alert-dismissable alert-' + this.weight,
26951 html: this.html || ''
26957 cfg.cls += ' alert-messages-fixed';
26971 onRender : function(ct, position)
26973 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26976 var cfg = Roo.apply({}, this.getAutoCreate());
26980 cfg.cls += ' ' + this.cls;
26983 cfg.style = this.style;
26985 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26987 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26990 this.el.select('>button.close').on('click', this.hide, this);
26996 if (!this.rendered) {
27002 this.fireEvent('show', this);
27008 if (!this.rendered) {
27014 this.fireEvent('hide', this);
27017 update : function()
27019 // var e = this.el.dom.firstChild;
27021 // if(this.closable){
27022 // e = e.nextSibling;
27025 // e.data = this.html || '';
27027 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27043 * @class Roo.bootstrap.Graph
27044 * @extends Roo.bootstrap.Component
27045 * Bootstrap Graph class
27049 @cfg {String} graphtype bar | vbar | pie
27050 @cfg {number} g_x coodinator | centre x (pie)
27051 @cfg {number} g_y coodinator | centre y (pie)
27052 @cfg {number} g_r radius (pie)
27053 @cfg {number} g_height height of the chart (respected by all elements in the set)
27054 @cfg {number} g_width width of the chart (respected by all elements in the set)
27055 @cfg {Object} title The title of the chart
27058 -opts (object) options for the chart
27060 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27061 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27063 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.
27064 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27066 o stretch (boolean)
27068 -opts (object) options for the pie
27071 o startAngle (number)
27072 o endAngle (number)
27076 * Create a new Input
27077 * @param {Object} config The config object
27080 Roo.bootstrap.Graph = function(config){
27081 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27087 * The img click event for the img.
27088 * @param {Roo.EventObject} e
27094 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27105 //g_colors: this.colors,
27112 getAutoCreate : function(){
27123 onRender : function(ct,position){
27126 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27128 if (typeof(Raphael) == 'undefined') {
27129 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27133 this.raphael = Raphael(this.el.dom);
27135 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27136 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27137 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27138 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27140 r.text(160, 10, "Single Series Chart").attr(txtattr);
27141 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27142 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27143 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27145 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27146 r.barchart(330, 10, 300, 220, data1);
27147 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27148 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27151 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27152 // r.barchart(30, 30, 560, 250, xdata, {
27153 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27154 // axis : "0 0 1 1",
27155 // axisxlabels : xdata
27156 // //yvalues : cols,
27159 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27161 // this.load(null,xdata,{
27162 // axis : "0 0 1 1",
27163 // axisxlabels : xdata
27168 load : function(graphtype,xdata,opts)
27170 this.raphael.clear();
27172 graphtype = this.graphtype;
27177 var r = this.raphael,
27178 fin = function () {
27179 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27181 fout = function () {
27182 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27184 pfin = function() {
27185 this.sector.stop();
27186 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27189 this.label[0].stop();
27190 this.label[0].attr({ r: 7.5 });
27191 this.label[1].attr({ "font-weight": 800 });
27194 pfout = function() {
27195 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27198 this.label[0].animate({ r: 5 }, 500, "bounce");
27199 this.label[1].attr({ "font-weight": 400 });
27205 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27208 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27211 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27212 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27214 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27221 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27226 setTitle: function(o)
27231 initEvents: function() {
27234 this.el.on('click', this.onClick, this);
27238 onClick : function(e)
27240 Roo.log('img onclick');
27241 this.fireEvent('click', this, e);
27253 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27256 * @class Roo.bootstrap.dash.NumberBox
27257 * @extends Roo.bootstrap.Component
27258 * Bootstrap NumberBox class
27259 * @cfg {String} headline Box headline
27260 * @cfg {String} content Box content
27261 * @cfg {String} icon Box icon
27262 * @cfg {String} footer Footer text
27263 * @cfg {String} fhref Footer href
27266 * Create a new NumberBox
27267 * @param {Object} config The config object
27271 Roo.bootstrap.dash.NumberBox = function(config){
27272 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27276 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27285 getAutoCreate : function(){
27289 cls : 'small-box ',
27297 cls : 'roo-headline',
27298 html : this.headline
27302 cls : 'roo-content',
27303 html : this.content
27317 cls : 'ion ' + this.icon
27326 cls : 'small-box-footer',
27327 href : this.fhref || '#',
27331 cfg.cn.push(footer);
27338 onRender : function(ct,position){
27339 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27346 setHeadline: function (value)
27348 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27351 setFooter: function (value, href)
27353 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27356 this.el.select('a.small-box-footer',true).first().attr('href', href);
27361 setContent: function (value)
27363 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27366 initEvents: function()
27380 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27383 * @class Roo.bootstrap.dash.TabBox
27384 * @extends Roo.bootstrap.Component
27385 * Bootstrap TabBox class
27386 * @cfg {String} title Title of the TabBox
27387 * @cfg {String} icon Icon of the TabBox
27388 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27389 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27392 * Create a new TabBox
27393 * @param {Object} config The config object
27397 Roo.bootstrap.dash.TabBox = function(config){
27398 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27403 * When a pane is added
27404 * @param {Roo.bootstrap.dash.TabPane} pane
27408 * @event activatepane
27409 * When a pane is activated
27410 * @param {Roo.bootstrap.dash.TabPane} pane
27412 "activatepane" : true
27420 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27425 tabScrollable : false,
27427 getChildContainer : function()
27429 return this.el.select('.tab-content', true).first();
27432 getAutoCreate : function(){
27436 cls: 'pull-left header',
27444 cls: 'fa ' + this.icon
27450 cls: 'nav nav-tabs pull-right',
27456 if(this.tabScrollable){
27463 cls: 'nav nav-tabs pull-right',
27474 cls: 'nav-tabs-custom',
27479 cls: 'tab-content no-padding',
27487 initEvents : function()
27489 //Roo.log('add add pane handler');
27490 this.on('addpane', this.onAddPane, this);
27493 * Updates the box title
27494 * @param {String} html to set the title to.
27496 setTitle : function(value)
27498 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27500 onAddPane : function(pane)
27502 this.panes.push(pane);
27503 //Roo.log('addpane');
27505 // tabs are rendere left to right..
27506 if(!this.showtabs){
27510 var ctr = this.el.select('.nav-tabs', true).first();
27513 var existing = ctr.select('.nav-tab',true);
27514 var qty = existing.getCount();;
27517 var tab = ctr.createChild({
27519 cls : 'nav-tab' + (qty ? '' : ' active'),
27527 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27530 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27532 pane.el.addClass('active');
27537 onTabClick : function(ev,un,ob,pane)
27539 //Roo.log('tab - prev default');
27540 ev.preventDefault();
27543 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27544 pane.tab.addClass('active');
27545 //Roo.log(pane.title);
27546 this.getChildContainer().select('.tab-pane',true).removeClass('active');
27547 // technically we should have a deactivate event.. but maybe add later.
27548 // and it should not de-activate the selected tab...
27549 this.fireEvent('activatepane', pane);
27550 pane.el.addClass('active');
27551 pane.fireEvent('activate');
27556 getActivePane : function()
27559 Roo.each(this.panes, function(p) {
27560 if(p.el.hasClass('active')){
27581 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27583 * @class Roo.bootstrap.TabPane
27584 * @extends Roo.bootstrap.Component
27585 * Bootstrap TabPane class
27586 * @cfg {Boolean} active (false | true) Default false
27587 * @cfg {String} title title of panel
27591 * Create a new TabPane
27592 * @param {Object} config The config object
27595 Roo.bootstrap.dash.TabPane = function(config){
27596 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27602 * When a pane is activated
27603 * @param {Roo.bootstrap.dash.TabPane} pane
27610 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27615 // the tabBox that this is attached to.
27618 getAutoCreate : function()
27626 cfg.cls += ' active';
27631 initEvents : function()
27633 //Roo.log('trigger add pane handler');
27634 this.parent().fireEvent('addpane', this)
27638 * Updates the tab title
27639 * @param {String} html to set the title to.
27641 setTitle: function(str)
27647 this.tab.select('a', true).first().dom.innerHTML = str;
27664 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27667 * @class Roo.bootstrap.menu.Menu
27668 * @extends Roo.bootstrap.Component
27669 * Bootstrap Menu class - container for Menu
27670 * @cfg {String} html Text of the menu
27671 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27672 * @cfg {String} icon Font awesome icon
27673 * @cfg {String} pos Menu align to (top | bottom) default bottom
27677 * Create a new Menu
27678 * @param {Object} config The config object
27682 Roo.bootstrap.menu.Menu = function(config){
27683 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27687 * @event beforeshow
27688 * Fires before this menu is displayed
27689 * @param {Roo.bootstrap.menu.Menu} this
27693 * @event beforehide
27694 * Fires before this menu is hidden
27695 * @param {Roo.bootstrap.menu.Menu} this
27700 * Fires after this menu is displayed
27701 * @param {Roo.bootstrap.menu.Menu} this
27706 * Fires after this menu is hidden
27707 * @param {Roo.bootstrap.menu.Menu} this
27712 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27713 * @param {Roo.bootstrap.menu.Menu} this
27714 * @param {Roo.EventObject} e
27721 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
27725 weight : 'default',
27730 getChildContainer : function() {
27731 if(this.isSubMenu){
27735 return this.el.select('ul.dropdown-menu', true).first();
27738 getAutoCreate : function()
27743 cls : 'roo-menu-text',
27751 cls : 'fa ' + this.icon
27762 cls : 'dropdown-button btn btn-' + this.weight,
27767 cls : 'dropdown-toggle btn btn-' + this.weight,
27777 cls : 'dropdown-menu'
27783 if(this.pos == 'top'){
27784 cfg.cls += ' dropup';
27787 if(this.isSubMenu){
27790 cls : 'dropdown-menu'
27797 onRender : function(ct, position)
27799 this.isSubMenu = ct.hasClass('dropdown-submenu');
27801 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27804 initEvents : function()
27806 if(this.isSubMenu){
27810 this.hidden = true;
27812 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27813 this.triggerEl.on('click', this.onTriggerPress, this);
27815 this.buttonEl = this.el.select('button.dropdown-button', true).first();
27816 this.buttonEl.on('click', this.onClick, this);
27822 if(this.isSubMenu){
27826 return this.el.select('ul.dropdown-menu', true).first();
27829 onClick : function(e)
27831 this.fireEvent("click", this, e);
27834 onTriggerPress : function(e)
27836 if (this.isVisible()) {
27843 isVisible : function(){
27844 return !this.hidden;
27849 this.fireEvent("beforeshow", this);
27851 this.hidden = false;
27852 this.el.addClass('open');
27854 Roo.get(document).on("mouseup", this.onMouseUp, this);
27856 this.fireEvent("show", this);
27863 this.fireEvent("beforehide", this);
27865 this.hidden = true;
27866 this.el.removeClass('open');
27868 Roo.get(document).un("mouseup", this.onMouseUp);
27870 this.fireEvent("hide", this);
27873 onMouseUp : function()
27887 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27890 * @class Roo.bootstrap.menu.Item
27891 * @extends Roo.bootstrap.Component
27892 * Bootstrap MenuItem class
27893 * @cfg {Boolean} submenu (true | false) default false
27894 * @cfg {String} html text of the item
27895 * @cfg {String} href the link
27896 * @cfg {Boolean} disable (true | false) default false
27897 * @cfg {Boolean} preventDefault (true | false) default true
27898 * @cfg {String} icon Font awesome icon
27899 * @cfg {String} pos Submenu align to (left | right) default right
27903 * Create a new Item
27904 * @param {Object} config The config object
27908 Roo.bootstrap.menu.Item = function(config){
27909 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27913 * Fires when the mouse is hovering over this menu
27914 * @param {Roo.bootstrap.menu.Item} this
27915 * @param {Roo.EventObject} e
27920 * Fires when the mouse exits this menu
27921 * @param {Roo.bootstrap.menu.Item} this
27922 * @param {Roo.EventObject} e
27928 * The raw click event for the entire grid.
27929 * @param {Roo.EventObject} e
27935 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
27940 preventDefault: true,
27945 getAutoCreate : function()
27950 cls : 'roo-menu-item-text',
27958 cls : 'fa ' + this.icon
27967 href : this.href || '#',
27974 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27978 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27980 if(this.pos == 'left'){
27981 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27988 initEvents : function()
27990 this.el.on('mouseover', this.onMouseOver, this);
27991 this.el.on('mouseout', this.onMouseOut, this);
27993 this.el.select('a', true).first().on('click', this.onClick, this);
27997 onClick : function(e)
27999 if(this.preventDefault){
28000 e.preventDefault();
28003 this.fireEvent("click", this, e);
28006 onMouseOver : function(e)
28008 if(this.submenu && this.pos == 'left'){
28009 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28012 this.fireEvent("mouseover", this, e);
28015 onMouseOut : function(e)
28017 this.fireEvent("mouseout", this, e);
28029 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28032 * @class Roo.bootstrap.menu.Separator
28033 * @extends Roo.bootstrap.Component
28034 * Bootstrap Separator class
28037 * Create a new Separator
28038 * @param {Object} config The config object
28042 Roo.bootstrap.menu.Separator = function(config){
28043 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28046 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28048 getAutoCreate : function(){
28069 * @class Roo.bootstrap.Tooltip
28070 * Bootstrap Tooltip class
28071 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28072 * to determine which dom element triggers the tooltip.
28074 * It needs to add support for additional attributes like tooltip-position
28077 * Create a new Toolti
28078 * @param {Object} config The config object
28081 Roo.bootstrap.Tooltip = function(config){
28082 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28084 this.alignment = Roo.bootstrap.Tooltip.alignment;
28086 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28087 this.alignment = config.alignment;
28092 Roo.apply(Roo.bootstrap.Tooltip, {
28094 * @function init initialize tooltip monitoring.
28098 currentTip : false,
28099 currentRegion : false,
28105 Roo.get(document).on('mouseover', this.enter ,this);
28106 Roo.get(document).on('mouseout', this.leave, this);
28109 this.currentTip = new Roo.bootstrap.Tooltip();
28112 enter : function(ev)
28114 var dom = ev.getTarget();
28116 //Roo.log(['enter',dom]);
28117 var el = Roo.fly(dom);
28118 if (this.currentEl) {
28120 //Roo.log(this.currentEl);
28121 //Roo.log(this.currentEl.contains(dom));
28122 if (this.currentEl == el) {
28125 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28131 if (this.currentTip.el) {
28132 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28136 if(!el || el.dom == document){
28142 // you can not look for children, as if el is the body.. then everythign is the child..
28143 if (!el.attr('tooltip')) { //
28144 if (!el.select("[tooltip]").elements.length) {
28147 // is the mouse over this child...?
28148 bindEl = el.select("[tooltip]").first();
28149 var xy = ev.getXY();
28150 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28151 //Roo.log("not in region.");
28154 //Roo.log("child element over..");
28157 this.currentEl = bindEl;
28158 this.currentTip.bind(bindEl);
28159 this.currentRegion = Roo.lib.Region.getRegion(dom);
28160 this.currentTip.enter();
28163 leave : function(ev)
28165 var dom = ev.getTarget();
28166 //Roo.log(['leave',dom]);
28167 if (!this.currentEl) {
28172 if (dom != this.currentEl.dom) {
28175 var xy = ev.getXY();
28176 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28179 // only activate leave if mouse cursor is outside... bounding box..
28184 if (this.currentTip) {
28185 this.currentTip.leave();
28187 //Roo.log('clear currentEl');
28188 this.currentEl = false;
28193 'left' : ['r-l', [-2,0], 'right'],
28194 'right' : ['l-r', [2,0], 'left'],
28195 'bottom' : ['t-b', [0,2], 'top'],
28196 'top' : [ 'b-t', [0,-2], 'bottom']
28202 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28207 delay : null, // can be { show : 300 , hide: 500}
28211 hoverState : null, //???
28213 placement : 'bottom',
28217 getAutoCreate : function(){
28224 cls : 'tooltip-arrow'
28227 cls : 'tooltip-inner'
28234 bind : function(el)
28240 enter : function () {
28242 if (this.timeout != null) {
28243 clearTimeout(this.timeout);
28246 this.hoverState = 'in';
28247 //Roo.log("enter - show");
28248 if (!this.delay || !this.delay.show) {
28253 this.timeout = setTimeout(function () {
28254 if (_t.hoverState == 'in') {
28257 }, this.delay.show);
28261 clearTimeout(this.timeout);
28263 this.hoverState = 'out';
28264 if (!this.delay || !this.delay.hide) {
28270 this.timeout = setTimeout(function () {
28271 //Roo.log("leave - timeout");
28273 if (_t.hoverState == 'out') {
28275 Roo.bootstrap.Tooltip.currentEl = false;
28280 show : function (msg)
28283 this.render(document.body);
28286 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28288 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28290 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28292 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
28294 var placement = typeof this.placement == 'function' ?
28295 this.placement.call(this, this.el, on_el) :
28298 var autoToken = /\s?auto?\s?/i;
28299 var autoPlace = autoToken.test(placement);
28301 placement = placement.replace(autoToken, '') || 'top';
28305 //this.el.setXY([0,0]);
28307 //this.el.dom.style.display='block';
28309 //this.el.appendTo(on_el);
28311 var p = this.getPosition();
28312 var box = this.el.getBox();
28318 var align = this.alignment[placement];
28320 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28322 if(placement == 'top' || placement == 'bottom'){
28324 placement = 'right';
28327 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28328 placement = 'left';
28331 var scroll = Roo.select('body', true).first().getScroll();
28333 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28337 align = this.alignment[placement];
28340 this.el.alignTo(this.bindEl, align[0],align[1]);
28341 //var arrow = this.el.select('.arrow',true).first();
28342 //arrow.set(align[2],
28344 this.el.addClass(placement);
28346 this.el.addClass('in fade');
28348 this.hoverState = null;
28350 if (this.el.hasClass('fade')) {
28361 //this.el.setXY([0,0]);
28362 this.el.removeClass('in');
28378 * @class Roo.bootstrap.LocationPicker
28379 * @extends Roo.bootstrap.Component
28380 * Bootstrap LocationPicker class
28381 * @cfg {Number} latitude Position when init default 0
28382 * @cfg {Number} longitude Position when init default 0
28383 * @cfg {Number} zoom default 15
28384 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28385 * @cfg {Boolean} mapTypeControl default false
28386 * @cfg {Boolean} disableDoubleClickZoom default false
28387 * @cfg {Boolean} scrollwheel default true
28388 * @cfg {Boolean} streetViewControl default false
28389 * @cfg {Number} radius default 0
28390 * @cfg {String} locationName
28391 * @cfg {Boolean} draggable default true
28392 * @cfg {Boolean} enableAutocomplete default false
28393 * @cfg {Boolean} enableReverseGeocode default true
28394 * @cfg {String} markerTitle
28397 * Create a new LocationPicker
28398 * @param {Object} config The config object
28402 Roo.bootstrap.LocationPicker = function(config){
28404 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28409 * Fires when the picker initialized.
28410 * @param {Roo.bootstrap.LocationPicker} this
28411 * @param {Google Location} location
28415 * @event positionchanged
28416 * Fires when the picker position changed.
28417 * @param {Roo.bootstrap.LocationPicker} this
28418 * @param {Google Location} location
28420 positionchanged : true,
28423 * Fires when the map resize.
28424 * @param {Roo.bootstrap.LocationPicker} this
28429 * Fires when the map show.
28430 * @param {Roo.bootstrap.LocationPicker} this
28435 * Fires when the map hide.
28436 * @param {Roo.bootstrap.LocationPicker} this
28441 * Fires when click the map.
28442 * @param {Roo.bootstrap.LocationPicker} this
28443 * @param {Map event} e
28447 * @event mapRightClick
28448 * Fires when right click the map.
28449 * @param {Roo.bootstrap.LocationPicker} this
28450 * @param {Map event} e
28452 mapRightClick : true,
28454 * @event markerClick
28455 * Fires when click the marker.
28456 * @param {Roo.bootstrap.LocationPicker} this
28457 * @param {Map event} e
28459 markerClick : true,
28461 * @event markerRightClick
28462 * Fires when right click the marker.
28463 * @param {Roo.bootstrap.LocationPicker} this
28464 * @param {Map event} e
28466 markerRightClick : true,
28468 * @event OverlayViewDraw
28469 * Fires when OverlayView Draw
28470 * @param {Roo.bootstrap.LocationPicker} this
28472 OverlayViewDraw : true,
28474 * @event OverlayViewOnAdd
28475 * Fires when OverlayView Draw
28476 * @param {Roo.bootstrap.LocationPicker} this
28478 OverlayViewOnAdd : true,
28480 * @event OverlayViewOnRemove
28481 * Fires when OverlayView Draw
28482 * @param {Roo.bootstrap.LocationPicker} this
28484 OverlayViewOnRemove : true,
28486 * @event OverlayViewShow
28487 * Fires when OverlayView Draw
28488 * @param {Roo.bootstrap.LocationPicker} this
28489 * @param {Pixel} cpx
28491 OverlayViewShow : true,
28493 * @event OverlayViewHide
28494 * Fires when OverlayView Draw
28495 * @param {Roo.bootstrap.LocationPicker} this
28497 OverlayViewHide : true,
28499 * @event loadexception
28500 * Fires when load google lib failed.
28501 * @param {Roo.bootstrap.LocationPicker} this
28503 loadexception : true
28508 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
28510 gMapContext: false,
28516 mapTypeControl: false,
28517 disableDoubleClickZoom: false,
28519 streetViewControl: false,
28523 enableAutocomplete: false,
28524 enableReverseGeocode: true,
28527 getAutoCreate: function()
28532 cls: 'roo-location-picker'
28538 initEvents: function(ct, position)
28540 if(!this.el.getWidth() || this.isApplied()){
28544 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28549 initial: function()
28551 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28552 this.fireEvent('loadexception', this);
28556 if(!this.mapTypeId){
28557 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28560 this.gMapContext = this.GMapContext();
28562 this.initOverlayView();
28564 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28568 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28569 _this.setPosition(_this.gMapContext.marker.position);
28572 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28573 _this.fireEvent('mapClick', this, event);
28577 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28578 _this.fireEvent('mapRightClick', this, event);
28582 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28583 _this.fireEvent('markerClick', this, event);
28587 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28588 _this.fireEvent('markerRightClick', this, event);
28592 this.setPosition(this.gMapContext.location);
28594 this.fireEvent('initial', this, this.gMapContext.location);
28597 initOverlayView: function()
28601 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28605 _this.fireEvent('OverlayViewDraw', _this);
28610 _this.fireEvent('OverlayViewOnAdd', _this);
28613 onRemove: function()
28615 _this.fireEvent('OverlayViewOnRemove', _this);
28618 show: function(cpx)
28620 _this.fireEvent('OverlayViewShow', _this, cpx);
28625 _this.fireEvent('OverlayViewHide', _this);
28631 fromLatLngToContainerPixel: function(event)
28633 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28636 isApplied: function()
28638 return this.getGmapContext() == false ? false : true;
28641 getGmapContext: function()
28643 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28646 GMapContext: function()
28648 var position = new google.maps.LatLng(this.latitude, this.longitude);
28650 var _map = new google.maps.Map(this.el.dom, {
28653 mapTypeId: this.mapTypeId,
28654 mapTypeControl: this.mapTypeControl,
28655 disableDoubleClickZoom: this.disableDoubleClickZoom,
28656 scrollwheel: this.scrollwheel,
28657 streetViewControl: this.streetViewControl,
28658 locationName: this.locationName,
28659 draggable: this.draggable,
28660 enableAutocomplete: this.enableAutocomplete,
28661 enableReverseGeocode: this.enableReverseGeocode
28664 var _marker = new google.maps.Marker({
28665 position: position,
28667 title: this.markerTitle,
28668 draggable: this.draggable
28675 location: position,
28676 radius: this.radius,
28677 locationName: this.locationName,
28678 addressComponents: {
28679 formatted_address: null,
28680 addressLine1: null,
28681 addressLine2: null,
28683 streetNumber: null,
28687 stateOrProvince: null
28690 domContainer: this.el.dom,
28691 geodecoder: new google.maps.Geocoder()
28695 drawCircle: function(center, radius, options)
28697 if (this.gMapContext.circle != null) {
28698 this.gMapContext.circle.setMap(null);
28702 options = Roo.apply({}, options, {
28703 strokeColor: "#0000FF",
28704 strokeOpacity: .35,
28706 fillColor: "#0000FF",
28710 options.map = this.gMapContext.map;
28711 options.radius = radius;
28712 options.center = center;
28713 this.gMapContext.circle = new google.maps.Circle(options);
28714 return this.gMapContext.circle;
28720 setPosition: function(location)
28722 this.gMapContext.location = location;
28723 this.gMapContext.marker.setPosition(location);
28724 this.gMapContext.map.panTo(location);
28725 this.drawCircle(location, this.gMapContext.radius, {});
28729 if (this.gMapContext.settings.enableReverseGeocode) {
28730 this.gMapContext.geodecoder.geocode({
28731 latLng: this.gMapContext.location
28732 }, function(results, status) {
28734 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28735 _this.gMapContext.locationName = results[0].formatted_address;
28736 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28738 _this.fireEvent('positionchanged', this, location);
28745 this.fireEvent('positionchanged', this, location);
28750 google.maps.event.trigger(this.gMapContext.map, "resize");
28752 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28754 this.fireEvent('resize', this);
28757 setPositionByLatLng: function(latitude, longitude)
28759 this.setPosition(new google.maps.LatLng(latitude, longitude));
28762 getCurrentPosition: function()
28765 latitude: this.gMapContext.location.lat(),
28766 longitude: this.gMapContext.location.lng()
28770 getAddressName: function()
28772 return this.gMapContext.locationName;
28775 getAddressComponents: function()
28777 return this.gMapContext.addressComponents;
28780 address_component_from_google_geocode: function(address_components)
28784 for (var i = 0; i < address_components.length; i++) {
28785 var component = address_components[i];
28786 if (component.types.indexOf("postal_code") >= 0) {
28787 result.postalCode = component.short_name;
28788 } else if (component.types.indexOf("street_number") >= 0) {
28789 result.streetNumber = component.short_name;
28790 } else if (component.types.indexOf("route") >= 0) {
28791 result.streetName = component.short_name;
28792 } else if (component.types.indexOf("neighborhood") >= 0) {
28793 result.city = component.short_name;
28794 } else if (component.types.indexOf("locality") >= 0) {
28795 result.city = component.short_name;
28796 } else if (component.types.indexOf("sublocality") >= 0) {
28797 result.district = component.short_name;
28798 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28799 result.stateOrProvince = component.short_name;
28800 } else if (component.types.indexOf("country") >= 0) {
28801 result.country = component.short_name;
28805 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28806 result.addressLine2 = "";
28810 setZoomLevel: function(zoom)
28812 this.gMapContext.map.setZoom(zoom);
28825 this.fireEvent('show', this);
28836 this.fireEvent('hide', this);
28841 Roo.apply(Roo.bootstrap.LocationPicker, {
28843 OverlayView : function(map, options)
28845 options = options || {};
28852 * @class Roo.bootstrap.Alert
28853 * @extends Roo.bootstrap.Component
28854 * Bootstrap Alert class - shows an alert area box
28856 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28857 Enter a valid email address
28860 * @cfg {String} title The title of alert
28861 * @cfg {String} html The content of alert
28862 * @cfg {String} weight ( success | info | warning | danger )
28863 * @cfg {String} faicon font-awesomeicon
28866 * Create a new alert
28867 * @param {Object} config The config object
28871 Roo.bootstrap.Alert = function(config){
28872 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28876 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
28883 getAutoCreate : function()
28892 cls : 'roo-alert-icon'
28897 cls : 'roo-alert-title',
28902 cls : 'roo-alert-text',
28909 cfg.cn[0].cls += ' fa ' + this.faicon;
28913 cfg.cls += ' alert-' + this.weight;
28919 initEvents: function()
28921 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28924 setTitle : function(str)
28926 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28929 setText : function(str)
28931 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28934 setWeight : function(weight)
28937 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28940 this.weight = weight;
28942 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28945 setIcon : function(icon)
28948 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28951 this.faicon = icon;
28953 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28974 * @class Roo.bootstrap.UploadCropbox
28975 * @extends Roo.bootstrap.Component
28976 * Bootstrap UploadCropbox class
28977 * @cfg {String} emptyText show when image has been loaded
28978 * @cfg {String} rotateNotify show when image too small to rotate
28979 * @cfg {Number} errorTimeout default 3000
28980 * @cfg {Number} minWidth default 300
28981 * @cfg {Number} minHeight default 300
28982 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28983 * @cfg {Boolean} isDocument (true|false) default false
28984 * @cfg {String} url action url
28985 * @cfg {String} paramName default 'imageUpload'
28986 * @cfg {String} method default POST
28987 * @cfg {Boolean} loadMask (true|false) default true
28988 * @cfg {Boolean} loadingText default 'Loading...'
28991 * Create a new UploadCropbox
28992 * @param {Object} config The config object
28995 Roo.bootstrap.UploadCropbox = function(config){
28996 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29000 * @event beforeselectfile
29001 * Fire before select file
29002 * @param {Roo.bootstrap.UploadCropbox} this
29004 "beforeselectfile" : true,
29007 * Fire after initEvent
29008 * @param {Roo.bootstrap.UploadCropbox} this
29013 * Fire after initEvent
29014 * @param {Roo.bootstrap.UploadCropbox} this
29015 * @param {String} data
29020 * Fire when preparing the file data
29021 * @param {Roo.bootstrap.UploadCropbox} this
29022 * @param {Object} file
29027 * Fire when get exception
29028 * @param {Roo.bootstrap.UploadCropbox} this
29029 * @param {XMLHttpRequest} xhr
29031 "exception" : true,
29033 * @event beforeloadcanvas
29034 * Fire before load the canvas
29035 * @param {Roo.bootstrap.UploadCropbox} this
29036 * @param {String} src
29038 "beforeloadcanvas" : true,
29041 * Fire when trash image
29042 * @param {Roo.bootstrap.UploadCropbox} this
29047 * Fire when download the image
29048 * @param {Roo.bootstrap.UploadCropbox} this
29052 * @event footerbuttonclick
29053 * Fire when footerbuttonclick
29054 * @param {Roo.bootstrap.UploadCropbox} this
29055 * @param {String} type
29057 "footerbuttonclick" : true,
29061 * @param {Roo.bootstrap.UploadCropbox} this
29066 * Fire when rotate the image
29067 * @param {Roo.bootstrap.UploadCropbox} this
29068 * @param {String} pos
29073 * Fire when inspect the file
29074 * @param {Roo.bootstrap.UploadCropbox} this
29075 * @param {Object} file
29080 * Fire when xhr upload the file
29081 * @param {Roo.bootstrap.UploadCropbox} this
29082 * @param {Object} data
29087 * Fire when arrange the file data
29088 * @param {Roo.bootstrap.UploadCropbox} this
29089 * @param {Object} formData
29094 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29097 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29099 emptyText : 'Click to upload image',
29100 rotateNotify : 'Image is too small to rotate',
29101 errorTimeout : 3000,
29115 cropType : 'image/jpeg',
29117 canvasLoaded : false,
29118 isDocument : false,
29120 paramName : 'imageUpload',
29122 loadingText : 'Loading...',
29125 getAutoCreate : function()
29129 cls : 'roo-upload-cropbox',
29133 cls : 'roo-upload-cropbox-selector',
29138 cls : 'roo-upload-cropbox-body',
29139 style : 'cursor:pointer',
29143 cls : 'roo-upload-cropbox-preview'
29147 cls : 'roo-upload-cropbox-thumb'
29151 cls : 'roo-upload-cropbox-empty-notify',
29152 html : this.emptyText
29156 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29157 html : this.rotateNotify
29163 cls : 'roo-upload-cropbox-footer',
29166 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29176 onRender : function(ct, position)
29178 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29180 if (this.buttons.length) {
29182 Roo.each(this.buttons, function(bb) {
29184 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29186 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29192 this.maskEl = this.el;
29196 initEvents : function()
29198 this.urlAPI = (window.createObjectURL && window) ||
29199 (window.URL && URL.revokeObjectURL && URL) ||
29200 (window.webkitURL && webkitURL);
29202 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29203 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29205 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29206 this.selectorEl.hide();
29208 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29209 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29211 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29212 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29213 this.thumbEl.hide();
29215 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29216 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29218 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29219 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29220 this.errorEl.hide();
29222 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29223 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29224 this.footerEl.hide();
29226 this.setThumbBoxSize();
29232 this.fireEvent('initial', this);
29239 window.addEventListener("resize", function() { _this.resize(); } );
29241 this.bodyEl.on('click', this.beforeSelectFile, this);
29244 this.bodyEl.on('touchstart', this.onTouchStart, this);
29245 this.bodyEl.on('touchmove', this.onTouchMove, this);
29246 this.bodyEl.on('touchend', this.onTouchEnd, this);
29250 this.bodyEl.on('mousedown', this.onMouseDown, this);
29251 this.bodyEl.on('mousemove', this.onMouseMove, this);
29252 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29253 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29254 Roo.get(document).on('mouseup', this.onMouseUp, this);
29257 this.selectorEl.on('change', this.onFileSelected, this);
29263 this.baseScale = 1;
29265 this.baseRotate = 1;
29266 this.dragable = false;
29267 this.pinching = false;
29270 this.cropData = false;
29271 this.notifyEl.dom.innerHTML = this.emptyText;
29273 this.selectorEl.dom.value = '';
29277 resize : function()
29279 if(this.fireEvent('resize', this) != false){
29280 this.setThumbBoxPosition();
29281 this.setCanvasPosition();
29285 onFooterButtonClick : function(e, el, o, type)
29288 case 'rotate-left' :
29289 this.onRotateLeft(e);
29291 case 'rotate-right' :
29292 this.onRotateRight(e);
29295 this.beforeSelectFile(e);
29310 this.fireEvent('footerbuttonclick', this, type);
29313 beforeSelectFile : function(e)
29315 e.preventDefault();
29317 if(this.fireEvent('beforeselectfile', this) != false){
29318 this.selectorEl.dom.click();
29322 onFileSelected : function(e)
29324 e.preventDefault();
29326 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29330 var file = this.selectorEl.dom.files[0];
29332 if(this.fireEvent('inspect', this, file) != false){
29333 this.prepare(file);
29338 trash : function(e)
29340 this.fireEvent('trash', this);
29343 download : function(e)
29345 this.fireEvent('download', this);
29348 loadCanvas : function(src)
29350 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29354 this.imageEl = document.createElement('img');
29358 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29360 this.imageEl.src = src;
29364 onLoadCanvas : function()
29366 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29367 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29369 this.bodyEl.un('click', this.beforeSelectFile, this);
29371 this.notifyEl.hide();
29372 this.thumbEl.show();
29373 this.footerEl.show();
29375 this.baseRotateLevel();
29377 if(this.isDocument){
29378 this.setThumbBoxSize();
29381 this.setThumbBoxPosition();
29383 this.baseScaleLevel();
29389 this.canvasLoaded = true;
29392 this.maskEl.unmask();
29397 setCanvasPosition : function()
29399 if(!this.canvasEl){
29403 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29404 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29406 this.previewEl.setLeft(pw);
29407 this.previewEl.setTop(ph);
29411 onMouseDown : function(e)
29415 this.dragable = true;
29416 this.pinching = false;
29418 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29419 this.dragable = false;
29423 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29424 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29428 onMouseMove : function(e)
29432 if(!this.canvasLoaded){
29436 if (!this.dragable){
29440 var minX = Math.ceil(this.thumbEl.getLeft(true));
29441 var minY = Math.ceil(this.thumbEl.getTop(true));
29443 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29444 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29446 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29447 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29449 x = x - this.mouseX;
29450 y = y - this.mouseY;
29452 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29453 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29455 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29456 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29458 this.previewEl.setLeft(bgX);
29459 this.previewEl.setTop(bgY);
29461 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29462 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29465 onMouseUp : function(e)
29469 this.dragable = false;
29472 onMouseWheel : function(e)
29476 this.startScale = this.scale;
29478 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29480 if(!this.zoomable()){
29481 this.scale = this.startScale;
29490 zoomable : function()
29492 var minScale = this.thumbEl.getWidth() / this.minWidth;
29494 if(this.minWidth < this.minHeight){
29495 minScale = this.thumbEl.getHeight() / this.minHeight;
29498 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29499 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29503 (this.rotate == 0 || this.rotate == 180) &&
29505 width > this.imageEl.OriginWidth ||
29506 height > this.imageEl.OriginHeight ||
29507 (width < this.minWidth && height < this.minHeight)
29515 (this.rotate == 90 || this.rotate == 270) &&
29517 width > this.imageEl.OriginWidth ||
29518 height > this.imageEl.OriginHeight ||
29519 (width < this.minHeight && height < this.minWidth)
29526 !this.isDocument &&
29527 (this.rotate == 0 || this.rotate == 180) &&
29529 width < this.minWidth ||
29530 width > this.imageEl.OriginWidth ||
29531 height < this.minHeight ||
29532 height > this.imageEl.OriginHeight
29539 !this.isDocument &&
29540 (this.rotate == 90 || this.rotate == 270) &&
29542 width < this.minHeight ||
29543 width > this.imageEl.OriginWidth ||
29544 height < this.minWidth ||
29545 height > this.imageEl.OriginHeight
29555 onRotateLeft : function(e)
29557 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29559 var minScale = this.thumbEl.getWidth() / this.minWidth;
29561 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29562 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29564 this.startScale = this.scale;
29566 while (this.getScaleLevel() < minScale){
29568 this.scale = this.scale + 1;
29570 if(!this.zoomable()){
29575 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29576 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29581 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29588 this.scale = this.startScale;
29590 this.onRotateFail();
29595 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29597 if(this.isDocument){
29598 this.setThumbBoxSize();
29599 this.setThumbBoxPosition();
29600 this.setCanvasPosition();
29605 this.fireEvent('rotate', this, 'left');
29609 onRotateRight : function(e)
29611 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29613 var minScale = this.thumbEl.getWidth() / this.minWidth;
29615 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29616 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29618 this.startScale = this.scale;
29620 while (this.getScaleLevel() < minScale){
29622 this.scale = this.scale + 1;
29624 if(!this.zoomable()){
29629 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29630 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29635 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29642 this.scale = this.startScale;
29644 this.onRotateFail();
29649 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29651 if(this.isDocument){
29652 this.setThumbBoxSize();
29653 this.setThumbBoxPosition();
29654 this.setCanvasPosition();
29659 this.fireEvent('rotate', this, 'right');
29662 onRotateFail : function()
29664 this.errorEl.show(true);
29668 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29673 this.previewEl.dom.innerHTML = '';
29675 var canvasEl = document.createElement("canvas");
29677 var contextEl = canvasEl.getContext("2d");
29679 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29680 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29681 var center = this.imageEl.OriginWidth / 2;
29683 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29684 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29685 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29686 center = this.imageEl.OriginHeight / 2;
29689 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29691 contextEl.translate(center, center);
29692 contextEl.rotate(this.rotate * Math.PI / 180);
29694 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29696 this.canvasEl = document.createElement("canvas");
29698 this.contextEl = this.canvasEl.getContext("2d");
29700 switch (this.rotate) {
29703 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29704 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29706 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29711 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29712 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29714 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29715 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);
29719 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29724 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29725 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29727 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29728 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);
29732 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);
29737 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29738 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29740 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29741 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29745 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);
29752 this.previewEl.appendChild(this.canvasEl);
29754 this.setCanvasPosition();
29759 if(!this.canvasLoaded){
29763 var imageCanvas = document.createElement("canvas");
29765 var imageContext = imageCanvas.getContext("2d");
29767 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29768 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29770 var center = imageCanvas.width / 2;
29772 imageContext.translate(center, center);
29774 imageContext.rotate(this.rotate * Math.PI / 180);
29776 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29778 var canvas = document.createElement("canvas");
29780 var context = canvas.getContext("2d");
29782 canvas.width = this.minWidth;
29783 canvas.height = this.minHeight;
29785 switch (this.rotate) {
29788 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29789 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29791 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29792 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29794 var targetWidth = this.minWidth - 2 * x;
29795 var targetHeight = this.minHeight - 2 * y;
29799 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29800 scale = targetWidth / width;
29803 if(x > 0 && y == 0){
29804 scale = targetHeight / height;
29807 if(x > 0 && y > 0){
29808 scale = targetWidth / width;
29810 if(width < height){
29811 scale = targetHeight / height;
29815 context.scale(scale, scale);
29817 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29818 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29820 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29821 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29823 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29828 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29829 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29831 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29832 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29834 var targetWidth = this.minWidth - 2 * x;
29835 var targetHeight = this.minHeight - 2 * y;
29839 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29840 scale = targetWidth / width;
29843 if(x > 0 && y == 0){
29844 scale = targetHeight / height;
29847 if(x > 0 && y > 0){
29848 scale = targetWidth / width;
29850 if(width < height){
29851 scale = targetHeight / height;
29855 context.scale(scale, scale);
29857 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29858 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29860 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29861 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29863 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29865 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29870 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29871 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29873 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29874 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29876 var targetWidth = this.minWidth - 2 * x;
29877 var targetHeight = this.minHeight - 2 * y;
29881 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29882 scale = targetWidth / width;
29885 if(x > 0 && y == 0){
29886 scale = targetHeight / height;
29889 if(x > 0 && y > 0){
29890 scale = targetWidth / width;
29892 if(width < height){
29893 scale = targetHeight / height;
29897 context.scale(scale, scale);
29899 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29900 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29902 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29903 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29905 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29906 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29908 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29913 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29914 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29916 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29917 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29919 var targetWidth = this.minWidth - 2 * x;
29920 var targetHeight = this.minHeight - 2 * y;
29924 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29925 scale = targetWidth / width;
29928 if(x > 0 && y == 0){
29929 scale = targetHeight / height;
29932 if(x > 0 && y > 0){
29933 scale = targetWidth / width;
29935 if(width < height){
29936 scale = targetHeight / height;
29940 context.scale(scale, scale);
29942 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29943 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29945 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29946 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29948 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29950 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29957 this.cropData = canvas.toDataURL(this.cropType);
29959 if(this.fireEvent('crop', this, this.cropData) !== false){
29960 this.process(this.file, this.cropData);
29967 setThumbBoxSize : function()
29971 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29972 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29973 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29975 this.minWidth = width;
29976 this.minHeight = height;
29978 if(this.rotate == 90 || this.rotate == 270){
29979 this.minWidth = height;
29980 this.minHeight = width;
29985 width = Math.ceil(this.minWidth * height / this.minHeight);
29987 if(this.minWidth > this.minHeight){
29989 height = Math.ceil(this.minHeight * width / this.minWidth);
29992 this.thumbEl.setStyle({
29993 width : width + 'px',
29994 height : height + 'px'
30001 setThumbBoxPosition : function()
30003 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30004 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30006 this.thumbEl.setLeft(x);
30007 this.thumbEl.setTop(y);
30011 baseRotateLevel : function()
30013 this.baseRotate = 1;
30016 typeof(this.exif) != 'undefined' &&
30017 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30018 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30020 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30023 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30027 baseScaleLevel : function()
30031 if(this.isDocument){
30033 if(this.baseRotate == 6 || this.baseRotate == 8){
30035 height = this.thumbEl.getHeight();
30036 this.baseScale = height / this.imageEl.OriginWidth;
30038 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30039 width = this.thumbEl.getWidth();
30040 this.baseScale = width / this.imageEl.OriginHeight;
30046 height = this.thumbEl.getHeight();
30047 this.baseScale = height / this.imageEl.OriginHeight;
30049 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30050 width = this.thumbEl.getWidth();
30051 this.baseScale = width / this.imageEl.OriginWidth;
30057 if(this.baseRotate == 6 || this.baseRotate == 8){
30059 width = this.thumbEl.getHeight();
30060 this.baseScale = width / this.imageEl.OriginHeight;
30062 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30063 height = this.thumbEl.getWidth();
30064 this.baseScale = height / this.imageEl.OriginHeight;
30067 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30068 height = this.thumbEl.getWidth();
30069 this.baseScale = height / this.imageEl.OriginHeight;
30071 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30072 width = this.thumbEl.getHeight();
30073 this.baseScale = width / this.imageEl.OriginWidth;
30080 width = this.thumbEl.getWidth();
30081 this.baseScale = width / this.imageEl.OriginWidth;
30083 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30084 height = this.thumbEl.getHeight();
30085 this.baseScale = height / this.imageEl.OriginHeight;
30088 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30090 height = this.thumbEl.getHeight();
30091 this.baseScale = height / this.imageEl.OriginHeight;
30093 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30094 width = this.thumbEl.getWidth();
30095 this.baseScale = width / this.imageEl.OriginWidth;
30103 getScaleLevel : function()
30105 return this.baseScale * Math.pow(1.1, this.scale);
30108 onTouchStart : function(e)
30110 if(!this.canvasLoaded){
30111 this.beforeSelectFile(e);
30115 var touches = e.browserEvent.touches;
30121 if(touches.length == 1){
30122 this.onMouseDown(e);
30126 if(touches.length != 2){
30132 for(var i = 0, finger; finger = touches[i]; i++){
30133 coords.push(finger.pageX, finger.pageY);
30136 var x = Math.pow(coords[0] - coords[2], 2);
30137 var y = Math.pow(coords[1] - coords[3], 2);
30139 this.startDistance = Math.sqrt(x + y);
30141 this.startScale = this.scale;
30143 this.pinching = true;
30144 this.dragable = false;
30148 onTouchMove : function(e)
30150 if(!this.pinching && !this.dragable){
30154 var touches = e.browserEvent.touches;
30161 this.onMouseMove(e);
30167 for(var i = 0, finger; finger = touches[i]; i++){
30168 coords.push(finger.pageX, finger.pageY);
30171 var x = Math.pow(coords[0] - coords[2], 2);
30172 var y = Math.pow(coords[1] - coords[3], 2);
30174 this.endDistance = Math.sqrt(x + y);
30176 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30178 if(!this.zoomable()){
30179 this.scale = this.startScale;
30187 onTouchEnd : function(e)
30189 this.pinching = false;
30190 this.dragable = false;
30194 process : function(file, crop)
30197 this.maskEl.mask(this.loadingText);
30200 this.xhr = new XMLHttpRequest();
30202 file.xhr = this.xhr;
30204 this.xhr.open(this.method, this.url, true);
30207 "Accept": "application/json",
30208 "Cache-Control": "no-cache",
30209 "X-Requested-With": "XMLHttpRequest"
30212 for (var headerName in headers) {
30213 var headerValue = headers[headerName];
30215 this.xhr.setRequestHeader(headerName, headerValue);
30221 this.xhr.onload = function()
30223 _this.xhrOnLoad(_this.xhr);
30226 this.xhr.onerror = function()
30228 _this.xhrOnError(_this.xhr);
30231 var formData = new FormData();
30233 formData.append('returnHTML', 'NO');
30236 formData.append('crop', crop);
30239 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30240 formData.append(this.paramName, file, file.name);
30243 if(typeof(file.filename) != 'undefined'){
30244 formData.append('filename', file.filename);
30247 if(typeof(file.mimetype) != 'undefined'){
30248 formData.append('mimetype', file.mimetype);
30251 if(this.fireEvent('arrange', this, formData) != false){
30252 this.xhr.send(formData);
30256 xhrOnLoad : function(xhr)
30259 this.maskEl.unmask();
30262 if (xhr.readyState !== 4) {
30263 this.fireEvent('exception', this, xhr);
30267 var response = Roo.decode(xhr.responseText);
30269 if(!response.success){
30270 this.fireEvent('exception', this, xhr);
30274 var response = Roo.decode(xhr.responseText);
30276 this.fireEvent('upload', this, response);
30280 xhrOnError : function()
30283 this.maskEl.unmask();
30286 Roo.log('xhr on error');
30288 var response = Roo.decode(xhr.responseText);
30294 prepare : function(file)
30297 this.maskEl.mask(this.loadingText);
30303 if(typeof(file) === 'string'){
30304 this.loadCanvas(file);
30308 if(!file || !this.urlAPI){
30313 this.cropType = file.type;
30317 if(this.fireEvent('prepare', this, this.file) != false){
30319 var reader = new FileReader();
30321 reader.onload = function (e) {
30322 if (e.target.error) {
30323 Roo.log(e.target.error);
30327 var buffer = e.target.result,
30328 dataView = new DataView(buffer),
30330 maxOffset = dataView.byteLength - 4,
30334 if (dataView.getUint16(0) === 0xffd8) {
30335 while (offset < maxOffset) {
30336 markerBytes = dataView.getUint16(offset);
30338 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30339 markerLength = dataView.getUint16(offset + 2) + 2;
30340 if (offset + markerLength > dataView.byteLength) {
30341 Roo.log('Invalid meta data: Invalid segment size.');
30345 if(markerBytes == 0xffe1){
30346 _this.parseExifData(
30353 offset += markerLength;
30363 var url = _this.urlAPI.createObjectURL(_this.file);
30365 _this.loadCanvas(url);
30370 reader.readAsArrayBuffer(this.file);
30376 parseExifData : function(dataView, offset, length)
30378 var tiffOffset = offset + 10,
30382 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30383 // No Exif data, might be XMP data instead
30387 // Check for the ASCII code for "Exif" (0x45786966):
30388 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30389 // No Exif data, might be XMP data instead
30392 if (tiffOffset + 8 > dataView.byteLength) {
30393 Roo.log('Invalid Exif data: Invalid segment size.');
30396 // Check for the two null bytes:
30397 if (dataView.getUint16(offset + 8) !== 0x0000) {
30398 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30401 // Check the byte alignment:
30402 switch (dataView.getUint16(tiffOffset)) {
30404 littleEndian = true;
30407 littleEndian = false;
30410 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30413 // Check for the TIFF tag marker (0x002A):
30414 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30415 Roo.log('Invalid Exif data: Missing TIFF marker.');
30418 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30419 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30421 this.parseExifTags(
30424 tiffOffset + dirOffset,
30429 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30434 if (dirOffset + 6 > dataView.byteLength) {
30435 Roo.log('Invalid Exif data: Invalid directory offset.');
30438 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30439 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30440 if (dirEndOffset + 4 > dataView.byteLength) {
30441 Roo.log('Invalid Exif data: Invalid directory size.');
30444 for (i = 0; i < tagsNumber; i += 1) {
30448 dirOffset + 2 + 12 * i, // tag offset
30452 // Return the offset to the next directory:
30453 return dataView.getUint32(dirEndOffset, littleEndian);
30456 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30458 var tag = dataView.getUint16(offset, littleEndian);
30460 this.exif[tag] = this.getExifValue(
30464 dataView.getUint16(offset + 2, littleEndian), // tag type
30465 dataView.getUint32(offset + 4, littleEndian), // tag length
30470 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30472 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30481 Roo.log('Invalid Exif data: Invalid tag type.');
30485 tagSize = tagType.size * length;
30486 // Determine if the value is contained in the dataOffset bytes,
30487 // or if the value at the dataOffset is a pointer to the actual data:
30488 dataOffset = tagSize > 4 ?
30489 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30490 if (dataOffset + tagSize > dataView.byteLength) {
30491 Roo.log('Invalid Exif data: Invalid data offset.');
30494 if (length === 1) {
30495 return tagType.getValue(dataView, dataOffset, littleEndian);
30498 for (i = 0; i < length; i += 1) {
30499 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30502 if (tagType.ascii) {
30504 // Concatenate the chars:
30505 for (i = 0; i < values.length; i += 1) {
30507 // Ignore the terminating NULL byte(s):
30508 if (c === '\u0000') {
30520 Roo.apply(Roo.bootstrap.UploadCropbox, {
30522 'Orientation': 0x0112
30526 1: 0, //'top-left',
30528 3: 180, //'bottom-right',
30529 // 4: 'bottom-left',
30531 6: 90, //'right-top',
30532 // 7: 'right-bottom',
30533 8: 270 //'left-bottom'
30537 // byte, 8-bit unsigned int:
30539 getValue: function (dataView, dataOffset) {
30540 return dataView.getUint8(dataOffset);
30544 // ascii, 8-bit byte:
30546 getValue: function (dataView, dataOffset) {
30547 return String.fromCharCode(dataView.getUint8(dataOffset));
30552 // short, 16 bit int:
30554 getValue: function (dataView, dataOffset, littleEndian) {
30555 return dataView.getUint16(dataOffset, littleEndian);
30559 // long, 32 bit int:
30561 getValue: function (dataView, dataOffset, littleEndian) {
30562 return dataView.getUint32(dataOffset, littleEndian);
30566 // rational = two long values, first is numerator, second is denominator:
30568 getValue: function (dataView, dataOffset, littleEndian) {
30569 return dataView.getUint32(dataOffset, littleEndian) /
30570 dataView.getUint32(dataOffset + 4, littleEndian);
30574 // slong, 32 bit signed int:
30576 getValue: function (dataView, dataOffset, littleEndian) {
30577 return dataView.getInt32(dataOffset, littleEndian);
30581 // srational, two slongs, first is numerator, second is denominator:
30583 getValue: function (dataView, dataOffset, littleEndian) {
30584 return dataView.getInt32(dataOffset, littleEndian) /
30585 dataView.getInt32(dataOffset + 4, littleEndian);
30595 cls : 'btn-group roo-upload-cropbox-rotate-left',
30596 action : 'rotate-left',
30600 cls : 'btn btn-default',
30601 html : '<i class="fa fa-undo"></i>'
30607 cls : 'btn-group roo-upload-cropbox-picture',
30608 action : 'picture',
30612 cls : 'btn btn-default',
30613 html : '<i class="fa fa-picture-o"></i>'
30619 cls : 'btn-group roo-upload-cropbox-rotate-right',
30620 action : 'rotate-right',
30624 cls : 'btn btn-default',
30625 html : '<i class="fa fa-repeat"></i>'
30633 cls : 'btn-group roo-upload-cropbox-rotate-left',
30634 action : 'rotate-left',
30638 cls : 'btn btn-default',
30639 html : '<i class="fa fa-undo"></i>'
30645 cls : 'btn-group roo-upload-cropbox-download',
30646 action : 'download',
30650 cls : 'btn btn-default',
30651 html : '<i class="fa fa-download"></i>'
30657 cls : 'btn-group roo-upload-cropbox-crop',
30662 cls : 'btn btn-default',
30663 html : '<i class="fa fa-crop"></i>'
30669 cls : 'btn-group roo-upload-cropbox-trash',
30674 cls : 'btn btn-default',
30675 html : '<i class="fa fa-trash"></i>'
30681 cls : 'btn-group roo-upload-cropbox-rotate-right',
30682 action : 'rotate-right',
30686 cls : 'btn btn-default',
30687 html : '<i class="fa fa-repeat"></i>'
30695 cls : 'btn-group roo-upload-cropbox-rotate-left',
30696 action : 'rotate-left',
30700 cls : 'btn btn-default',
30701 html : '<i class="fa fa-undo"></i>'
30707 cls : 'btn-group roo-upload-cropbox-rotate-right',
30708 action : 'rotate-right',
30712 cls : 'btn btn-default',
30713 html : '<i class="fa fa-repeat"></i>'
30726 * @class Roo.bootstrap.DocumentManager
30727 * @extends Roo.bootstrap.Component
30728 * Bootstrap DocumentManager class
30729 * @cfg {String} paramName default 'imageUpload'
30730 * @cfg {String} toolTipName default 'filename'
30731 * @cfg {String} method default POST
30732 * @cfg {String} url action url
30733 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30734 * @cfg {Boolean} multiple multiple upload default true
30735 * @cfg {Number} thumbSize default 300
30736 * @cfg {String} fieldLabel
30737 * @cfg {Number} labelWidth default 4
30738 * @cfg {String} labelAlign (left|top) default left
30739 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30740 * @cfg {Number} labellg set the width of label (1-12)
30741 * @cfg {Number} labelmd set the width of label (1-12)
30742 * @cfg {Number} labelsm set the width of label (1-12)
30743 * @cfg {Number} labelxs set the width of label (1-12)
30746 * Create a new DocumentManager
30747 * @param {Object} config The config object
30750 Roo.bootstrap.DocumentManager = function(config){
30751 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30754 this.delegates = [];
30759 * Fire when initial the DocumentManager
30760 * @param {Roo.bootstrap.DocumentManager} this
30765 * inspect selected file
30766 * @param {Roo.bootstrap.DocumentManager} this
30767 * @param {File} file
30772 * Fire when xhr load exception
30773 * @param {Roo.bootstrap.DocumentManager} this
30774 * @param {XMLHttpRequest} xhr
30776 "exception" : true,
30778 * @event afterupload
30779 * Fire when xhr load exception
30780 * @param {Roo.bootstrap.DocumentManager} this
30781 * @param {XMLHttpRequest} xhr
30783 "afterupload" : true,
30786 * prepare the form data
30787 * @param {Roo.bootstrap.DocumentManager} this
30788 * @param {Object} formData
30793 * Fire when remove the file
30794 * @param {Roo.bootstrap.DocumentManager} this
30795 * @param {Object} file
30800 * Fire after refresh the file
30801 * @param {Roo.bootstrap.DocumentManager} this
30806 * Fire after click the image
30807 * @param {Roo.bootstrap.DocumentManager} this
30808 * @param {Object} file
30813 * Fire when upload a image and editable set to true
30814 * @param {Roo.bootstrap.DocumentManager} this
30815 * @param {Object} file
30819 * @event beforeselectfile
30820 * Fire before select file
30821 * @param {Roo.bootstrap.DocumentManager} this
30823 "beforeselectfile" : true,
30826 * Fire before process file
30827 * @param {Roo.bootstrap.DocumentManager} this
30828 * @param {Object} file
30832 * @event previewrendered
30833 * Fire when preview rendered
30834 * @param {Roo.bootstrap.DocumentManager} this
30835 * @param {Object} file
30837 "previewrendered" : true,
30840 "previewResize" : true
30845 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
30854 paramName : 'imageUpload',
30855 toolTipName : 'filename',
30858 labelAlign : 'left',
30868 getAutoCreate : function()
30870 var managerWidget = {
30872 cls : 'roo-document-manager',
30876 cls : 'roo-document-manager-selector',
30881 cls : 'roo-document-manager-uploader',
30885 cls : 'roo-document-manager-upload-btn',
30886 html : '<i class="fa fa-plus"></i>'
30897 cls : 'column col-md-12',
30902 if(this.fieldLabel.length){
30907 cls : 'column col-md-12',
30908 html : this.fieldLabel
30912 cls : 'column col-md-12',
30917 if(this.labelAlign == 'left'){
30922 html : this.fieldLabel
30931 if(this.labelWidth > 12){
30932 content[0].style = "width: " + this.labelWidth + 'px';
30935 if(this.labelWidth < 13 && this.labelmd == 0){
30936 this.labelmd = this.labelWidth;
30939 if(this.labellg > 0){
30940 content[0].cls += ' col-lg-' + this.labellg;
30941 content[1].cls += ' col-lg-' + (12 - this.labellg);
30944 if(this.labelmd > 0){
30945 content[0].cls += ' col-md-' + this.labelmd;
30946 content[1].cls += ' col-md-' + (12 - this.labelmd);
30949 if(this.labelsm > 0){
30950 content[0].cls += ' col-sm-' + this.labelsm;
30951 content[1].cls += ' col-sm-' + (12 - this.labelsm);
30954 if(this.labelxs > 0){
30955 content[0].cls += ' col-xs-' + this.labelxs;
30956 content[1].cls += ' col-xs-' + (12 - this.labelxs);
30964 cls : 'row clearfix',
30972 initEvents : function()
30974 this.managerEl = this.el.select('.roo-document-manager', true).first();
30975 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30977 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30978 this.selectorEl.hide();
30981 this.selectorEl.attr('multiple', 'multiple');
30984 this.selectorEl.on('change', this.onFileSelected, this);
30986 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30987 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30989 this.uploader.on('click', this.onUploaderClick, this);
30991 this.renderProgressDialog();
30995 window.addEventListener("resize", function() { _this.refresh(); } );
30997 this.fireEvent('initial', this);
31000 renderProgressDialog : function()
31004 this.progressDialog = new Roo.bootstrap.Modal({
31005 cls : 'roo-document-manager-progress-dialog',
31006 allow_close : false,
31017 btnclick : function() {
31018 _this.uploadCancel();
31024 this.progressDialog.render(Roo.get(document.body));
31026 this.progress = new Roo.bootstrap.Progress({
31027 cls : 'roo-document-manager-progress',
31032 this.progress.render(this.progressDialog.getChildContainer());
31034 this.progressBar = new Roo.bootstrap.ProgressBar({
31035 cls : 'roo-document-manager-progress-bar',
31038 aria_valuemax : 12,
31042 this.progressBar.render(this.progress.getChildContainer());
31045 onUploaderClick : function(e)
31047 e.preventDefault();
31049 if(this.fireEvent('beforeselectfile', this) != false){
31050 this.selectorEl.dom.click();
31055 onFileSelected : function(e)
31057 e.preventDefault();
31059 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31063 Roo.each(this.selectorEl.dom.files, function(file){
31064 if(this.fireEvent('inspect', this, file) != false){
31065 this.files.push(file);
31075 this.selectorEl.dom.value = '';
31077 if(!this.files || !this.files.length){
31081 if(this.boxes > 0 && this.files.length > this.boxes){
31082 this.files = this.files.slice(0, this.boxes);
31085 this.uploader.show();
31087 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31088 this.uploader.hide();
31097 Roo.each(this.files, function(file){
31099 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31100 var f = this.renderPreview(file);
31105 if(file.type.indexOf('image') != -1){
31106 this.delegates.push(
31108 _this.process(file);
31109 }).createDelegate(this)
31117 _this.process(file);
31118 }).createDelegate(this)
31123 this.files = files;
31125 this.delegates = this.delegates.concat(docs);
31127 if(!this.delegates.length){
31132 this.progressBar.aria_valuemax = this.delegates.length;
31139 arrange : function()
31141 if(!this.delegates.length){
31142 this.progressDialog.hide();
31147 var delegate = this.delegates.shift();
31149 this.progressDialog.show();
31151 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31153 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31158 refresh : function()
31160 this.uploader.show();
31162 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31163 this.uploader.hide();
31166 Roo.isTouch ? this.closable(false) : this.closable(true);
31168 this.fireEvent('refresh', this);
31171 onRemove : function(e, el, o)
31173 e.preventDefault();
31175 this.fireEvent('remove', this, o);
31179 remove : function(o)
31183 Roo.each(this.files, function(file){
31184 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31193 this.files = files;
31200 Roo.each(this.files, function(file){
31205 file.target.remove();
31214 onClick : function(e, el, o)
31216 e.preventDefault();
31218 this.fireEvent('click', this, o);
31222 closable : function(closable)
31224 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31226 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31238 xhrOnLoad : function(xhr)
31240 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31244 if (xhr.readyState !== 4) {
31246 this.fireEvent('exception', this, xhr);
31250 var response = Roo.decode(xhr.responseText);
31252 if(!response.success){
31254 this.fireEvent('exception', this, xhr);
31258 var file = this.renderPreview(response.data);
31260 this.files.push(file);
31264 this.fireEvent('afterupload', this, xhr);
31268 xhrOnError : function(xhr)
31270 Roo.log('xhr on error');
31272 var response = Roo.decode(xhr.responseText);
31279 process : function(file)
31281 if(this.fireEvent('process', this, file) !== false){
31282 if(this.editable && file.type.indexOf('image') != -1){
31283 this.fireEvent('edit', this, file);
31287 this.uploadStart(file, false);
31294 uploadStart : function(file, crop)
31296 this.xhr = new XMLHttpRequest();
31298 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31303 file.xhr = this.xhr;
31305 this.managerEl.createChild({
31307 cls : 'roo-document-manager-loading',
31311 tooltip : file.name,
31312 cls : 'roo-document-manager-thumb',
31313 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31319 this.xhr.open(this.method, this.url, true);
31322 "Accept": "application/json",
31323 "Cache-Control": "no-cache",
31324 "X-Requested-With": "XMLHttpRequest"
31327 for (var headerName in headers) {
31328 var headerValue = headers[headerName];
31330 this.xhr.setRequestHeader(headerName, headerValue);
31336 this.xhr.onload = function()
31338 _this.xhrOnLoad(_this.xhr);
31341 this.xhr.onerror = function()
31343 _this.xhrOnError(_this.xhr);
31346 var formData = new FormData();
31348 formData.append('returnHTML', 'NO');
31351 formData.append('crop', crop);
31354 formData.append(this.paramName, file, file.name);
31361 if(this.fireEvent('prepare', this, formData, options) != false){
31363 if(options.manually){
31367 this.xhr.send(formData);
31371 this.uploadCancel();
31374 uploadCancel : function()
31380 this.delegates = [];
31382 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31389 renderPreview : function(file)
31391 if(typeof(file.target) != 'undefined' && file.target){
31395 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31397 var previewEl = this.managerEl.createChild({
31399 cls : 'roo-document-manager-preview',
31403 tooltip : file[this.toolTipName],
31404 cls : 'roo-document-manager-thumb',
31405 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31410 html : '<i class="fa fa-times-circle"></i>'
31415 var close = previewEl.select('button.close', true).first();
31417 close.on('click', this.onRemove, this, file);
31419 file.target = previewEl;
31421 var image = previewEl.select('img', true).first();
31425 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31427 image.on('click', this.onClick, this, file);
31429 this.fireEvent('previewrendered', this, file);
31435 onPreviewLoad : function(file, image)
31437 if(typeof(file.target) == 'undefined' || !file.target){
31441 var width = image.dom.naturalWidth || image.dom.width;
31442 var height = image.dom.naturalHeight || image.dom.height;
31444 if(!this.previewResize) {
31448 if(width > height){
31449 file.target.addClass('wide');
31453 file.target.addClass('tall');
31458 uploadFromSource : function(file, crop)
31460 this.xhr = new XMLHttpRequest();
31462 this.managerEl.createChild({
31464 cls : 'roo-document-manager-loading',
31468 tooltip : file.name,
31469 cls : 'roo-document-manager-thumb',
31470 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31476 this.xhr.open(this.method, this.url, true);
31479 "Accept": "application/json",
31480 "Cache-Control": "no-cache",
31481 "X-Requested-With": "XMLHttpRequest"
31484 for (var headerName in headers) {
31485 var headerValue = headers[headerName];
31487 this.xhr.setRequestHeader(headerName, headerValue);
31493 this.xhr.onload = function()
31495 _this.xhrOnLoad(_this.xhr);
31498 this.xhr.onerror = function()
31500 _this.xhrOnError(_this.xhr);
31503 var formData = new FormData();
31505 formData.append('returnHTML', 'NO');
31507 formData.append('crop', crop);
31509 if(typeof(file.filename) != 'undefined'){
31510 formData.append('filename', file.filename);
31513 if(typeof(file.mimetype) != 'undefined'){
31514 formData.append('mimetype', file.mimetype);
31519 if(this.fireEvent('prepare', this, formData) != false){
31520 this.xhr.send(formData);
31530 * @class Roo.bootstrap.DocumentViewer
31531 * @extends Roo.bootstrap.Component
31532 * Bootstrap DocumentViewer class
31533 * @cfg {Boolean} showDownload (true|false) show download button (default true)
31534 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31537 * Create a new DocumentViewer
31538 * @param {Object} config The config object
31541 Roo.bootstrap.DocumentViewer = function(config){
31542 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31547 * Fire after initEvent
31548 * @param {Roo.bootstrap.DocumentViewer} this
31554 * @param {Roo.bootstrap.DocumentViewer} this
31559 * Fire after download button
31560 * @param {Roo.bootstrap.DocumentViewer} this
31565 * Fire after trash button
31566 * @param {Roo.bootstrap.DocumentViewer} this
31573 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
31575 showDownload : true,
31579 getAutoCreate : function()
31583 cls : 'roo-document-viewer',
31587 cls : 'roo-document-viewer-body',
31591 cls : 'roo-document-viewer-thumb',
31595 cls : 'roo-document-viewer-image'
31603 cls : 'roo-document-viewer-footer',
31606 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31610 cls : 'btn-group roo-document-viewer-download',
31614 cls : 'btn btn-default',
31615 html : '<i class="fa fa-download"></i>'
31621 cls : 'btn-group roo-document-viewer-trash',
31625 cls : 'btn btn-default',
31626 html : '<i class="fa fa-trash"></i>'
31639 initEvents : function()
31641 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31642 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31644 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31645 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31647 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31648 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31650 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31651 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31653 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31654 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31656 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31657 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31659 this.bodyEl.on('click', this.onClick, this);
31660 this.downloadBtn.on('click', this.onDownload, this);
31661 this.trashBtn.on('click', this.onTrash, this);
31663 this.downloadBtn.hide();
31664 this.trashBtn.hide();
31666 if(this.showDownload){
31667 this.downloadBtn.show();
31670 if(this.showTrash){
31671 this.trashBtn.show();
31674 if(!this.showDownload && !this.showTrash) {
31675 this.footerEl.hide();
31680 initial : function()
31682 this.fireEvent('initial', this);
31686 onClick : function(e)
31688 e.preventDefault();
31690 this.fireEvent('click', this);
31693 onDownload : function(e)
31695 e.preventDefault();
31697 this.fireEvent('download', this);
31700 onTrash : function(e)
31702 e.preventDefault();
31704 this.fireEvent('trash', this);
31716 * @class Roo.bootstrap.NavProgressBar
31717 * @extends Roo.bootstrap.Component
31718 * Bootstrap NavProgressBar class
31721 * Create a new nav progress bar
31722 * @param {Object} config The config object
31725 Roo.bootstrap.NavProgressBar = function(config){
31726 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31728 this.bullets = this.bullets || [];
31730 // Roo.bootstrap.NavProgressBar.register(this);
31734 * Fires when the active item changes
31735 * @param {Roo.bootstrap.NavProgressBar} this
31736 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31737 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
31744 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
31749 getAutoCreate : function()
31751 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31755 cls : 'roo-navigation-bar-group',
31759 cls : 'roo-navigation-top-bar'
31763 cls : 'roo-navigation-bullets-bar',
31767 cls : 'roo-navigation-bar'
31774 cls : 'roo-navigation-bottom-bar'
31784 initEvents: function()
31789 onRender : function(ct, position)
31791 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31793 if(this.bullets.length){
31794 Roo.each(this.bullets, function(b){
31803 addItem : function(cfg)
31805 var item = new Roo.bootstrap.NavProgressItem(cfg);
31807 item.parentId = this.id;
31808 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31811 var top = new Roo.bootstrap.Element({
31813 cls : 'roo-navigation-bar-text'
31816 var bottom = new Roo.bootstrap.Element({
31818 cls : 'roo-navigation-bar-text'
31821 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31822 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31824 var topText = new Roo.bootstrap.Element({
31826 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31829 var bottomText = new Roo.bootstrap.Element({
31831 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31834 topText.onRender(top.el, null);
31835 bottomText.onRender(bottom.el, null);
31838 item.bottomEl = bottom;
31841 this.barItems.push(item);
31846 getActive : function()
31848 var active = false;
31850 Roo.each(this.barItems, function(v){
31852 if (!v.isActive()) {
31864 setActiveItem : function(item)
31868 Roo.each(this.barItems, function(v){
31869 if (v.rid == item.rid) {
31873 if (v.isActive()) {
31874 v.setActive(false);
31879 item.setActive(true);
31881 this.fireEvent('changed', this, item, prev);
31884 getBarItem: function(rid)
31888 Roo.each(this.barItems, function(e) {
31889 if (e.rid != rid) {
31900 indexOfItem : function(item)
31904 Roo.each(this.barItems, function(v, i){
31906 if (v.rid != item.rid) {
31917 setActiveNext : function()
31919 var i = this.indexOfItem(this.getActive());
31921 if (i > this.barItems.length) {
31925 this.setActiveItem(this.barItems[i+1]);
31928 setActivePrev : function()
31930 var i = this.indexOfItem(this.getActive());
31936 this.setActiveItem(this.barItems[i-1]);
31939 format : function()
31941 if(!this.barItems.length){
31945 var width = 100 / this.barItems.length;
31947 Roo.each(this.barItems, function(i){
31948 i.el.setStyle('width', width + '%');
31949 i.topEl.el.setStyle('width', width + '%');
31950 i.bottomEl.el.setStyle('width', width + '%');
31959 * Nav Progress Item
31964 * @class Roo.bootstrap.NavProgressItem
31965 * @extends Roo.bootstrap.Component
31966 * Bootstrap NavProgressItem class
31967 * @cfg {String} rid the reference id
31968 * @cfg {Boolean} active (true|false) Is item active default false
31969 * @cfg {Boolean} disabled (true|false) Is item active default false
31970 * @cfg {String} html
31971 * @cfg {String} position (top|bottom) text position default bottom
31972 * @cfg {String} icon show icon instead of number
31975 * Create a new NavProgressItem
31976 * @param {Object} config The config object
31978 Roo.bootstrap.NavProgressItem = function(config){
31979 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31984 * The raw click event for the entire grid.
31985 * @param {Roo.bootstrap.NavProgressItem} this
31986 * @param {Roo.EventObject} e
31993 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
31999 position : 'bottom',
32002 getAutoCreate : function()
32004 var iconCls = 'roo-navigation-bar-item-icon';
32006 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32010 cls: 'roo-navigation-bar-item',
32020 cfg.cls += ' active';
32023 cfg.cls += ' disabled';
32029 disable : function()
32031 this.setDisabled(true);
32034 enable : function()
32036 this.setDisabled(false);
32039 initEvents: function()
32041 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32043 this.iconEl.on('click', this.onClick, this);
32046 onClick : function(e)
32048 e.preventDefault();
32054 if(this.fireEvent('click', this, e) === false){
32058 this.parent().setActiveItem(this);
32061 isActive: function ()
32063 return this.active;
32066 setActive : function(state)
32068 if(this.active == state){
32072 this.active = state;
32075 this.el.addClass('active');
32079 this.el.removeClass('active');
32084 setDisabled : function(state)
32086 if(this.disabled == state){
32090 this.disabled = state;
32093 this.el.addClass('disabled');
32097 this.el.removeClass('disabled');
32100 tooltipEl : function()
32102 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32115 * @class Roo.bootstrap.FieldLabel
32116 * @extends Roo.bootstrap.Component
32117 * Bootstrap FieldLabel class
32118 * @cfg {String} html contents of the element
32119 * @cfg {String} tag tag of the element default label
32120 * @cfg {String} cls class of the element
32121 * @cfg {String} target label target
32122 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32123 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32124 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32125 * @cfg {String} iconTooltip default "This field is required"
32126 * @cfg {String} indicatorpos (left|right) default left
32129 * Create a new FieldLabel
32130 * @param {Object} config The config object
32133 Roo.bootstrap.FieldLabel = function(config){
32134 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32139 * Fires after the field has been marked as invalid.
32140 * @param {Roo.form.FieldLabel} this
32141 * @param {String} msg The validation message
32146 * Fires after the field has been validated with no errors.
32147 * @param {Roo.form.FieldLabel} this
32153 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32160 invalidClass : 'has-warning',
32161 validClass : 'has-success',
32162 iconTooltip : 'This field is required',
32163 indicatorpos : 'left',
32165 getAutoCreate : function(){
32168 if (!this.allowBlank) {
32174 cls : 'roo-bootstrap-field-label ' + this.cls,
32179 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32180 tooltip : this.iconTooltip
32189 if(this.indicatorpos == 'right'){
32192 cls : 'roo-bootstrap-field-label ' + this.cls,
32201 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32202 tooltip : this.iconTooltip
32211 initEvents: function()
32213 Roo.bootstrap.Element.superclass.initEvents.call(this);
32215 this.indicator = this.indicatorEl();
32217 if(this.indicator){
32218 this.indicator.removeClass('visible');
32219 this.indicator.addClass('invisible');
32222 Roo.bootstrap.FieldLabel.register(this);
32225 indicatorEl : function()
32227 var indicator = this.el.select('i.roo-required-indicator',true).first();
32238 * Mark this field as valid
32240 markValid : function()
32242 if(this.indicator){
32243 this.indicator.removeClass('visible');
32244 this.indicator.addClass('invisible');
32246 if (Roo.bootstrap.version == 3) {
32247 this.el.removeClass(this.invalidClass);
32248 this.el.addClass(this.validClass);
32250 this.el.removeClass('is-invalid');
32251 this.el.addClass('is-valid');
32255 this.fireEvent('valid', this);
32259 * Mark this field as invalid
32260 * @param {String} msg The validation message
32262 markInvalid : function(msg)
32264 if(this.indicator){
32265 this.indicator.removeClass('invisible');
32266 this.indicator.addClass('visible');
32268 if (Roo.bootstrap.version == 3) {
32269 this.el.removeClass(this.validClass);
32270 this.el.addClass(this.invalidClass);
32272 this.el.removeClass('is-valid');
32273 this.el.addClass('is-invalid');
32277 this.fireEvent('invalid', this, msg);
32283 Roo.apply(Roo.bootstrap.FieldLabel, {
32288 * register a FieldLabel Group
32289 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32291 register : function(label)
32293 if(this.groups.hasOwnProperty(label.target)){
32297 this.groups[label.target] = label;
32301 * fetch a FieldLabel Group based on the target
32302 * @param {string} target
32303 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32305 get: function(target) {
32306 if (typeof(this.groups[target]) == 'undefined') {
32310 return this.groups[target] ;
32319 * page DateSplitField.
32325 * @class Roo.bootstrap.DateSplitField
32326 * @extends Roo.bootstrap.Component
32327 * Bootstrap DateSplitField class
32328 * @cfg {string} fieldLabel - the label associated
32329 * @cfg {Number} labelWidth set the width of label (0-12)
32330 * @cfg {String} labelAlign (top|left)
32331 * @cfg {Boolean} dayAllowBlank (true|false) default false
32332 * @cfg {Boolean} monthAllowBlank (true|false) default false
32333 * @cfg {Boolean} yearAllowBlank (true|false) default false
32334 * @cfg {string} dayPlaceholder
32335 * @cfg {string} monthPlaceholder
32336 * @cfg {string} yearPlaceholder
32337 * @cfg {string} dayFormat default 'd'
32338 * @cfg {string} monthFormat default 'm'
32339 * @cfg {string} yearFormat default 'Y'
32340 * @cfg {Number} labellg set the width of label (1-12)
32341 * @cfg {Number} labelmd set the width of label (1-12)
32342 * @cfg {Number} labelsm set the width of label (1-12)
32343 * @cfg {Number} labelxs set the width of label (1-12)
32347 * Create a new DateSplitField
32348 * @param {Object} config The config object
32351 Roo.bootstrap.DateSplitField = function(config){
32352 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32358 * getting the data of years
32359 * @param {Roo.bootstrap.DateSplitField} this
32360 * @param {Object} years
32365 * getting the data of days
32366 * @param {Roo.bootstrap.DateSplitField} this
32367 * @param {Object} days
32372 * Fires after the field has been marked as invalid.
32373 * @param {Roo.form.Field} this
32374 * @param {String} msg The validation message
32379 * Fires after the field has been validated with no errors.
32380 * @param {Roo.form.Field} this
32386 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32389 labelAlign : 'top',
32391 dayAllowBlank : false,
32392 monthAllowBlank : false,
32393 yearAllowBlank : false,
32394 dayPlaceholder : '',
32395 monthPlaceholder : '',
32396 yearPlaceholder : '',
32400 isFormField : true,
32406 getAutoCreate : function()
32410 cls : 'row roo-date-split-field-group',
32415 cls : 'form-hidden-field roo-date-split-field-group-value',
32421 var labelCls = 'col-md-12';
32422 var contentCls = 'col-md-4';
32424 if(this.fieldLabel){
32428 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32432 html : this.fieldLabel
32437 if(this.labelAlign == 'left'){
32439 if(this.labelWidth > 12){
32440 label.style = "width: " + this.labelWidth + 'px';
32443 if(this.labelWidth < 13 && this.labelmd == 0){
32444 this.labelmd = this.labelWidth;
32447 if(this.labellg > 0){
32448 labelCls = ' col-lg-' + this.labellg;
32449 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32452 if(this.labelmd > 0){
32453 labelCls = ' col-md-' + this.labelmd;
32454 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32457 if(this.labelsm > 0){
32458 labelCls = ' col-sm-' + this.labelsm;
32459 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32462 if(this.labelxs > 0){
32463 labelCls = ' col-xs-' + this.labelxs;
32464 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32468 label.cls += ' ' + labelCls;
32470 cfg.cn.push(label);
32473 Roo.each(['day', 'month', 'year'], function(t){
32476 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32483 inputEl: function ()
32485 return this.el.select('.roo-date-split-field-group-value', true).first();
32488 onRender : function(ct, position)
32492 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32494 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32496 this.dayField = new Roo.bootstrap.ComboBox({
32497 allowBlank : this.dayAllowBlank,
32498 alwaysQuery : true,
32499 displayField : 'value',
32502 forceSelection : true,
32504 placeholder : this.dayPlaceholder,
32505 selectOnFocus : true,
32506 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32507 triggerAction : 'all',
32509 valueField : 'value',
32510 store : new Roo.data.SimpleStore({
32511 data : (function() {
32513 _this.fireEvent('days', _this, days);
32516 fields : [ 'value' ]
32519 select : function (_self, record, index)
32521 _this.setValue(_this.getValue());
32526 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32528 this.monthField = new Roo.bootstrap.MonthField({
32529 after : '<i class=\"fa fa-calendar\"></i>',
32530 allowBlank : this.monthAllowBlank,
32531 placeholder : this.monthPlaceholder,
32534 render : function (_self)
32536 this.el.select('span.input-group-addon', true).first().on('click', function(e){
32537 e.preventDefault();
32541 select : function (_self, oldvalue, newvalue)
32543 _this.setValue(_this.getValue());
32548 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32550 this.yearField = new Roo.bootstrap.ComboBox({
32551 allowBlank : this.yearAllowBlank,
32552 alwaysQuery : true,
32553 displayField : 'value',
32556 forceSelection : true,
32558 placeholder : this.yearPlaceholder,
32559 selectOnFocus : true,
32560 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32561 triggerAction : 'all',
32563 valueField : 'value',
32564 store : new Roo.data.SimpleStore({
32565 data : (function() {
32567 _this.fireEvent('years', _this, years);
32570 fields : [ 'value' ]
32573 select : function (_self, record, index)
32575 _this.setValue(_this.getValue());
32580 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32583 setValue : function(v, format)
32585 this.inputEl.dom.value = v;
32587 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32589 var d = Date.parseDate(v, f);
32596 this.setDay(d.format(this.dayFormat));
32597 this.setMonth(d.format(this.monthFormat));
32598 this.setYear(d.format(this.yearFormat));
32605 setDay : function(v)
32607 this.dayField.setValue(v);
32608 this.inputEl.dom.value = this.getValue();
32613 setMonth : function(v)
32615 this.monthField.setValue(v, true);
32616 this.inputEl.dom.value = this.getValue();
32621 setYear : function(v)
32623 this.yearField.setValue(v);
32624 this.inputEl.dom.value = this.getValue();
32629 getDay : function()
32631 return this.dayField.getValue();
32634 getMonth : function()
32636 return this.monthField.getValue();
32639 getYear : function()
32641 return this.yearField.getValue();
32644 getValue : function()
32646 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32648 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32658 this.inputEl.dom.value = '';
32663 validate : function()
32665 var d = this.dayField.validate();
32666 var m = this.monthField.validate();
32667 var y = this.yearField.validate();
32672 (!this.dayAllowBlank && !d) ||
32673 (!this.monthAllowBlank && !m) ||
32674 (!this.yearAllowBlank && !y)
32679 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32688 this.markInvalid();
32693 markValid : function()
32696 var label = this.el.select('label', true).first();
32697 var icon = this.el.select('i.fa-star', true).first();
32703 this.fireEvent('valid', this);
32707 * Mark this field as invalid
32708 * @param {String} msg The validation message
32710 markInvalid : function(msg)
32713 var label = this.el.select('label', true).first();
32714 var icon = this.el.select('i.fa-star', true).first();
32716 if(label && !icon){
32717 this.el.select('.roo-date-split-field-label', true).createChild({
32719 cls : 'text-danger fa fa-lg fa-star',
32720 tooltip : 'This field is required',
32721 style : 'margin-right:5px;'
32725 this.fireEvent('invalid', this, msg);
32728 clearInvalid : function()
32730 var label = this.el.select('label', true).first();
32731 var icon = this.el.select('i.fa-star', true).first();
32737 this.fireEvent('valid', this);
32740 getName: function()
32750 * http://masonry.desandro.com
32752 * The idea is to render all the bricks based on vertical width...
32754 * The original code extends 'outlayer' - we might need to use that....
32760 * @class Roo.bootstrap.LayoutMasonry
32761 * @extends Roo.bootstrap.Component
32762 * Bootstrap Layout Masonry class
32765 * Create a new Element
32766 * @param {Object} config The config object
32769 Roo.bootstrap.LayoutMasonry = function(config){
32771 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32775 Roo.bootstrap.LayoutMasonry.register(this);
32781 * Fire after layout the items
32782 * @param {Roo.bootstrap.LayoutMasonry} this
32783 * @param {Roo.EventObject} e
32790 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
32793 * @cfg {Boolean} isLayoutInstant = no animation?
32795 isLayoutInstant : false, // needed?
32798 * @cfg {Number} boxWidth width of the columns
32803 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
32808 * @cfg {Number} padWidth padding below box..
32813 * @cfg {Number} gutter gutter width..
32818 * @cfg {Number} maxCols maximum number of columns
32824 * @cfg {Boolean} isAutoInitial defalut true
32826 isAutoInitial : true,
32831 * @cfg {Boolean} isHorizontal defalut false
32833 isHorizontal : false,
32835 currentSize : null,
32841 bricks: null, //CompositeElement
32845 _isLayoutInited : false,
32847 // isAlternative : false, // only use for vertical layout...
32850 * @cfg {Number} alternativePadWidth padding below box..
32852 alternativePadWidth : 50,
32854 selectedBrick : [],
32856 getAutoCreate : function(){
32858 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32862 cls: 'blog-masonary-wrapper ' + this.cls,
32864 cls : 'mas-boxes masonary'
32871 getChildContainer: function( )
32873 if (this.boxesEl) {
32874 return this.boxesEl;
32877 this.boxesEl = this.el.select('.mas-boxes').first();
32879 return this.boxesEl;
32883 initEvents : function()
32887 if(this.isAutoInitial){
32888 Roo.log('hook children rendered');
32889 this.on('childrenrendered', function() {
32890 Roo.log('children rendered');
32896 initial : function()
32898 this.selectedBrick = [];
32900 this.currentSize = this.el.getBox(true);
32902 Roo.EventManager.onWindowResize(this.resize, this);
32904 if(!this.isAutoInitial){
32912 //this.layout.defer(500,this);
32916 resize : function()
32918 var cs = this.el.getBox(true);
32921 this.currentSize.width == cs.width &&
32922 this.currentSize.x == cs.x &&
32923 this.currentSize.height == cs.height &&
32924 this.currentSize.y == cs.y
32926 Roo.log("no change in with or X or Y");
32930 this.currentSize = cs;
32936 layout : function()
32938 this._resetLayout();
32940 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32942 this.layoutItems( isInstant );
32944 this._isLayoutInited = true;
32946 this.fireEvent('layout', this);
32950 _resetLayout : function()
32952 if(this.isHorizontal){
32953 this.horizontalMeasureColumns();
32957 this.verticalMeasureColumns();
32961 verticalMeasureColumns : function()
32963 this.getContainerWidth();
32965 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32966 // this.colWidth = Math.floor(this.containerWidth * 0.8);
32970 var boxWidth = this.boxWidth + this.padWidth;
32972 if(this.containerWidth < this.boxWidth){
32973 boxWidth = this.containerWidth
32976 var containerWidth = this.containerWidth;
32978 var cols = Math.floor(containerWidth / boxWidth);
32980 this.cols = Math.max( cols, 1 );
32982 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32984 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32986 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32988 this.colWidth = boxWidth + avail - this.padWidth;
32990 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32991 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
32994 horizontalMeasureColumns : function()
32996 this.getContainerWidth();
32998 var boxWidth = this.boxWidth;
33000 if(this.containerWidth < boxWidth){
33001 boxWidth = this.containerWidth;
33004 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33006 this.el.setHeight(boxWidth);
33010 getContainerWidth : function()
33012 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33015 layoutItems : function( isInstant )
33017 Roo.log(this.bricks);
33019 var items = Roo.apply([], this.bricks);
33021 if(this.isHorizontal){
33022 this._horizontalLayoutItems( items , isInstant );
33026 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33027 // this._verticalAlternativeLayoutItems( items , isInstant );
33031 this._verticalLayoutItems( items , isInstant );
33035 _verticalLayoutItems : function ( items , isInstant)
33037 if ( !items || !items.length ) {
33042 ['xs', 'xs', 'xs', 'tall'],
33043 ['xs', 'xs', 'tall'],
33044 ['xs', 'xs', 'sm'],
33045 ['xs', 'xs', 'xs'],
33051 ['sm', 'xs', 'xs'],
33055 ['tall', 'xs', 'xs', 'xs'],
33056 ['tall', 'xs', 'xs'],
33068 Roo.each(items, function(item, k){
33070 switch (item.size) {
33071 // these layouts take up a full box,
33082 boxes.push([item]);
33105 var filterPattern = function(box, length)
33113 var pattern = box.slice(0, length);
33117 Roo.each(pattern, function(i){
33118 format.push(i.size);
33121 Roo.each(standard, function(s){
33123 if(String(s) != String(format)){
33132 if(!match && length == 1){
33137 filterPattern(box, length - 1);
33141 queue.push(pattern);
33143 box = box.slice(length, box.length);
33145 filterPattern(box, 4);
33151 Roo.each(boxes, function(box, k){
33157 if(box.length == 1){
33162 filterPattern(box, 4);
33166 this._processVerticalLayoutQueue( queue, isInstant );
33170 // _verticalAlternativeLayoutItems : function( items , isInstant )
33172 // if ( !items || !items.length ) {
33176 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33180 _horizontalLayoutItems : function ( items , isInstant)
33182 if ( !items || !items.length || items.length < 3) {
33188 var eItems = items.slice(0, 3);
33190 items = items.slice(3, items.length);
33193 ['xs', 'xs', 'xs', 'wide'],
33194 ['xs', 'xs', 'wide'],
33195 ['xs', 'xs', 'sm'],
33196 ['xs', 'xs', 'xs'],
33202 ['sm', 'xs', 'xs'],
33206 ['wide', 'xs', 'xs', 'xs'],
33207 ['wide', 'xs', 'xs'],
33220 Roo.each(items, function(item, k){
33222 switch (item.size) {
33233 boxes.push([item]);
33257 var filterPattern = function(box, length)
33265 var pattern = box.slice(0, length);
33269 Roo.each(pattern, function(i){
33270 format.push(i.size);
33273 Roo.each(standard, function(s){
33275 if(String(s) != String(format)){
33284 if(!match && length == 1){
33289 filterPattern(box, length - 1);
33293 queue.push(pattern);
33295 box = box.slice(length, box.length);
33297 filterPattern(box, 4);
33303 Roo.each(boxes, function(box, k){
33309 if(box.length == 1){
33314 filterPattern(box, 4);
33321 var pos = this.el.getBox(true);
33325 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33327 var hit_end = false;
33329 Roo.each(queue, function(box){
33333 Roo.each(box, function(b){
33335 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33345 Roo.each(box, function(b){
33347 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33350 mx = Math.max(mx, b.x);
33354 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33358 Roo.each(box, function(b){
33360 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33374 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33377 /** Sets position of item in DOM
33378 * @param {Element} item
33379 * @param {Number} x - horizontal position
33380 * @param {Number} y - vertical position
33381 * @param {Boolean} isInstant - disables transitions
33383 _processVerticalLayoutQueue : function( queue, isInstant )
33385 var pos = this.el.getBox(true);
33390 for (var i = 0; i < this.cols; i++){
33394 Roo.each(queue, function(box, k){
33396 var col = k % this.cols;
33398 Roo.each(box, function(b,kk){
33400 b.el.position('absolute');
33402 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33403 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33405 if(b.size == 'md-left' || b.size == 'md-right'){
33406 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33407 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33410 b.el.setWidth(width);
33411 b.el.setHeight(height);
33413 b.el.select('iframe',true).setSize(width,height);
33417 for (var i = 0; i < this.cols; i++){
33419 if(maxY[i] < maxY[col]){
33424 col = Math.min(col, i);
33428 x = pos.x + col * (this.colWidth + this.padWidth);
33432 var positions = [];
33434 switch (box.length){
33436 positions = this.getVerticalOneBoxColPositions(x, y, box);
33439 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33442 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33445 positions = this.getVerticalFourBoxColPositions(x, y, box);
33451 Roo.each(box, function(b,kk){
33453 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33455 var sz = b.el.getSize();
33457 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33465 for (var i = 0; i < this.cols; i++){
33466 mY = Math.max(mY, maxY[i]);
33469 this.el.setHeight(mY - pos.y);
33473 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33475 // var pos = this.el.getBox(true);
33478 // var maxX = pos.right;
33480 // var maxHeight = 0;
33482 // Roo.each(items, function(item, k){
33486 // item.el.position('absolute');
33488 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33490 // item.el.setWidth(width);
33492 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33494 // item.el.setHeight(height);
33497 // item.el.setXY([x, y], isInstant ? false : true);
33499 // item.el.setXY([maxX - width, y], isInstant ? false : true);
33502 // y = y + height + this.alternativePadWidth;
33504 // maxHeight = maxHeight + height + this.alternativePadWidth;
33508 // this.el.setHeight(maxHeight);
33512 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33514 var pos = this.el.getBox(true);
33519 var maxX = pos.right;
33521 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33523 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33525 Roo.each(queue, function(box, k){
33527 Roo.each(box, function(b, kk){
33529 b.el.position('absolute');
33531 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33532 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33534 if(b.size == 'md-left' || b.size == 'md-right'){
33535 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33536 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33539 b.el.setWidth(width);
33540 b.el.setHeight(height);
33548 var positions = [];
33550 switch (box.length){
33552 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33555 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33558 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33561 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33567 Roo.each(box, function(b,kk){
33569 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33571 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33579 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33581 Roo.each(eItems, function(b,k){
33583 b.size = (k == 0) ? 'sm' : 'xs';
33584 b.x = (k == 0) ? 2 : 1;
33585 b.y = (k == 0) ? 2 : 1;
33587 b.el.position('absolute');
33589 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33591 b.el.setWidth(width);
33593 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33595 b.el.setHeight(height);
33599 var positions = [];
33602 x : maxX - this.unitWidth * 2 - this.gutter,
33607 x : maxX - this.unitWidth,
33608 y : minY + (this.unitWidth + this.gutter) * 2
33612 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33616 Roo.each(eItems, function(b,k){
33618 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33624 getVerticalOneBoxColPositions : function(x, y, box)
33628 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33630 if(box[0].size == 'md-left'){
33634 if(box[0].size == 'md-right'){
33639 x : x + (this.unitWidth + this.gutter) * rand,
33646 getVerticalTwoBoxColPositions : function(x, y, box)
33650 if(box[0].size == 'xs'){
33654 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33658 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33672 x : x + (this.unitWidth + this.gutter) * 2,
33673 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33680 getVerticalThreeBoxColPositions : function(x, y, box)
33684 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33692 x : x + (this.unitWidth + this.gutter) * 1,
33697 x : x + (this.unitWidth + this.gutter) * 2,
33705 if(box[0].size == 'xs' && box[1].size == 'xs'){
33714 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33718 x : x + (this.unitWidth + this.gutter) * 1,
33732 x : x + (this.unitWidth + this.gutter) * 2,
33737 x : x + (this.unitWidth + this.gutter) * 2,
33738 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33745 getVerticalFourBoxColPositions : function(x, y, box)
33749 if(box[0].size == 'xs'){
33758 y : y + (this.unitHeight + this.gutter) * 1
33763 y : y + (this.unitHeight + this.gutter) * 2
33767 x : x + (this.unitWidth + this.gutter) * 1,
33781 x : x + (this.unitWidth + this.gutter) * 2,
33786 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33787 y : y + (this.unitHeight + this.gutter) * 1
33791 x : x + (this.unitWidth + this.gutter) * 2,
33792 y : y + (this.unitWidth + this.gutter) * 2
33799 getHorizontalOneBoxColPositions : function(maxX, minY, box)
33803 if(box[0].size == 'md-left'){
33805 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33812 if(box[0].size == 'md-right'){
33814 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33815 y : minY + (this.unitWidth + this.gutter) * 1
33821 var rand = Math.floor(Math.random() * (4 - box[0].y));
33824 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33825 y : minY + (this.unitWidth + this.gutter) * rand
33832 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33836 if(box[0].size == 'xs'){
33839 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33844 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33845 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33853 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33858 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33859 y : minY + (this.unitWidth + this.gutter) * 2
33866 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33870 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33873 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33878 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33879 y : minY + (this.unitWidth + this.gutter) * 1
33883 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33884 y : minY + (this.unitWidth + this.gutter) * 2
33891 if(box[0].size == 'xs' && box[1].size == 'xs'){
33894 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33899 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33904 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33905 y : minY + (this.unitWidth + this.gutter) * 1
33913 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33918 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33919 y : minY + (this.unitWidth + this.gutter) * 2
33923 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33924 y : minY + (this.unitWidth + this.gutter) * 2
33931 getHorizontalFourBoxColPositions : function(maxX, minY, box)
33935 if(box[0].size == 'xs'){
33938 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33943 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33948 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),
33953 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33954 y : minY + (this.unitWidth + this.gutter) * 1
33962 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33967 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33968 y : minY + (this.unitWidth + this.gutter) * 2
33972 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33973 y : minY + (this.unitWidth + this.gutter) * 2
33977 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),
33978 y : minY + (this.unitWidth + this.gutter) * 2
33986 * remove a Masonry Brick
33987 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33989 removeBrick : function(brick_id)
33995 for (var i = 0; i<this.bricks.length; i++) {
33996 if (this.bricks[i].id == brick_id) {
33997 this.bricks.splice(i,1);
33998 this.el.dom.removeChild(Roo.get(brick_id).dom);
34005 * adds a Masonry Brick
34006 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34008 addBrick : function(cfg)
34010 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34011 //this.register(cn);
34012 cn.parentId = this.id;
34013 cn.render(this.el);
34018 * register a Masonry Brick
34019 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34022 register : function(brick)
34024 this.bricks.push(brick);
34025 brick.masonryId = this.id;
34029 * clear all the Masonry Brick
34031 clearAll : function()
34034 //this.getChildContainer().dom.innerHTML = "";
34035 this.el.dom.innerHTML = '';
34038 getSelected : function()
34040 if (!this.selectedBrick) {
34044 return this.selectedBrick;
34048 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34052 * register a Masonry Layout
34053 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34056 register : function(layout)
34058 this.groups[layout.id] = layout;
34061 * fetch a Masonry Layout based on the masonry layout ID
34062 * @param {string} the masonry layout to add
34063 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34066 get: function(layout_id) {
34067 if (typeof(this.groups[layout_id]) == 'undefined') {
34070 return this.groups[layout_id] ;
34082 * http://masonry.desandro.com
34084 * The idea is to render all the bricks based on vertical width...
34086 * The original code extends 'outlayer' - we might need to use that....
34092 * @class Roo.bootstrap.LayoutMasonryAuto
34093 * @extends Roo.bootstrap.Component
34094 * Bootstrap Layout Masonry class
34097 * Create a new Element
34098 * @param {Object} config The config object
34101 Roo.bootstrap.LayoutMasonryAuto = function(config){
34102 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34105 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34108 * @cfg {Boolean} isFitWidth - resize the width..
34110 isFitWidth : false, // options..
34112 * @cfg {Boolean} isOriginLeft = left align?
34114 isOriginLeft : true,
34116 * @cfg {Boolean} isOriginTop = top align?
34118 isOriginTop : false,
34120 * @cfg {Boolean} isLayoutInstant = no animation?
34122 isLayoutInstant : false, // needed?
34124 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34126 isResizingContainer : true,
34128 * @cfg {Number} columnWidth width of the columns
34134 * @cfg {Number} maxCols maximum number of columns
34139 * @cfg {Number} padHeight padding below box..
34145 * @cfg {Boolean} isAutoInitial defalut true
34148 isAutoInitial : true,
34154 initialColumnWidth : 0,
34155 currentSize : null,
34157 colYs : null, // array.
34164 bricks: null, //CompositeElement
34165 cols : 0, // array?
34166 // element : null, // wrapped now this.el
34167 _isLayoutInited : null,
34170 getAutoCreate : function(){
34174 cls: 'blog-masonary-wrapper ' + this.cls,
34176 cls : 'mas-boxes masonary'
34183 getChildContainer: function( )
34185 if (this.boxesEl) {
34186 return this.boxesEl;
34189 this.boxesEl = this.el.select('.mas-boxes').first();
34191 return this.boxesEl;
34195 initEvents : function()
34199 if(this.isAutoInitial){
34200 Roo.log('hook children rendered');
34201 this.on('childrenrendered', function() {
34202 Roo.log('children rendered');
34209 initial : function()
34211 this.reloadItems();
34213 this.currentSize = this.el.getBox(true);
34215 /// was window resize... - let's see if this works..
34216 Roo.EventManager.onWindowResize(this.resize, this);
34218 if(!this.isAutoInitial){
34223 this.layout.defer(500,this);
34226 reloadItems: function()
34228 this.bricks = this.el.select('.masonry-brick', true);
34230 this.bricks.each(function(b) {
34231 //Roo.log(b.getSize());
34232 if (!b.attr('originalwidth')) {
34233 b.attr('originalwidth', b.getSize().width);
34238 Roo.log(this.bricks.elements.length);
34241 resize : function()
34244 var cs = this.el.getBox(true);
34246 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34247 Roo.log("no change in with or X");
34250 this.currentSize = cs;
34254 layout : function()
34257 this._resetLayout();
34258 //this._manageStamps();
34260 // don't animate first layout
34261 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34262 this.layoutItems( isInstant );
34264 // flag for initalized
34265 this._isLayoutInited = true;
34268 layoutItems : function( isInstant )
34270 //var items = this._getItemsForLayout( this.items );
34271 // original code supports filtering layout items.. we just ignore it..
34273 this._layoutItems( this.bricks , isInstant );
34275 this._postLayout();
34277 _layoutItems : function ( items , isInstant)
34279 //this.fireEvent( 'layout', this, items );
34282 if ( !items || !items.elements.length ) {
34283 // no items, emit event with empty array
34288 items.each(function(item) {
34289 Roo.log("layout item");
34291 // get x/y object from method
34292 var position = this._getItemLayoutPosition( item );
34294 position.item = item;
34295 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34296 queue.push( position );
34299 this._processLayoutQueue( queue );
34301 /** Sets position of item in DOM
34302 * @param {Element} item
34303 * @param {Number} x - horizontal position
34304 * @param {Number} y - vertical position
34305 * @param {Boolean} isInstant - disables transitions
34307 _processLayoutQueue : function( queue )
34309 for ( var i=0, len = queue.length; i < len; i++ ) {
34310 var obj = queue[i];
34311 obj.item.position('absolute');
34312 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34318 * Any logic you want to do after each layout,
34319 * i.e. size the container
34321 _postLayout : function()
34323 this.resizeContainer();
34326 resizeContainer : function()
34328 if ( !this.isResizingContainer ) {
34331 var size = this._getContainerSize();
34333 this.el.setSize(size.width,size.height);
34334 this.boxesEl.setSize(size.width,size.height);
34340 _resetLayout : function()
34342 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34343 this.colWidth = this.el.getWidth();
34344 //this.gutter = this.el.getWidth();
34346 this.measureColumns();
34352 this.colYs.push( 0 );
34358 measureColumns : function()
34360 this.getContainerWidth();
34361 // if columnWidth is 0, default to outerWidth of first item
34362 if ( !this.columnWidth ) {
34363 var firstItem = this.bricks.first();
34364 Roo.log(firstItem);
34365 this.columnWidth = this.containerWidth;
34366 if (firstItem && firstItem.attr('originalwidth') ) {
34367 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34369 // columnWidth fall back to item of first element
34370 Roo.log("set column width?");
34371 this.initialColumnWidth = this.columnWidth ;
34373 // if first elem has no width, default to size of container
34378 if (this.initialColumnWidth) {
34379 this.columnWidth = this.initialColumnWidth;
34384 // column width is fixed at the top - however if container width get's smaller we should
34387 // this bit calcs how man columns..
34389 var columnWidth = this.columnWidth += this.gutter;
34391 // calculate columns
34392 var containerWidth = this.containerWidth + this.gutter;
34394 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34395 // fix rounding errors, typically with gutters
34396 var excess = columnWidth - containerWidth % columnWidth;
34399 // if overshoot is less than a pixel, round up, otherwise floor it
34400 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34401 cols = Math[ mathMethod ]( cols );
34402 this.cols = Math.max( cols, 1 );
34403 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34405 // padding positioning..
34406 var totalColWidth = this.cols * this.columnWidth;
34407 var padavail = this.containerWidth - totalColWidth;
34408 // so for 2 columns - we need 3 'pads'
34410 var padNeeded = (1+this.cols) * this.padWidth;
34412 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34414 this.columnWidth += padExtra
34415 //this.padWidth = Math.floor(padavail / ( this.cols));
34417 // adjust colum width so that padding is fixed??
34419 // we have 3 columns ... total = width * 3
34420 // we have X left over... that should be used by
34422 //if (this.expandC) {
34430 getContainerWidth : function()
34432 /* // container is parent if fit width
34433 var container = this.isFitWidth ? this.element.parentNode : this.element;
34434 // check that this.size and size are there
34435 // IE8 triggers resize on body size change, so they might not be
34437 var size = getSize( container ); //FIXME
34438 this.containerWidth = size && size.innerWidth; //FIXME
34441 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34445 _getItemLayoutPosition : function( item ) // what is item?
34447 // we resize the item to our columnWidth..
34449 item.setWidth(this.columnWidth);
34450 item.autoBoxAdjust = false;
34452 var sz = item.getSize();
34454 // how many columns does this brick span
34455 var remainder = this.containerWidth % this.columnWidth;
34457 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34458 // round if off by 1 pixel, otherwise use ceil
34459 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34460 colSpan = Math.min( colSpan, this.cols );
34462 // normally this should be '1' as we dont' currently allow multi width columns..
34464 var colGroup = this._getColGroup( colSpan );
34465 // get the minimum Y value from the columns
34466 var minimumY = Math.min.apply( Math, colGroup );
34467 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34469 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
34471 // position the brick
34473 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34474 y: this.currentSize.y + minimumY + this.padHeight
34478 // apply setHeight to necessary columns
34479 var setHeight = minimumY + sz.height + this.padHeight;
34480 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34482 var setSpan = this.cols + 1 - colGroup.length;
34483 for ( var i = 0; i < setSpan; i++ ) {
34484 this.colYs[ shortColIndex + i ] = setHeight ;
34491 * @param {Number} colSpan - number of columns the element spans
34492 * @returns {Array} colGroup
34494 _getColGroup : function( colSpan )
34496 if ( colSpan < 2 ) {
34497 // if brick spans only one column, use all the column Ys
34502 // how many different places could this brick fit horizontally
34503 var groupCount = this.cols + 1 - colSpan;
34504 // for each group potential horizontal position
34505 for ( var i = 0; i < groupCount; i++ ) {
34506 // make an array of colY values for that one group
34507 var groupColYs = this.colYs.slice( i, i + colSpan );
34508 // and get the max value of the array
34509 colGroup[i] = Math.max.apply( Math, groupColYs );
34514 _manageStamp : function( stamp )
34516 var stampSize = stamp.getSize();
34517 var offset = stamp.getBox();
34518 // get the columns that this stamp affects
34519 var firstX = this.isOriginLeft ? offset.x : offset.right;
34520 var lastX = firstX + stampSize.width;
34521 var firstCol = Math.floor( firstX / this.columnWidth );
34522 firstCol = Math.max( 0, firstCol );
34524 var lastCol = Math.floor( lastX / this.columnWidth );
34525 // lastCol should not go over if multiple of columnWidth #425
34526 lastCol -= lastX % this.columnWidth ? 0 : 1;
34527 lastCol = Math.min( this.cols - 1, lastCol );
34529 // set colYs to bottom of the stamp
34530 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34533 for ( var i = firstCol; i <= lastCol; i++ ) {
34534 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34539 _getContainerSize : function()
34541 this.maxY = Math.max.apply( Math, this.colYs );
34546 if ( this.isFitWidth ) {
34547 size.width = this._getContainerFitWidth();
34553 _getContainerFitWidth : function()
34555 var unusedCols = 0;
34556 // count unused columns
34559 if ( this.colYs[i] !== 0 ) {
34564 // fit container to columns that have been used
34565 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34568 needsResizeLayout : function()
34570 var previousWidth = this.containerWidth;
34571 this.getContainerWidth();
34572 return previousWidth !== this.containerWidth;
34587 * @class Roo.bootstrap.MasonryBrick
34588 * @extends Roo.bootstrap.Component
34589 * Bootstrap MasonryBrick class
34592 * Create a new MasonryBrick
34593 * @param {Object} config The config object
34596 Roo.bootstrap.MasonryBrick = function(config){
34598 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34600 Roo.bootstrap.MasonryBrick.register(this);
34606 * When a MasonryBrick is clcik
34607 * @param {Roo.bootstrap.MasonryBrick} this
34608 * @param {Roo.EventObject} e
34614 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34617 * @cfg {String} title
34621 * @cfg {String} html
34625 * @cfg {String} bgimage
34629 * @cfg {String} videourl
34633 * @cfg {String} cls
34637 * @cfg {String} href
34641 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34646 * @cfg {String} placetitle (center|bottom)
34651 * @cfg {Boolean} isFitContainer defalut true
34653 isFitContainer : true,
34656 * @cfg {Boolean} preventDefault defalut false
34658 preventDefault : false,
34661 * @cfg {Boolean} inverse defalut false
34663 maskInverse : false,
34665 getAutoCreate : function()
34667 if(!this.isFitContainer){
34668 return this.getSplitAutoCreate();
34671 var cls = 'masonry-brick masonry-brick-full';
34673 if(this.href.length){
34674 cls += ' masonry-brick-link';
34677 if(this.bgimage.length){
34678 cls += ' masonry-brick-image';
34681 if(this.maskInverse){
34682 cls += ' mask-inverse';
34685 if(!this.html.length && !this.maskInverse && !this.videourl.length){
34686 cls += ' enable-mask';
34690 cls += ' masonry-' + this.size + '-brick';
34693 if(this.placetitle.length){
34695 switch (this.placetitle) {
34697 cls += ' masonry-center-title';
34700 cls += ' masonry-bottom-title';
34707 if(!this.html.length && !this.bgimage.length){
34708 cls += ' masonry-center-title';
34711 if(!this.html.length && this.bgimage.length){
34712 cls += ' masonry-bottom-title';
34717 cls += ' ' + this.cls;
34721 tag: (this.href.length) ? 'a' : 'div',
34726 cls: 'masonry-brick-mask'
34730 cls: 'masonry-brick-paragraph',
34736 if(this.href.length){
34737 cfg.href = this.href;
34740 var cn = cfg.cn[1].cn;
34742 if(this.title.length){
34745 cls: 'masonry-brick-title',
34750 if(this.html.length){
34753 cls: 'masonry-brick-text',
34758 if (!this.title.length && !this.html.length) {
34759 cfg.cn[1].cls += ' hide';
34762 if(this.bgimage.length){
34765 cls: 'masonry-brick-image-view',
34770 if(this.videourl.length){
34771 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34772 // youtube support only?
34775 cls: 'masonry-brick-image-view',
34778 allowfullscreen : true
34786 getSplitAutoCreate : function()
34788 var cls = 'masonry-brick masonry-brick-split';
34790 if(this.href.length){
34791 cls += ' masonry-brick-link';
34794 if(this.bgimage.length){
34795 cls += ' masonry-brick-image';
34799 cls += ' masonry-' + this.size + '-brick';
34802 switch (this.placetitle) {
34804 cls += ' masonry-center-title';
34807 cls += ' masonry-bottom-title';
34810 if(!this.bgimage.length){
34811 cls += ' masonry-center-title';
34814 if(this.bgimage.length){
34815 cls += ' masonry-bottom-title';
34821 cls += ' ' + this.cls;
34825 tag: (this.href.length) ? 'a' : 'div',
34830 cls: 'masonry-brick-split-head',
34834 cls: 'masonry-brick-paragraph',
34841 cls: 'masonry-brick-split-body',
34847 if(this.href.length){
34848 cfg.href = this.href;
34851 if(this.title.length){
34852 cfg.cn[0].cn[0].cn.push({
34854 cls: 'masonry-brick-title',
34859 if(this.html.length){
34860 cfg.cn[1].cn.push({
34862 cls: 'masonry-brick-text',
34867 if(this.bgimage.length){
34868 cfg.cn[0].cn.push({
34870 cls: 'masonry-brick-image-view',
34875 if(this.videourl.length){
34876 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34877 // youtube support only?
34878 cfg.cn[0].cn.cn.push({
34880 cls: 'masonry-brick-image-view',
34883 allowfullscreen : true
34890 initEvents: function()
34892 switch (this.size) {
34925 this.el.on('touchstart', this.onTouchStart, this);
34926 this.el.on('touchmove', this.onTouchMove, this);
34927 this.el.on('touchend', this.onTouchEnd, this);
34928 this.el.on('contextmenu', this.onContextMenu, this);
34930 this.el.on('mouseenter' ,this.enter, this);
34931 this.el.on('mouseleave', this.leave, this);
34932 this.el.on('click', this.onClick, this);
34935 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34936 this.parent().bricks.push(this);
34941 onClick: function(e, el)
34943 var time = this.endTimer - this.startTimer;
34944 // Roo.log(e.preventDefault());
34947 e.preventDefault();
34952 if(!this.preventDefault){
34956 e.preventDefault();
34958 if (this.activeClass != '') {
34959 this.selectBrick();
34962 this.fireEvent('click', this, e);
34965 enter: function(e, el)
34967 e.preventDefault();
34969 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34973 if(this.bgimage.length && this.html.length){
34974 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34978 leave: function(e, el)
34980 e.preventDefault();
34982 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34986 if(this.bgimage.length && this.html.length){
34987 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34991 onTouchStart: function(e, el)
34993 // e.preventDefault();
34995 this.touchmoved = false;
34997 if(!this.isFitContainer){
35001 if(!this.bgimage.length || !this.html.length){
35005 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35007 this.timer = new Date().getTime();
35011 onTouchMove: function(e, el)
35013 this.touchmoved = true;
35016 onContextMenu : function(e,el)
35018 e.preventDefault();
35019 e.stopPropagation();
35023 onTouchEnd: function(e, el)
35025 // e.preventDefault();
35027 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35034 if(!this.bgimage.length || !this.html.length){
35036 if(this.href.length){
35037 window.location.href = this.href;
35043 if(!this.isFitContainer){
35047 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35049 window.location.href = this.href;
35052 //selection on single brick only
35053 selectBrick : function() {
35055 if (!this.parentId) {
35059 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35060 var index = m.selectedBrick.indexOf(this.id);
35063 m.selectedBrick.splice(index,1);
35064 this.el.removeClass(this.activeClass);
35068 for(var i = 0; i < m.selectedBrick.length; i++) {
35069 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35070 b.el.removeClass(b.activeClass);
35073 m.selectedBrick = [];
35075 m.selectedBrick.push(this.id);
35076 this.el.addClass(this.activeClass);
35080 isSelected : function(){
35081 return this.el.hasClass(this.activeClass);
35086 Roo.apply(Roo.bootstrap.MasonryBrick, {
35089 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35091 * register a Masonry Brick
35092 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35095 register : function(brick)
35097 //this.groups[brick.id] = brick;
35098 this.groups.add(brick.id, brick);
35101 * fetch a masonry brick based on the masonry brick ID
35102 * @param {string} the masonry brick to add
35103 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35106 get: function(brick_id)
35108 // if (typeof(this.groups[brick_id]) == 'undefined') {
35111 // return this.groups[brick_id] ;
35113 if(this.groups.key(brick_id)) {
35114 return this.groups.key(brick_id);
35132 * @class Roo.bootstrap.Brick
35133 * @extends Roo.bootstrap.Component
35134 * Bootstrap Brick class
35137 * Create a new Brick
35138 * @param {Object} config The config object
35141 Roo.bootstrap.Brick = function(config){
35142 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35148 * When a Brick is click
35149 * @param {Roo.bootstrap.Brick} this
35150 * @param {Roo.EventObject} e
35156 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35159 * @cfg {String} title
35163 * @cfg {String} html
35167 * @cfg {String} bgimage
35171 * @cfg {String} cls
35175 * @cfg {String} href
35179 * @cfg {String} video
35183 * @cfg {Boolean} square
35187 getAutoCreate : function()
35189 var cls = 'roo-brick';
35191 if(this.href.length){
35192 cls += ' roo-brick-link';
35195 if(this.bgimage.length){
35196 cls += ' roo-brick-image';
35199 if(!this.html.length && !this.bgimage.length){
35200 cls += ' roo-brick-center-title';
35203 if(!this.html.length && this.bgimage.length){
35204 cls += ' roo-brick-bottom-title';
35208 cls += ' ' + this.cls;
35212 tag: (this.href.length) ? 'a' : 'div',
35217 cls: 'roo-brick-paragraph',
35223 if(this.href.length){
35224 cfg.href = this.href;
35227 var cn = cfg.cn[0].cn;
35229 if(this.title.length){
35232 cls: 'roo-brick-title',
35237 if(this.html.length){
35240 cls: 'roo-brick-text',
35247 if(this.bgimage.length){
35250 cls: 'roo-brick-image-view',
35258 initEvents: function()
35260 if(this.title.length || this.html.length){
35261 this.el.on('mouseenter' ,this.enter, this);
35262 this.el.on('mouseleave', this.leave, this);
35265 Roo.EventManager.onWindowResize(this.resize, this);
35267 if(this.bgimage.length){
35268 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35269 this.imageEl.on('load', this.onImageLoad, this);
35276 onImageLoad : function()
35281 resize : function()
35283 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35285 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35287 if(this.bgimage.length){
35288 var image = this.el.select('.roo-brick-image-view', true).first();
35290 image.setWidth(paragraph.getWidth());
35293 image.setHeight(paragraph.getWidth());
35296 this.el.setHeight(image.getHeight());
35297 paragraph.setHeight(image.getHeight());
35303 enter: function(e, el)
35305 e.preventDefault();
35307 if(this.bgimage.length){
35308 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35309 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35313 leave: function(e, el)
35315 e.preventDefault();
35317 if(this.bgimage.length){
35318 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35319 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35334 * @class Roo.bootstrap.NumberField
35335 * @extends Roo.bootstrap.Input
35336 * Bootstrap NumberField class
35342 * Create a new NumberField
35343 * @param {Object} config The config object
35346 Roo.bootstrap.NumberField = function(config){
35347 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35350 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35353 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35355 allowDecimals : true,
35357 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35359 decimalSeparator : ".",
35361 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35363 decimalPrecision : 2,
35365 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35367 allowNegative : true,
35370 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35374 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35376 minValue : Number.NEGATIVE_INFINITY,
35378 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35380 maxValue : Number.MAX_VALUE,
35382 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35384 minText : "The minimum value for this field is {0}",
35386 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35388 maxText : "The maximum value for this field is {0}",
35390 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35391 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35393 nanText : "{0} is not a valid number",
35395 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35397 thousandsDelimiter : false,
35399 * @cfg {String} valueAlign alignment of value
35401 valueAlign : "left",
35403 getAutoCreate : function()
35405 var hiddenInput = {
35409 cls: 'hidden-number-input'
35413 hiddenInput.name = this.name;
35418 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35420 this.name = hiddenInput.name;
35422 if(cfg.cn.length > 0) {
35423 cfg.cn.push(hiddenInput);
35430 initEvents : function()
35432 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35434 var allowed = "0123456789";
35436 if(this.allowDecimals){
35437 allowed += this.decimalSeparator;
35440 if(this.allowNegative){
35444 if(this.thousandsDelimiter) {
35448 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35450 var keyPress = function(e){
35452 var k = e.getKey();
35454 var c = e.getCharCode();
35457 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35458 allowed.indexOf(String.fromCharCode(c)) === -1
35464 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35468 if(allowed.indexOf(String.fromCharCode(c)) === -1){
35473 this.el.on("keypress", keyPress, this);
35476 validateValue : function(value)
35479 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35483 var num = this.parseValue(value);
35486 this.markInvalid(String.format(this.nanText, value));
35490 if(num < this.minValue){
35491 this.markInvalid(String.format(this.minText, this.minValue));
35495 if(num > this.maxValue){
35496 this.markInvalid(String.format(this.maxText, this.maxValue));
35503 getValue : function()
35505 var v = this.hiddenEl().getValue();
35507 return this.fixPrecision(this.parseValue(v));
35510 parseValue : function(value)
35512 if(this.thousandsDelimiter) {
35514 r = new RegExp(",", "g");
35515 value = value.replace(r, "");
35518 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35519 return isNaN(value) ? '' : value;
35522 fixPrecision : function(value)
35524 if(this.thousandsDelimiter) {
35526 r = new RegExp(",", "g");
35527 value = value.replace(r, "");
35530 var nan = isNaN(value);
35532 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35533 return nan ? '' : value;
35535 return parseFloat(value).toFixed(this.decimalPrecision);
35538 setValue : function(v)
35540 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35546 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35548 this.inputEl().dom.value = (v == '') ? '' :
35549 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35551 if(!this.allowZero && v === '0') {
35552 this.hiddenEl().dom.value = '';
35553 this.inputEl().dom.value = '';
35560 decimalPrecisionFcn : function(v)
35562 return Math.floor(v);
35565 beforeBlur : function()
35567 var v = this.parseValue(this.getRawValue());
35569 if(v || v === 0 || v === ''){
35574 hiddenEl : function()
35576 return this.el.select('input.hidden-number-input',true).first();
35588 * @class Roo.bootstrap.DocumentSlider
35589 * @extends Roo.bootstrap.Component
35590 * Bootstrap DocumentSlider class
35593 * Create a new DocumentViewer
35594 * @param {Object} config The config object
35597 Roo.bootstrap.DocumentSlider = function(config){
35598 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35605 * Fire after initEvent
35606 * @param {Roo.bootstrap.DocumentSlider} this
35611 * Fire after update
35612 * @param {Roo.bootstrap.DocumentSlider} this
35618 * @param {Roo.bootstrap.DocumentSlider} this
35624 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35630 getAutoCreate : function()
35634 cls : 'roo-document-slider',
35638 cls : 'roo-document-slider-header',
35642 cls : 'roo-document-slider-header-title'
35648 cls : 'roo-document-slider-body',
35652 cls : 'roo-document-slider-prev',
35656 cls : 'fa fa-chevron-left'
35662 cls : 'roo-document-slider-thumb',
35666 cls : 'roo-document-slider-image'
35672 cls : 'roo-document-slider-next',
35676 cls : 'fa fa-chevron-right'
35688 initEvents : function()
35690 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35691 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35693 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35694 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35696 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35697 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35699 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35700 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35702 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35703 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35705 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35706 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35708 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35709 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35711 this.thumbEl.on('click', this.onClick, this);
35713 this.prevIndicator.on('click', this.prev, this);
35715 this.nextIndicator.on('click', this.next, this);
35719 initial : function()
35721 if(this.files.length){
35722 this.indicator = 1;
35726 this.fireEvent('initial', this);
35729 update : function()
35731 this.imageEl.attr('src', this.files[this.indicator - 1]);
35733 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35735 this.prevIndicator.show();
35737 if(this.indicator == 1){
35738 this.prevIndicator.hide();
35741 this.nextIndicator.show();
35743 if(this.indicator == this.files.length){
35744 this.nextIndicator.hide();
35747 this.thumbEl.scrollTo('top');
35749 this.fireEvent('update', this);
35752 onClick : function(e)
35754 e.preventDefault();
35756 this.fireEvent('click', this);
35761 e.preventDefault();
35763 this.indicator = Math.max(1, this.indicator - 1);
35770 e.preventDefault();
35772 this.indicator = Math.min(this.files.length, this.indicator + 1);
35786 * @class Roo.bootstrap.RadioSet
35787 * @extends Roo.bootstrap.Input
35788 * Bootstrap RadioSet class
35789 * @cfg {String} indicatorpos (left|right) default left
35790 * @cfg {Boolean} inline (true|false) inline the element (default true)
35791 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35793 * Create a new RadioSet
35794 * @param {Object} config The config object
35797 Roo.bootstrap.RadioSet = function(config){
35799 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35803 Roo.bootstrap.RadioSet.register(this);
35808 * Fires when the element is checked or unchecked.
35809 * @param {Roo.bootstrap.RadioSet} this This radio
35810 * @param {Roo.bootstrap.Radio} item The checked item
35815 * Fires when the element is click.
35816 * @param {Roo.bootstrap.RadioSet} this This radio set
35817 * @param {Roo.bootstrap.Radio} item The checked item
35818 * @param {Roo.EventObject} e The event object
35825 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
35833 indicatorpos : 'left',
35835 getAutoCreate : function()
35839 cls : 'roo-radio-set-label',
35843 html : this.fieldLabel
35847 if (Roo.bootstrap.version == 3) {
35850 if(this.indicatorpos == 'left'){
35853 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35854 tooltip : 'This field is required'
35859 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35860 tooltip : 'This field is required'
35866 cls : 'roo-radio-set-items'
35869 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35871 if (align === 'left' && this.fieldLabel.length) {
35874 cls : "roo-radio-set-right",
35880 if(this.labelWidth > 12){
35881 label.style = "width: " + this.labelWidth + 'px';
35884 if(this.labelWidth < 13 && this.labelmd == 0){
35885 this.labelmd = this.labelWidth;
35888 if(this.labellg > 0){
35889 label.cls += ' col-lg-' + this.labellg;
35890 items.cls += ' col-lg-' + (12 - this.labellg);
35893 if(this.labelmd > 0){
35894 label.cls += ' col-md-' + this.labelmd;
35895 items.cls += ' col-md-' + (12 - this.labelmd);
35898 if(this.labelsm > 0){
35899 label.cls += ' col-sm-' + this.labelsm;
35900 items.cls += ' col-sm-' + (12 - this.labelsm);
35903 if(this.labelxs > 0){
35904 label.cls += ' col-xs-' + this.labelxs;
35905 items.cls += ' col-xs-' + (12 - this.labelxs);
35911 cls : 'roo-radio-set',
35915 cls : 'roo-radio-set-input',
35918 value : this.value ? this.value : ''
35925 if(this.weight.length){
35926 cfg.cls += ' roo-radio-' + this.weight;
35930 cfg.cls += ' roo-radio-set-inline';
35934 ['xs','sm','md','lg'].map(function(size){
35935 if (settings[size]) {
35936 cfg.cls += ' col-' + size + '-' + settings[size];
35944 initEvents : function()
35946 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35947 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35949 if(!this.fieldLabel.length){
35950 this.labelEl.hide();
35953 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35954 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35956 this.indicator = this.indicatorEl();
35958 if(this.indicator){
35959 this.indicator.addClass('invisible');
35962 this.originalValue = this.getValue();
35966 inputEl: function ()
35968 return this.el.select('.roo-radio-set-input', true).first();
35971 getChildContainer : function()
35973 return this.itemsEl;
35976 register : function(item)
35978 this.radioes.push(item);
35982 validate : function()
35984 if(this.getVisibilityEl().hasClass('hidden')){
35990 Roo.each(this.radioes, function(i){
35999 if(this.allowBlank) {
36003 if(this.disabled || valid){
36008 this.markInvalid();
36013 markValid : function()
36015 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36016 this.indicatorEl().removeClass('visible');
36017 this.indicatorEl().addClass('invisible');
36021 if (Roo.bootstrap.version == 3) {
36022 this.el.removeClass([this.invalidClass, this.validClass]);
36023 this.el.addClass(this.validClass);
36025 this.el.removeClass(['is-invalid','is-valid']);
36026 this.el.addClass(['is-valid']);
36028 this.fireEvent('valid', this);
36031 markInvalid : function(msg)
36033 if(this.allowBlank || this.disabled){
36037 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36038 this.indicatorEl().removeClass('invisible');
36039 this.indicatorEl().addClass('visible');
36041 if (Roo.bootstrap.version == 3) {
36042 this.el.removeClass([this.invalidClass, this.validClass]);
36043 this.el.addClass(this.invalidClass);
36045 this.el.removeClass(['is-invalid','is-valid']);
36046 this.el.addClass(['is-invalid']);
36049 this.fireEvent('invalid', this, msg);
36053 setValue : function(v, suppressEvent)
36055 if(this.value === v){
36062 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36065 Roo.each(this.radioes, function(i){
36067 i.el.removeClass('checked');
36070 Roo.each(this.radioes, function(i){
36072 if(i.value === v || i.value.toString() === v.toString()){
36074 i.el.addClass('checked');
36076 if(suppressEvent !== true){
36077 this.fireEvent('check', this, i);
36088 clearInvalid : function(){
36090 if(!this.el || this.preventMark){
36094 this.el.removeClass([this.invalidClass]);
36096 this.fireEvent('valid', this);
36101 Roo.apply(Roo.bootstrap.RadioSet, {
36105 register : function(set)
36107 this.groups[set.name] = set;
36110 get: function(name)
36112 if (typeof(this.groups[name]) == 'undefined') {
36116 return this.groups[name] ;
36122 * Ext JS Library 1.1.1
36123 * Copyright(c) 2006-2007, Ext JS, LLC.
36125 * Originally Released Under LGPL - original licence link has changed is not relivant.
36128 * <script type="text/javascript">
36133 * @class Roo.bootstrap.SplitBar
36134 * @extends Roo.util.Observable
36135 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36139 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36140 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36141 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36142 split.minSize = 100;
36143 split.maxSize = 600;
36144 split.animate = true;
36145 split.on('moved', splitterMoved);
36148 * Create a new SplitBar
36149 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36150 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36151 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36152 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36153 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36154 position of the SplitBar).
36156 Roo.bootstrap.SplitBar = function(cfg){
36161 // dragElement : elm
36162 // resizingElement: el,
36164 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36165 // placement : Roo.bootstrap.SplitBar.LEFT ,
36166 // existingProxy ???
36169 this.el = Roo.get(cfg.dragElement, true);
36170 this.el.dom.unselectable = "on";
36172 this.resizingEl = Roo.get(cfg.resizingElement, true);
36176 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36177 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36180 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36183 * The minimum size of the resizing element. (Defaults to 0)
36189 * The maximum size of the resizing element. (Defaults to 2000)
36192 this.maxSize = 2000;
36195 * Whether to animate the transition to the new size
36198 this.animate = false;
36201 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36204 this.useShim = false;
36209 if(!cfg.existingProxy){
36211 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36213 this.proxy = Roo.get(cfg.existingProxy).dom;
36216 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36219 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36222 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36225 this.dragSpecs = {};
36228 * @private The adapter to use to positon and resize elements
36230 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36231 this.adapter.init(this);
36233 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36235 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36236 this.el.addClass("roo-splitbar-h");
36239 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36240 this.el.addClass("roo-splitbar-v");
36246 * Fires when the splitter is moved (alias for {@link #event-moved})
36247 * @param {Roo.bootstrap.SplitBar} this
36248 * @param {Number} newSize the new width or height
36253 * Fires when the splitter is moved
36254 * @param {Roo.bootstrap.SplitBar} this
36255 * @param {Number} newSize the new width or height
36259 * @event beforeresize
36260 * Fires before the splitter is dragged
36261 * @param {Roo.bootstrap.SplitBar} this
36263 "beforeresize" : true,
36265 "beforeapply" : true
36268 Roo.util.Observable.call(this);
36271 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36272 onStartProxyDrag : function(x, y){
36273 this.fireEvent("beforeresize", this);
36275 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36277 o.enableDisplayMode("block");
36278 // all splitbars share the same overlay
36279 Roo.bootstrap.SplitBar.prototype.overlay = o;
36281 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36282 this.overlay.show();
36283 Roo.get(this.proxy).setDisplayed("block");
36284 var size = this.adapter.getElementSize(this);
36285 this.activeMinSize = this.getMinimumSize();;
36286 this.activeMaxSize = this.getMaximumSize();;
36287 var c1 = size - this.activeMinSize;
36288 var c2 = Math.max(this.activeMaxSize - size, 0);
36289 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36290 this.dd.resetConstraints();
36291 this.dd.setXConstraint(
36292 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36293 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36295 this.dd.setYConstraint(0, 0);
36297 this.dd.resetConstraints();
36298 this.dd.setXConstraint(0, 0);
36299 this.dd.setYConstraint(
36300 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36301 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36304 this.dragSpecs.startSize = size;
36305 this.dragSpecs.startPoint = [x, y];
36306 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36310 * @private Called after the drag operation by the DDProxy
36312 onEndProxyDrag : function(e){
36313 Roo.get(this.proxy).setDisplayed(false);
36314 var endPoint = Roo.lib.Event.getXY(e);
36316 this.overlay.hide();
36319 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36320 newSize = this.dragSpecs.startSize +
36321 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36322 endPoint[0] - this.dragSpecs.startPoint[0] :
36323 this.dragSpecs.startPoint[0] - endPoint[0]
36326 newSize = this.dragSpecs.startSize +
36327 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36328 endPoint[1] - this.dragSpecs.startPoint[1] :
36329 this.dragSpecs.startPoint[1] - endPoint[1]
36332 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36333 if(newSize != this.dragSpecs.startSize){
36334 if(this.fireEvent('beforeapply', this, newSize) !== false){
36335 this.adapter.setElementSize(this, newSize);
36336 this.fireEvent("moved", this, newSize);
36337 this.fireEvent("resize", this, newSize);
36343 * Get the adapter this SplitBar uses
36344 * @return The adapter object
36346 getAdapter : function(){
36347 return this.adapter;
36351 * Set the adapter this SplitBar uses
36352 * @param {Object} adapter A SplitBar adapter object
36354 setAdapter : function(adapter){
36355 this.adapter = adapter;
36356 this.adapter.init(this);
36360 * Gets the minimum size for the resizing element
36361 * @return {Number} The minimum size
36363 getMinimumSize : function(){
36364 return this.minSize;
36368 * Sets the minimum size for the resizing element
36369 * @param {Number} minSize The minimum size
36371 setMinimumSize : function(minSize){
36372 this.minSize = minSize;
36376 * Gets the maximum size for the resizing element
36377 * @return {Number} The maximum size
36379 getMaximumSize : function(){
36380 return this.maxSize;
36384 * Sets the maximum size for the resizing element
36385 * @param {Number} maxSize The maximum size
36387 setMaximumSize : function(maxSize){
36388 this.maxSize = maxSize;
36392 * Sets the initialize size for the resizing element
36393 * @param {Number} size The initial size
36395 setCurrentSize : function(size){
36396 var oldAnimate = this.animate;
36397 this.animate = false;
36398 this.adapter.setElementSize(this, size);
36399 this.animate = oldAnimate;
36403 * Destroy this splitbar.
36404 * @param {Boolean} removeEl True to remove the element
36406 destroy : function(removeEl){
36408 this.shim.remove();
36411 this.proxy.parentNode.removeChild(this.proxy);
36419 * @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.
36421 Roo.bootstrap.SplitBar.createProxy = function(dir){
36422 var proxy = new Roo.Element(document.createElement("div"));
36423 proxy.unselectable();
36424 var cls = 'roo-splitbar-proxy';
36425 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36426 document.body.appendChild(proxy.dom);
36431 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36432 * Default Adapter. It assumes the splitter and resizing element are not positioned
36433 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36435 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36438 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36439 // do nothing for now
36440 init : function(s){
36444 * Called before drag operations to get the current size of the resizing element.
36445 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36447 getElementSize : function(s){
36448 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36449 return s.resizingEl.getWidth();
36451 return s.resizingEl.getHeight();
36456 * Called after drag operations to set the size of the resizing element.
36457 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36458 * @param {Number} newSize The new size to set
36459 * @param {Function} onComplete A function to be invoked when resizing is complete
36461 setElementSize : function(s, newSize, onComplete){
36462 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36464 s.resizingEl.setWidth(newSize);
36466 onComplete(s, newSize);
36469 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36474 s.resizingEl.setHeight(newSize);
36476 onComplete(s, newSize);
36479 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36486 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36487 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36488 * Adapter that moves the splitter element to align with the resized sizing element.
36489 * Used with an absolute positioned SplitBar.
36490 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36491 * document.body, make sure you assign an id to the body element.
36493 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36494 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36495 this.container = Roo.get(container);
36498 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36499 init : function(s){
36500 this.basic.init(s);
36503 getElementSize : function(s){
36504 return this.basic.getElementSize(s);
36507 setElementSize : function(s, newSize, onComplete){
36508 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36511 moveSplitter : function(s){
36512 var yes = Roo.bootstrap.SplitBar;
36513 switch(s.placement){
36515 s.el.setX(s.resizingEl.getRight());
36518 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36521 s.el.setY(s.resizingEl.getBottom());
36524 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36531 * Orientation constant - Create a vertical SplitBar
36535 Roo.bootstrap.SplitBar.VERTICAL = 1;
36538 * Orientation constant - Create a horizontal SplitBar
36542 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36545 * Placement constant - The resizing element is to the left of the splitter element
36549 Roo.bootstrap.SplitBar.LEFT = 1;
36552 * Placement constant - The resizing element is to the right of the splitter element
36556 Roo.bootstrap.SplitBar.RIGHT = 2;
36559 * Placement constant - The resizing element is positioned above the splitter element
36563 Roo.bootstrap.SplitBar.TOP = 3;
36566 * Placement constant - The resizing element is positioned under splitter element
36570 Roo.bootstrap.SplitBar.BOTTOM = 4;
36571 Roo.namespace("Roo.bootstrap.layout");/*
36573 * Ext JS Library 1.1.1
36574 * Copyright(c) 2006-2007, Ext JS, LLC.
36576 * Originally Released Under LGPL - original licence link has changed is not relivant.
36579 * <script type="text/javascript">
36583 * @class Roo.bootstrap.layout.Manager
36584 * @extends Roo.bootstrap.Component
36585 * Base class for layout managers.
36587 Roo.bootstrap.layout.Manager = function(config)
36589 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36595 /** false to disable window resize monitoring @type Boolean */
36596 this.monitorWindowResize = true;
36601 * Fires when a layout is performed.
36602 * @param {Roo.LayoutManager} this
36606 * @event regionresized
36607 * Fires when the user resizes a region.
36608 * @param {Roo.LayoutRegion} region The resized region
36609 * @param {Number} newSize The new size (width for east/west, height for north/south)
36611 "regionresized" : true,
36613 * @event regioncollapsed
36614 * Fires when a region is collapsed.
36615 * @param {Roo.LayoutRegion} region The collapsed region
36617 "regioncollapsed" : true,
36619 * @event regionexpanded
36620 * Fires when a region is expanded.
36621 * @param {Roo.LayoutRegion} region The expanded region
36623 "regionexpanded" : true
36625 this.updating = false;
36628 this.el = Roo.get(config.el);
36634 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36639 monitorWindowResize : true,
36645 onRender : function(ct, position)
36648 this.el = Roo.get(ct);
36651 //this.fireEvent('render',this);
36655 initEvents: function()
36659 // ie scrollbar fix
36660 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36661 document.body.scroll = "no";
36662 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36663 this.el.position('relative');
36665 this.id = this.el.id;
36666 this.el.addClass("roo-layout-container");
36667 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36668 if(this.el.dom != document.body ) {
36669 this.el.on('resize', this.layout,this);
36670 this.el.on('show', this.layout,this);
36676 * Returns true if this layout is currently being updated
36677 * @return {Boolean}
36679 isUpdating : function(){
36680 return this.updating;
36684 * Suspend the LayoutManager from doing auto-layouts while
36685 * making multiple add or remove calls
36687 beginUpdate : function(){
36688 this.updating = true;
36692 * Restore auto-layouts and optionally disable the manager from performing a layout
36693 * @param {Boolean} noLayout true to disable a layout update
36695 endUpdate : function(noLayout){
36696 this.updating = false;
36702 layout: function(){
36706 onRegionResized : function(region, newSize){
36707 this.fireEvent("regionresized", region, newSize);
36711 onRegionCollapsed : function(region){
36712 this.fireEvent("regioncollapsed", region);
36715 onRegionExpanded : function(region){
36716 this.fireEvent("regionexpanded", region);
36720 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36721 * performs box-model adjustments.
36722 * @return {Object} The size as an object {width: (the width), height: (the height)}
36724 getViewSize : function()
36727 if(this.el.dom != document.body){
36728 size = this.el.getSize();
36730 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36732 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36733 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36738 * Returns the Element this layout is bound to.
36739 * @return {Roo.Element}
36741 getEl : function(){
36746 * Returns the specified region.
36747 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36748 * @return {Roo.LayoutRegion}
36750 getRegion : function(target){
36751 return this.regions[target.toLowerCase()];
36754 onWindowResize : function(){
36755 if(this.monitorWindowResize){
36762 * Ext JS Library 1.1.1
36763 * Copyright(c) 2006-2007, Ext JS, LLC.
36765 * Originally Released Under LGPL - original licence link has changed is not relivant.
36768 * <script type="text/javascript">
36771 * @class Roo.bootstrap.layout.Border
36772 * @extends Roo.bootstrap.layout.Manager
36773 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36774 * please see: examples/bootstrap/nested.html<br><br>
36776 <b>The container the layout is rendered into can be either the body element or any other element.
36777 If it is not the body element, the container needs to either be an absolute positioned element,
36778 or you will need to add "position:relative" to the css of the container. You will also need to specify
36779 the container size if it is not the body element.</b>
36782 * Create a new Border
36783 * @param {Object} config Configuration options
36785 Roo.bootstrap.layout.Border = function(config){
36786 config = config || {};
36787 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36791 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36792 if(config[region]){
36793 config[region].region = region;
36794 this.addRegion(config[region]);
36800 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
36802 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36804 parent : false, // this might point to a 'nest' or a ???
36807 * Creates and adds a new region if it doesn't already exist.
36808 * @param {String} target The target region key (north, south, east, west or center).
36809 * @param {Object} config The regions config object
36810 * @return {BorderLayoutRegion} The new region
36812 addRegion : function(config)
36814 if(!this.regions[config.region]){
36815 var r = this.factory(config);
36816 this.bindRegion(r);
36818 return this.regions[config.region];
36822 bindRegion : function(r){
36823 this.regions[r.config.region] = r;
36825 r.on("visibilitychange", this.layout, this);
36826 r.on("paneladded", this.layout, this);
36827 r.on("panelremoved", this.layout, this);
36828 r.on("invalidated", this.layout, this);
36829 r.on("resized", this.onRegionResized, this);
36830 r.on("collapsed", this.onRegionCollapsed, this);
36831 r.on("expanded", this.onRegionExpanded, this);
36835 * Performs a layout update.
36837 layout : function()
36839 if(this.updating) {
36843 // render all the rebions if they have not been done alreayd?
36844 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36845 if(this.regions[region] && !this.regions[region].bodyEl){
36846 this.regions[region].onRender(this.el)
36850 var size = this.getViewSize();
36851 var w = size.width;
36852 var h = size.height;
36857 //var x = 0, y = 0;
36859 var rs = this.regions;
36860 var north = rs["north"];
36861 var south = rs["south"];
36862 var west = rs["west"];
36863 var east = rs["east"];
36864 var center = rs["center"];
36865 //if(this.hideOnLayout){ // not supported anymore
36866 //c.el.setStyle("display", "none");
36868 if(north && north.isVisible()){
36869 var b = north.getBox();
36870 var m = north.getMargins();
36871 b.width = w - (m.left+m.right);
36874 centerY = b.height + b.y + m.bottom;
36875 centerH -= centerY;
36876 north.updateBox(this.safeBox(b));
36878 if(south && south.isVisible()){
36879 var b = south.getBox();
36880 var m = south.getMargins();
36881 b.width = w - (m.left+m.right);
36883 var totalHeight = (b.height + m.top + m.bottom);
36884 b.y = h - totalHeight + m.top;
36885 centerH -= totalHeight;
36886 south.updateBox(this.safeBox(b));
36888 if(west && west.isVisible()){
36889 var b = west.getBox();
36890 var m = west.getMargins();
36891 b.height = centerH - (m.top+m.bottom);
36893 b.y = centerY + m.top;
36894 var totalWidth = (b.width + m.left + m.right);
36895 centerX += totalWidth;
36896 centerW -= totalWidth;
36897 west.updateBox(this.safeBox(b));
36899 if(east && east.isVisible()){
36900 var b = east.getBox();
36901 var m = east.getMargins();
36902 b.height = centerH - (m.top+m.bottom);
36903 var totalWidth = (b.width + m.left + m.right);
36904 b.x = w - totalWidth + m.left;
36905 b.y = centerY + m.top;
36906 centerW -= totalWidth;
36907 east.updateBox(this.safeBox(b));
36910 var m = center.getMargins();
36912 x: centerX + m.left,
36913 y: centerY + m.top,
36914 width: centerW - (m.left+m.right),
36915 height: centerH - (m.top+m.bottom)
36917 //if(this.hideOnLayout){
36918 //center.el.setStyle("display", "block");
36920 center.updateBox(this.safeBox(centerBox));
36923 this.fireEvent("layout", this);
36927 safeBox : function(box){
36928 box.width = Math.max(0, box.width);
36929 box.height = Math.max(0, box.height);
36934 * Adds a ContentPanel (or subclass) to this layout.
36935 * @param {String} target The target region key (north, south, east, west or center).
36936 * @param {Roo.ContentPanel} panel The panel to add
36937 * @return {Roo.ContentPanel} The added panel
36939 add : function(target, panel){
36941 target = target.toLowerCase();
36942 return this.regions[target].add(panel);
36946 * Remove a ContentPanel (or subclass) to this layout.
36947 * @param {String} target The target region key (north, south, east, west or center).
36948 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36949 * @return {Roo.ContentPanel} The removed panel
36951 remove : function(target, panel){
36952 target = target.toLowerCase();
36953 return this.regions[target].remove(panel);
36957 * Searches all regions for a panel with the specified id
36958 * @param {String} panelId
36959 * @return {Roo.ContentPanel} The panel or null if it wasn't found
36961 findPanel : function(panelId){
36962 var rs = this.regions;
36963 for(var target in rs){
36964 if(typeof rs[target] != "function"){
36965 var p = rs[target].getPanel(panelId);
36975 * Searches all regions for a panel with the specified id and activates (shows) it.
36976 * @param {String/ContentPanel} panelId The panels id or the panel itself
36977 * @return {Roo.ContentPanel} The shown panel or null
36979 showPanel : function(panelId) {
36980 var rs = this.regions;
36981 for(var target in rs){
36982 var r = rs[target];
36983 if(typeof r != "function"){
36984 if(r.hasPanel(panelId)){
36985 return r.showPanel(panelId);
36993 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36994 * @param {Roo.state.Provider} provider (optional) An alternate state provider
36997 restoreState : function(provider){
36999 provider = Roo.state.Manager;
37001 var sm = new Roo.LayoutStateManager();
37002 sm.init(this, provider);
37008 * Adds a xtype elements to the layout.
37012 xtype : 'ContentPanel',
37019 xtype : 'NestedLayoutPanel',
37025 items : [ ... list of content panels or nested layout panels.. ]
37029 * @param {Object} cfg Xtype definition of item to add.
37031 addxtype : function(cfg)
37033 // basically accepts a pannel...
37034 // can accept a layout region..!?!?
37035 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37038 // theory? children can only be panels??
37040 //if (!cfg.xtype.match(/Panel$/)) {
37045 if (typeof(cfg.region) == 'undefined') {
37046 Roo.log("Failed to add Panel, region was not set");
37050 var region = cfg.region;
37056 xitems = cfg.items;
37061 if ( region == 'center') {
37062 Roo.log("Center: " + cfg.title);
37068 case 'Content': // ContentPanel (el, cfg)
37069 case 'Scroll': // ContentPanel (el, cfg)
37071 cfg.autoCreate = cfg.autoCreate || true;
37072 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37074 // var el = this.el.createChild();
37075 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37078 this.add(region, ret);
37082 case 'TreePanel': // our new panel!
37083 cfg.el = this.el.createChild();
37084 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37085 this.add(region, ret);
37090 // create a new Layout (which is a Border Layout...
37092 var clayout = cfg.layout;
37093 clayout.el = this.el.createChild();
37094 clayout.items = clayout.items || [];
37098 // replace this exitems with the clayout ones..
37099 xitems = clayout.items;
37101 // force background off if it's in center...
37102 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37103 cfg.background = false;
37105 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37108 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37109 //console.log('adding nested layout panel ' + cfg.toSource());
37110 this.add(region, ret);
37111 nb = {}; /// find first...
37116 // needs grid and region
37118 //var el = this.getRegion(region).el.createChild();
37120 *var el = this.el.createChild();
37121 // create the grid first...
37122 cfg.grid.container = el;
37123 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37126 if (region == 'center' && this.active ) {
37127 cfg.background = false;
37130 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37132 this.add(region, ret);
37134 if (cfg.background) {
37135 // render grid on panel activation (if panel background)
37136 ret.on('activate', function(gp) {
37137 if (!gp.grid.rendered) {
37138 // gp.grid.render(el);
37142 // cfg.grid.render(el);
37148 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37149 // it was the old xcomponent building that caused this before.
37150 // espeically if border is the top element in the tree.
37160 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37162 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37163 this.add(region, ret);
37167 throw "Can not add '" + cfg.xtype + "' to Border";
37173 this.beginUpdate();
37177 Roo.each(xitems, function(i) {
37178 region = nb && i.region ? i.region : false;
37180 var add = ret.addxtype(i);
37183 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37184 if (!i.background) {
37185 abn[region] = nb[region] ;
37192 // make the last non-background panel active..
37193 //if (nb) { Roo.log(abn); }
37196 for(var r in abn) {
37197 region = this.getRegion(r);
37199 // tried using nb[r], but it does not work..
37201 region.showPanel(abn[r]);
37212 factory : function(cfg)
37215 var validRegions = Roo.bootstrap.layout.Border.regions;
37217 var target = cfg.region;
37220 var r = Roo.bootstrap.layout;
37224 return new r.North(cfg);
37226 return new r.South(cfg);
37228 return new r.East(cfg);
37230 return new r.West(cfg);
37232 return new r.Center(cfg);
37234 throw 'Layout region "'+target+'" not supported.';
37241 * Ext JS Library 1.1.1
37242 * Copyright(c) 2006-2007, Ext JS, LLC.
37244 * Originally Released Under LGPL - original licence link has changed is not relivant.
37247 * <script type="text/javascript">
37251 * @class Roo.bootstrap.layout.Basic
37252 * @extends Roo.util.Observable
37253 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37254 * and does not have a titlebar, tabs or any other features. All it does is size and position
37255 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37256 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37257 * @cfg {string} region the region that it inhabits..
37258 * @cfg {bool} skipConfig skip config?
37262 Roo.bootstrap.layout.Basic = function(config){
37264 this.mgr = config.mgr;
37266 this.position = config.region;
37268 var skipConfig = config.skipConfig;
37272 * @scope Roo.BasicLayoutRegion
37276 * @event beforeremove
37277 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37278 * @param {Roo.LayoutRegion} this
37279 * @param {Roo.ContentPanel} panel The panel
37280 * @param {Object} e The cancel event object
37282 "beforeremove" : true,
37284 * @event invalidated
37285 * Fires when the layout for this region is changed.
37286 * @param {Roo.LayoutRegion} this
37288 "invalidated" : true,
37290 * @event visibilitychange
37291 * Fires when this region is shown or hidden
37292 * @param {Roo.LayoutRegion} this
37293 * @param {Boolean} visibility true or false
37295 "visibilitychange" : true,
37297 * @event paneladded
37298 * Fires when a panel is added.
37299 * @param {Roo.LayoutRegion} this
37300 * @param {Roo.ContentPanel} panel The panel
37302 "paneladded" : true,
37304 * @event panelremoved
37305 * Fires when a panel is removed.
37306 * @param {Roo.LayoutRegion} this
37307 * @param {Roo.ContentPanel} panel The panel
37309 "panelremoved" : true,
37311 * @event beforecollapse
37312 * Fires when this region before collapse.
37313 * @param {Roo.LayoutRegion} this
37315 "beforecollapse" : true,
37318 * Fires when this region is collapsed.
37319 * @param {Roo.LayoutRegion} this
37321 "collapsed" : true,
37324 * Fires when this region is expanded.
37325 * @param {Roo.LayoutRegion} this
37330 * Fires when this region is slid into view.
37331 * @param {Roo.LayoutRegion} this
37333 "slideshow" : true,
37336 * Fires when this region slides out of view.
37337 * @param {Roo.LayoutRegion} this
37339 "slidehide" : true,
37341 * @event panelactivated
37342 * Fires when a panel is activated.
37343 * @param {Roo.LayoutRegion} this
37344 * @param {Roo.ContentPanel} panel The activated panel
37346 "panelactivated" : true,
37349 * Fires when the user resizes this region.
37350 * @param {Roo.LayoutRegion} this
37351 * @param {Number} newSize The new size (width for east/west, height for north/south)
37355 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37356 this.panels = new Roo.util.MixedCollection();
37357 this.panels.getKey = this.getPanelId.createDelegate(this);
37359 this.activePanel = null;
37360 // ensure listeners are added...
37362 if (config.listeners || config.events) {
37363 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37364 listeners : config.listeners || {},
37365 events : config.events || {}
37369 if(skipConfig !== true){
37370 this.applyConfig(config);
37374 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37376 getPanelId : function(p){
37380 applyConfig : function(config){
37381 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37382 this.config = config;
37387 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37388 * the width, for horizontal (north, south) the height.
37389 * @param {Number} newSize The new width or height
37391 resizeTo : function(newSize){
37392 var el = this.el ? this.el :
37393 (this.activePanel ? this.activePanel.getEl() : null);
37395 switch(this.position){
37398 el.setWidth(newSize);
37399 this.fireEvent("resized", this, newSize);
37403 el.setHeight(newSize);
37404 this.fireEvent("resized", this, newSize);
37410 getBox : function(){
37411 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37414 getMargins : function(){
37415 return this.margins;
37418 updateBox : function(box){
37420 var el = this.activePanel.getEl();
37421 el.dom.style.left = box.x + "px";
37422 el.dom.style.top = box.y + "px";
37423 this.activePanel.setSize(box.width, box.height);
37427 * Returns the container element for this region.
37428 * @return {Roo.Element}
37430 getEl : function(){
37431 return this.activePanel;
37435 * Returns true if this region is currently visible.
37436 * @return {Boolean}
37438 isVisible : function(){
37439 return this.activePanel ? true : false;
37442 setActivePanel : function(panel){
37443 panel = this.getPanel(panel);
37444 if(this.activePanel && this.activePanel != panel){
37445 this.activePanel.setActiveState(false);
37446 this.activePanel.getEl().setLeftTop(-10000,-10000);
37448 this.activePanel = panel;
37449 panel.setActiveState(true);
37451 panel.setSize(this.box.width, this.box.height);
37453 this.fireEvent("panelactivated", this, panel);
37454 this.fireEvent("invalidated");
37458 * Show the specified panel.
37459 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37460 * @return {Roo.ContentPanel} The shown panel or null
37462 showPanel : function(panel){
37463 panel = this.getPanel(panel);
37465 this.setActivePanel(panel);
37471 * Get the active panel for this region.
37472 * @return {Roo.ContentPanel} The active panel or null
37474 getActivePanel : function(){
37475 return this.activePanel;
37479 * Add the passed ContentPanel(s)
37480 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37481 * @return {Roo.ContentPanel} The panel added (if only one was added)
37483 add : function(panel){
37484 if(arguments.length > 1){
37485 for(var i = 0, len = arguments.length; i < len; i++) {
37486 this.add(arguments[i]);
37490 if(this.hasPanel(panel)){
37491 this.showPanel(panel);
37494 var el = panel.getEl();
37495 if(el.dom.parentNode != this.mgr.el.dom){
37496 this.mgr.el.dom.appendChild(el.dom);
37498 if(panel.setRegion){
37499 panel.setRegion(this);
37501 this.panels.add(panel);
37502 el.setStyle("position", "absolute");
37503 if(!panel.background){
37504 this.setActivePanel(panel);
37505 if(this.config.initialSize && this.panels.getCount()==1){
37506 this.resizeTo(this.config.initialSize);
37509 this.fireEvent("paneladded", this, panel);
37514 * Returns true if the panel is in this region.
37515 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37516 * @return {Boolean}
37518 hasPanel : function(panel){
37519 if(typeof panel == "object"){ // must be panel obj
37520 panel = panel.getId();
37522 return this.getPanel(panel) ? true : false;
37526 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37527 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37528 * @param {Boolean} preservePanel Overrides the config preservePanel option
37529 * @return {Roo.ContentPanel} The panel that was removed
37531 remove : function(panel, preservePanel){
37532 panel = this.getPanel(panel);
37537 this.fireEvent("beforeremove", this, panel, e);
37538 if(e.cancel === true){
37541 var panelId = panel.getId();
37542 this.panels.removeKey(panelId);
37547 * Returns the panel specified or null if it's not in this region.
37548 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37549 * @return {Roo.ContentPanel}
37551 getPanel : function(id){
37552 if(typeof id == "object"){ // must be panel obj
37555 return this.panels.get(id);
37559 * Returns this regions position (north/south/east/west/center).
37562 getPosition: function(){
37563 return this.position;
37567 * Ext JS Library 1.1.1
37568 * Copyright(c) 2006-2007, Ext JS, LLC.
37570 * Originally Released Under LGPL - original licence link has changed is not relivant.
37573 * <script type="text/javascript">
37577 * @class Roo.bootstrap.layout.Region
37578 * @extends Roo.bootstrap.layout.Basic
37579 * This class represents a region in a layout manager.
37581 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37582 * @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})
37583 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
37584 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
37585 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
37586 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
37587 * @cfg {String} title The title for the region (overrides panel titles)
37588 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
37589 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37590 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
37591 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37592 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37593 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37594 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37595 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37596 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37597 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37599 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37600 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37601 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37602 * @cfg {Number} width For East/West panels
37603 * @cfg {Number} height For North/South panels
37604 * @cfg {Boolean} split To show the splitter
37605 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37607 * @cfg {string} cls Extra CSS classes to add to region
37609 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37610 * @cfg {string} region the region that it inhabits..
37613 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37614 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37616 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37617 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37618 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37620 Roo.bootstrap.layout.Region = function(config)
37622 this.applyConfig(config);
37624 var mgr = config.mgr;
37625 var pos = config.region;
37626 config.skipConfig = true;
37627 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37630 this.onRender(mgr.el);
37633 this.visible = true;
37634 this.collapsed = false;
37635 this.unrendered_panels = [];
37638 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37640 position: '', // set by wrapper (eg. north/south etc..)
37641 unrendered_panels : null, // unrendered panels.
37643 tabPosition : false,
37645 mgr: false, // points to 'Border'
37648 createBody : function(){
37649 /** This region's body element
37650 * @type Roo.Element */
37651 this.bodyEl = this.el.createChild({
37653 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37657 onRender: function(ctr, pos)
37659 var dh = Roo.DomHelper;
37660 /** This region's container element
37661 * @type Roo.Element */
37662 this.el = dh.append(ctr.dom, {
37664 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37666 /** This region's title element
37667 * @type Roo.Element */
37669 this.titleEl = dh.append(this.el.dom, {
37671 unselectable: "on",
37672 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37674 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
37675 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37679 this.titleEl.enableDisplayMode();
37680 /** This region's title text element
37681 * @type HTMLElement */
37682 this.titleTextEl = this.titleEl.dom.firstChild;
37683 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37685 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37686 this.closeBtn.enableDisplayMode();
37687 this.closeBtn.on("click", this.closeClicked, this);
37688 this.closeBtn.hide();
37690 this.createBody(this.config);
37691 if(this.config.hideWhenEmpty){
37693 this.on("paneladded", this.validateVisibility, this);
37694 this.on("panelremoved", this.validateVisibility, this);
37696 if(this.autoScroll){
37697 this.bodyEl.setStyle("overflow", "auto");
37699 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37701 //if(c.titlebar !== false){
37702 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37703 this.titleEl.hide();
37705 this.titleEl.show();
37706 if(this.config.title){
37707 this.titleTextEl.innerHTML = this.config.title;
37711 if(this.config.collapsed){
37712 this.collapse(true);
37714 if(this.config.hidden){
37718 if (this.unrendered_panels && this.unrendered_panels.length) {
37719 for (var i =0;i< this.unrendered_panels.length; i++) {
37720 this.add(this.unrendered_panels[i]);
37722 this.unrendered_panels = null;
37728 applyConfig : function(c)
37731 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37732 var dh = Roo.DomHelper;
37733 if(c.titlebar !== false){
37734 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37735 this.collapseBtn.on("click", this.collapse, this);
37736 this.collapseBtn.enableDisplayMode();
37738 if(c.showPin === true || this.showPin){
37739 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37740 this.stickBtn.enableDisplayMode();
37741 this.stickBtn.on("click", this.expand, this);
37742 this.stickBtn.hide();
37747 /** This region's collapsed element
37748 * @type Roo.Element */
37751 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37752 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37755 if(c.floatable !== false){
37756 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37757 this.collapsedEl.on("click", this.collapseClick, this);
37760 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37761 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37762 id: "message", unselectable: "on", style:{"float":"left"}});
37763 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37765 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37766 this.expandBtn.on("click", this.expand, this);
37770 if(this.collapseBtn){
37771 this.collapseBtn.setVisible(c.collapsible == true);
37774 this.cmargins = c.cmargins || this.cmargins ||
37775 (this.position == "west" || this.position == "east" ?
37776 {top: 0, left: 2, right:2, bottom: 0} :
37777 {top: 2, left: 0, right:0, bottom: 2});
37779 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37782 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37784 this.autoScroll = c.autoScroll || false;
37789 this.duration = c.duration || .30;
37790 this.slideDuration = c.slideDuration || .45;
37795 * Returns true if this region is currently visible.
37796 * @return {Boolean}
37798 isVisible : function(){
37799 return this.visible;
37803 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37804 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
37806 //setCollapsedTitle : function(title){
37807 // title = title || " ";
37808 // if(this.collapsedTitleTextEl){
37809 // this.collapsedTitleTextEl.innerHTML = title;
37813 getBox : function(){
37815 // if(!this.collapsed){
37816 b = this.el.getBox(false, true);
37818 // b = this.collapsedEl.getBox(false, true);
37823 getMargins : function(){
37824 return this.margins;
37825 //return this.collapsed ? this.cmargins : this.margins;
37828 highlight : function(){
37829 this.el.addClass("x-layout-panel-dragover");
37832 unhighlight : function(){
37833 this.el.removeClass("x-layout-panel-dragover");
37836 updateBox : function(box)
37838 if (!this.bodyEl) {
37839 return; // not rendered yet..
37843 if(!this.collapsed){
37844 this.el.dom.style.left = box.x + "px";
37845 this.el.dom.style.top = box.y + "px";
37846 this.updateBody(box.width, box.height);
37848 this.collapsedEl.dom.style.left = box.x + "px";
37849 this.collapsedEl.dom.style.top = box.y + "px";
37850 this.collapsedEl.setSize(box.width, box.height);
37853 this.tabs.autoSizeTabs();
37857 updateBody : function(w, h)
37860 this.el.setWidth(w);
37861 w -= this.el.getBorderWidth("rl");
37862 if(this.config.adjustments){
37863 w += this.config.adjustments[0];
37866 if(h !== null && h > 0){
37867 this.el.setHeight(h);
37868 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37869 h -= this.el.getBorderWidth("tb");
37870 if(this.config.adjustments){
37871 h += this.config.adjustments[1];
37873 this.bodyEl.setHeight(h);
37875 h = this.tabs.syncHeight(h);
37878 if(this.panelSize){
37879 w = w !== null ? w : this.panelSize.width;
37880 h = h !== null ? h : this.panelSize.height;
37882 if(this.activePanel){
37883 var el = this.activePanel.getEl();
37884 w = w !== null ? w : el.getWidth();
37885 h = h !== null ? h : el.getHeight();
37886 this.panelSize = {width: w, height: h};
37887 this.activePanel.setSize(w, h);
37889 if(Roo.isIE && this.tabs){
37890 this.tabs.el.repaint();
37895 * Returns the container element for this region.
37896 * @return {Roo.Element}
37898 getEl : function(){
37903 * Hides this region.
37906 //if(!this.collapsed){
37907 this.el.dom.style.left = "-2000px";
37910 // this.collapsedEl.dom.style.left = "-2000px";
37911 // this.collapsedEl.hide();
37913 this.visible = false;
37914 this.fireEvent("visibilitychange", this, false);
37918 * Shows this region if it was previously hidden.
37921 //if(!this.collapsed){
37924 // this.collapsedEl.show();
37926 this.visible = true;
37927 this.fireEvent("visibilitychange", this, true);
37930 closeClicked : function(){
37931 if(this.activePanel){
37932 this.remove(this.activePanel);
37936 collapseClick : function(e){
37938 e.stopPropagation();
37941 e.stopPropagation();
37947 * Collapses this region.
37948 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37951 collapse : function(skipAnim, skipCheck = false){
37952 if(this.collapsed) {
37956 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37958 this.collapsed = true;
37960 this.split.el.hide();
37962 if(this.config.animate && skipAnim !== true){
37963 this.fireEvent("invalidated", this);
37964 this.animateCollapse();
37966 this.el.setLocation(-20000,-20000);
37968 this.collapsedEl.show();
37969 this.fireEvent("collapsed", this);
37970 this.fireEvent("invalidated", this);
37976 animateCollapse : function(){
37981 * Expands this region if it was previously collapsed.
37982 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37983 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37986 expand : function(e, skipAnim){
37988 e.stopPropagation();
37990 if(!this.collapsed || this.el.hasActiveFx()) {
37994 this.afterSlideIn();
37997 this.collapsed = false;
37998 if(this.config.animate && skipAnim !== true){
37999 this.animateExpand();
38003 this.split.el.show();
38005 this.collapsedEl.setLocation(-2000,-2000);
38006 this.collapsedEl.hide();
38007 this.fireEvent("invalidated", this);
38008 this.fireEvent("expanded", this);
38012 animateExpand : function(){
38016 initTabs : function()
38018 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38020 var ts = new Roo.bootstrap.panel.Tabs({
38021 el: this.bodyEl.dom,
38023 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38024 disableTooltips: this.config.disableTabTips,
38025 toolbar : this.config.toolbar
38028 if(this.config.hideTabs){
38029 ts.stripWrap.setDisplayed(false);
38032 ts.resizeTabs = this.config.resizeTabs === true;
38033 ts.minTabWidth = this.config.minTabWidth || 40;
38034 ts.maxTabWidth = this.config.maxTabWidth || 250;
38035 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38036 ts.monitorResize = false;
38037 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38038 ts.bodyEl.addClass('roo-layout-tabs-body');
38039 this.panels.each(this.initPanelAsTab, this);
38042 initPanelAsTab : function(panel){
38043 var ti = this.tabs.addTab(
38047 this.config.closeOnTab && panel.isClosable(),
38050 if(panel.tabTip !== undefined){
38051 ti.setTooltip(panel.tabTip);
38053 ti.on("activate", function(){
38054 this.setActivePanel(panel);
38057 if(this.config.closeOnTab){
38058 ti.on("beforeclose", function(t, e){
38060 this.remove(panel);
38064 panel.tabItem = ti;
38069 updatePanelTitle : function(panel, title)
38071 if(this.activePanel == panel){
38072 this.updateTitle(title);
38075 var ti = this.tabs.getTab(panel.getEl().id);
38077 if(panel.tabTip !== undefined){
38078 ti.setTooltip(panel.tabTip);
38083 updateTitle : function(title){
38084 if(this.titleTextEl && !this.config.title){
38085 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38089 setActivePanel : function(panel)
38091 panel = this.getPanel(panel);
38092 if(this.activePanel && this.activePanel != panel){
38093 if(this.activePanel.setActiveState(false) === false){
38097 this.activePanel = panel;
38098 panel.setActiveState(true);
38099 if(this.panelSize){
38100 panel.setSize(this.panelSize.width, this.panelSize.height);
38103 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38105 this.updateTitle(panel.getTitle());
38107 this.fireEvent("invalidated", this);
38109 this.fireEvent("panelactivated", this, panel);
38113 * Shows the specified panel.
38114 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38115 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38117 showPanel : function(panel)
38119 panel = this.getPanel(panel);
38122 var tab = this.tabs.getTab(panel.getEl().id);
38123 if(tab.isHidden()){
38124 this.tabs.unhideTab(tab.id);
38128 this.setActivePanel(panel);
38135 * Get the active panel for this region.
38136 * @return {Roo.ContentPanel} The active panel or null
38138 getActivePanel : function(){
38139 return this.activePanel;
38142 validateVisibility : function(){
38143 if(this.panels.getCount() < 1){
38144 this.updateTitle(" ");
38145 this.closeBtn.hide();
38148 if(!this.isVisible()){
38155 * Adds the passed ContentPanel(s) to this region.
38156 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38157 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38159 add : function(panel)
38161 if(arguments.length > 1){
38162 for(var i = 0, len = arguments.length; i < len; i++) {
38163 this.add(arguments[i]);
38168 // if we have not been rendered yet, then we can not really do much of this..
38169 if (!this.bodyEl) {
38170 this.unrendered_panels.push(panel);
38177 if(this.hasPanel(panel)){
38178 this.showPanel(panel);
38181 panel.setRegion(this);
38182 this.panels.add(panel);
38183 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38184 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38185 // and hide them... ???
38186 this.bodyEl.dom.appendChild(panel.getEl().dom);
38187 if(panel.background !== true){
38188 this.setActivePanel(panel);
38190 this.fireEvent("paneladded", this, panel);
38197 this.initPanelAsTab(panel);
38201 if(panel.background !== true){
38202 this.tabs.activate(panel.getEl().id);
38204 this.fireEvent("paneladded", this, panel);
38209 * Hides the tab for the specified panel.
38210 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38212 hidePanel : function(panel){
38213 if(this.tabs && (panel = this.getPanel(panel))){
38214 this.tabs.hideTab(panel.getEl().id);
38219 * Unhides the tab for a previously hidden panel.
38220 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38222 unhidePanel : function(panel){
38223 if(this.tabs && (panel = this.getPanel(panel))){
38224 this.tabs.unhideTab(panel.getEl().id);
38228 clearPanels : function(){
38229 while(this.panels.getCount() > 0){
38230 this.remove(this.panels.first());
38235 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38236 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38237 * @param {Boolean} preservePanel Overrides the config preservePanel option
38238 * @return {Roo.ContentPanel} The panel that was removed
38240 remove : function(panel, preservePanel)
38242 panel = this.getPanel(panel);
38247 this.fireEvent("beforeremove", this, panel, e);
38248 if(e.cancel === true){
38251 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38252 var panelId = panel.getId();
38253 this.panels.removeKey(panelId);
38255 document.body.appendChild(panel.getEl().dom);
38258 this.tabs.removeTab(panel.getEl().id);
38259 }else if (!preservePanel){
38260 this.bodyEl.dom.removeChild(panel.getEl().dom);
38262 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38263 var p = this.panels.first();
38264 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38265 tempEl.appendChild(p.getEl().dom);
38266 this.bodyEl.update("");
38267 this.bodyEl.dom.appendChild(p.getEl().dom);
38269 this.updateTitle(p.getTitle());
38271 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38272 this.setActivePanel(p);
38274 panel.setRegion(null);
38275 if(this.activePanel == panel){
38276 this.activePanel = null;
38278 if(this.config.autoDestroy !== false && preservePanel !== true){
38279 try{panel.destroy();}catch(e){}
38281 this.fireEvent("panelremoved", this, panel);
38286 * Returns the TabPanel component used by this region
38287 * @return {Roo.TabPanel}
38289 getTabs : function(){
38293 createTool : function(parentEl, className){
38294 var btn = Roo.DomHelper.append(parentEl, {
38296 cls: "x-layout-tools-button",
38299 cls: "roo-layout-tools-button-inner " + className,
38303 btn.addClassOnOver("roo-layout-tools-button-over");
38308 * Ext JS Library 1.1.1
38309 * Copyright(c) 2006-2007, Ext JS, LLC.
38311 * Originally Released Under LGPL - original licence link has changed is not relivant.
38314 * <script type="text/javascript">
38320 * @class Roo.SplitLayoutRegion
38321 * @extends Roo.LayoutRegion
38322 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38324 Roo.bootstrap.layout.Split = function(config){
38325 this.cursor = config.cursor;
38326 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38329 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38331 splitTip : "Drag to resize.",
38332 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38333 useSplitTips : false,
38335 applyConfig : function(config){
38336 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38339 onRender : function(ctr,pos) {
38341 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38342 if(!this.config.split){
38347 var splitEl = Roo.DomHelper.append(ctr.dom, {
38349 id: this.el.id + "-split",
38350 cls: "roo-layout-split roo-layout-split-"+this.position,
38353 /** The SplitBar for this region
38354 * @type Roo.SplitBar */
38355 // does not exist yet...
38356 Roo.log([this.position, this.orientation]);
38358 this.split = new Roo.bootstrap.SplitBar({
38359 dragElement : splitEl,
38360 resizingElement: this.el,
38361 orientation : this.orientation
38364 this.split.on("moved", this.onSplitMove, this);
38365 this.split.useShim = this.config.useShim === true;
38366 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38367 if(this.useSplitTips){
38368 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38370 //if(config.collapsible){
38371 // this.split.el.on("dblclick", this.collapse, this);
38374 if(typeof this.config.minSize != "undefined"){
38375 this.split.minSize = this.config.minSize;
38377 if(typeof this.config.maxSize != "undefined"){
38378 this.split.maxSize = this.config.maxSize;
38380 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38381 this.hideSplitter();
38386 getHMaxSize : function(){
38387 var cmax = this.config.maxSize || 10000;
38388 var center = this.mgr.getRegion("center");
38389 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38392 getVMaxSize : function(){
38393 var cmax = this.config.maxSize || 10000;
38394 var center = this.mgr.getRegion("center");
38395 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38398 onSplitMove : function(split, newSize){
38399 this.fireEvent("resized", this, newSize);
38403 * Returns the {@link Roo.SplitBar} for this region.
38404 * @return {Roo.SplitBar}
38406 getSplitBar : function(){
38411 this.hideSplitter();
38412 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38415 hideSplitter : function(){
38417 this.split.el.setLocation(-2000,-2000);
38418 this.split.el.hide();
38424 this.split.el.show();
38426 Roo.bootstrap.layout.Split.superclass.show.call(this);
38429 beforeSlide: function(){
38430 if(Roo.isGecko){// firefox overflow auto bug workaround
38431 this.bodyEl.clip();
38433 this.tabs.bodyEl.clip();
38435 if(this.activePanel){
38436 this.activePanel.getEl().clip();
38438 if(this.activePanel.beforeSlide){
38439 this.activePanel.beforeSlide();
38445 afterSlide : function(){
38446 if(Roo.isGecko){// firefox overflow auto bug workaround
38447 this.bodyEl.unclip();
38449 this.tabs.bodyEl.unclip();
38451 if(this.activePanel){
38452 this.activePanel.getEl().unclip();
38453 if(this.activePanel.afterSlide){
38454 this.activePanel.afterSlide();
38460 initAutoHide : function(){
38461 if(this.autoHide !== false){
38462 if(!this.autoHideHd){
38463 var st = new Roo.util.DelayedTask(this.slideIn, this);
38464 this.autoHideHd = {
38465 "mouseout": function(e){
38466 if(!e.within(this.el, true)){
38470 "mouseover" : function(e){
38476 this.el.on(this.autoHideHd);
38480 clearAutoHide : function(){
38481 if(this.autoHide !== false){
38482 this.el.un("mouseout", this.autoHideHd.mouseout);
38483 this.el.un("mouseover", this.autoHideHd.mouseover);
38487 clearMonitor : function(){
38488 Roo.get(document).un("click", this.slideInIf, this);
38491 // these names are backwards but not changed for compat
38492 slideOut : function(){
38493 if(this.isSlid || this.el.hasActiveFx()){
38496 this.isSlid = true;
38497 if(this.collapseBtn){
38498 this.collapseBtn.hide();
38500 this.closeBtnState = this.closeBtn.getStyle('display');
38501 this.closeBtn.hide();
38503 this.stickBtn.show();
38506 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38507 this.beforeSlide();
38508 this.el.setStyle("z-index", 10001);
38509 this.el.slideIn(this.getSlideAnchor(), {
38510 callback: function(){
38512 this.initAutoHide();
38513 Roo.get(document).on("click", this.slideInIf, this);
38514 this.fireEvent("slideshow", this);
38521 afterSlideIn : function(){
38522 this.clearAutoHide();
38523 this.isSlid = false;
38524 this.clearMonitor();
38525 this.el.setStyle("z-index", "");
38526 if(this.collapseBtn){
38527 this.collapseBtn.show();
38529 this.closeBtn.setStyle('display', this.closeBtnState);
38531 this.stickBtn.hide();
38533 this.fireEvent("slidehide", this);
38536 slideIn : function(cb){
38537 if(!this.isSlid || this.el.hasActiveFx()){
38541 this.isSlid = false;
38542 this.beforeSlide();
38543 this.el.slideOut(this.getSlideAnchor(), {
38544 callback: function(){
38545 this.el.setLeftTop(-10000, -10000);
38547 this.afterSlideIn();
38555 slideInIf : function(e){
38556 if(!e.within(this.el)){
38561 animateCollapse : function(){
38562 this.beforeSlide();
38563 this.el.setStyle("z-index", 20000);
38564 var anchor = this.getSlideAnchor();
38565 this.el.slideOut(anchor, {
38566 callback : function(){
38567 this.el.setStyle("z-index", "");
38568 this.collapsedEl.slideIn(anchor, {duration:.3});
38570 this.el.setLocation(-10000,-10000);
38572 this.fireEvent("collapsed", this);
38579 animateExpand : function(){
38580 this.beforeSlide();
38581 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38582 this.el.setStyle("z-index", 20000);
38583 this.collapsedEl.hide({
38586 this.el.slideIn(this.getSlideAnchor(), {
38587 callback : function(){
38588 this.el.setStyle("z-index", "");
38591 this.split.el.show();
38593 this.fireEvent("invalidated", this);
38594 this.fireEvent("expanded", this);
38622 getAnchor : function(){
38623 return this.anchors[this.position];
38626 getCollapseAnchor : function(){
38627 return this.canchors[this.position];
38630 getSlideAnchor : function(){
38631 return this.sanchors[this.position];
38634 getAlignAdj : function(){
38635 var cm = this.cmargins;
38636 switch(this.position){
38652 getExpandAdj : function(){
38653 var c = this.collapsedEl, cm = this.cmargins;
38654 switch(this.position){
38656 return [-(cm.right+c.getWidth()+cm.left), 0];
38659 return [cm.right+c.getWidth()+cm.left, 0];
38662 return [0, -(cm.top+cm.bottom+c.getHeight())];
38665 return [0, cm.top+cm.bottom+c.getHeight()];
38671 * Ext JS Library 1.1.1
38672 * Copyright(c) 2006-2007, Ext JS, LLC.
38674 * Originally Released Under LGPL - original licence link has changed is not relivant.
38677 * <script type="text/javascript">
38680 * These classes are private internal classes
38682 Roo.bootstrap.layout.Center = function(config){
38683 config.region = "center";
38684 Roo.bootstrap.layout.Region.call(this, config);
38685 this.visible = true;
38686 this.minWidth = config.minWidth || 20;
38687 this.minHeight = config.minHeight || 20;
38690 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38692 // center panel can't be hidden
38696 // center panel can't be hidden
38699 getMinWidth: function(){
38700 return this.minWidth;
38703 getMinHeight: function(){
38704 return this.minHeight;
38718 Roo.bootstrap.layout.North = function(config)
38720 config.region = 'north';
38721 config.cursor = 'n-resize';
38723 Roo.bootstrap.layout.Split.call(this, config);
38727 this.split.placement = Roo.bootstrap.SplitBar.TOP;
38728 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38729 this.split.el.addClass("roo-layout-split-v");
38731 var size = config.initialSize || config.height;
38732 if(typeof size != "undefined"){
38733 this.el.setHeight(size);
38736 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38738 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38742 getBox : function(){
38743 if(this.collapsed){
38744 return this.collapsedEl.getBox();
38746 var box = this.el.getBox();
38748 box.height += this.split.el.getHeight();
38753 updateBox : function(box){
38754 if(this.split && !this.collapsed){
38755 box.height -= this.split.el.getHeight();
38756 this.split.el.setLeft(box.x);
38757 this.split.el.setTop(box.y+box.height);
38758 this.split.el.setWidth(box.width);
38760 if(this.collapsed){
38761 this.updateBody(box.width, null);
38763 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38771 Roo.bootstrap.layout.South = function(config){
38772 config.region = 'south';
38773 config.cursor = 's-resize';
38774 Roo.bootstrap.layout.Split.call(this, config);
38776 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38777 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38778 this.split.el.addClass("roo-layout-split-v");
38780 var size = config.initialSize || config.height;
38781 if(typeof size != "undefined"){
38782 this.el.setHeight(size);
38786 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38787 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38788 getBox : function(){
38789 if(this.collapsed){
38790 return this.collapsedEl.getBox();
38792 var box = this.el.getBox();
38794 var sh = this.split.el.getHeight();
38801 updateBox : function(box){
38802 if(this.split && !this.collapsed){
38803 var sh = this.split.el.getHeight();
38806 this.split.el.setLeft(box.x);
38807 this.split.el.setTop(box.y-sh);
38808 this.split.el.setWidth(box.width);
38810 if(this.collapsed){
38811 this.updateBody(box.width, null);
38813 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38817 Roo.bootstrap.layout.East = function(config){
38818 config.region = "east";
38819 config.cursor = "e-resize";
38820 Roo.bootstrap.layout.Split.call(this, config);
38822 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38823 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38824 this.split.el.addClass("roo-layout-split-h");
38826 var size = config.initialSize || config.width;
38827 if(typeof size != "undefined"){
38828 this.el.setWidth(size);
38831 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38832 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38833 getBox : function(){
38834 if(this.collapsed){
38835 return this.collapsedEl.getBox();
38837 var box = this.el.getBox();
38839 var sw = this.split.el.getWidth();
38846 updateBox : function(box){
38847 if(this.split && !this.collapsed){
38848 var sw = this.split.el.getWidth();
38850 this.split.el.setLeft(box.x);
38851 this.split.el.setTop(box.y);
38852 this.split.el.setHeight(box.height);
38855 if(this.collapsed){
38856 this.updateBody(null, box.height);
38858 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38862 Roo.bootstrap.layout.West = function(config){
38863 config.region = "west";
38864 config.cursor = "w-resize";
38866 Roo.bootstrap.layout.Split.call(this, config);
38868 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38869 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38870 this.split.el.addClass("roo-layout-split-h");
38874 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38875 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38877 onRender: function(ctr, pos)
38879 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38880 var size = this.config.initialSize || this.config.width;
38881 if(typeof size != "undefined"){
38882 this.el.setWidth(size);
38886 getBox : function(){
38887 if(this.collapsed){
38888 return this.collapsedEl.getBox();
38890 var box = this.el.getBox();
38892 box.width += this.split.el.getWidth();
38897 updateBox : function(box){
38898 if(this.split && !this.collapsed){
38899 var sw = this.split.el.getWidth();
38901 this.split.el.setLeft(box.x+box.width);
38902 this.split.el.setTop(box.y);
38903 this.split.el.setHeight(box.height);
38905 if(this.collapsed){
38906 this.updateBody(null, box.height);
38908 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38910 });Roo.namespace("Roo.bootstrap.panel");/*
38912 * Ext JS Library 1.1.1
38913 * Copyright(c) 2006-2007, Ext JS, LLC.
38915 * Originally Released Under LGPL - original licence link has changed is not relivant.
38918 * <script type="text/javascript">
38921 * @class Roo.ContentPanel
38922 * @extends Roo.util.Observable
38923 * A basic ContentPanel element.
38924 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
38925 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
38926 * @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
38927 * @cfg {Boolean} closable True if the panel can be closed/removed
38928 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
38929 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38930 * @cfg {Toolbar} toolbar A toolbar for this panel
38931 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
38932 * @cfg {String} title The title for this panel
38933 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38934 * @cfg {String} url Calls {@link #setUrl} with this value
38935 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38936 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
38937 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
38938 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
38939 * @cfg {Boolean} badges render the badges
38942 * Create a new ContentPanel.
38943 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38944 * @param {String/Object} config A string to set only the title or a config object
38945 * @param {String} content (optional) Set the HTML content for this panel
38946 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38948 Roo.bootstrap.panel.Content = function( config){
38950 this.tpl = config.tpl || false;
38952 var el = config.el;
38953 var content = config.content;
38955 if(config.autoCreate){ // xtype is available if this is called from factory
38958 this.el = Roo.get(el);
38959 if(!this.el && config && config.autoCreate){
38960 if(typeof config.autoCreate == "object"){
38961 if(!config.autoCreate.id){
38962 config.autoCreate.id = config.id||el;
38964 this.el = Roo.DomHelper.append(document.body,
38965 config.autoCreate, true);
38967 var elcfg = { tag: "div",
38968 cls: "roo-layout-inactive-content",
38972 elcfg.html = config.html;
38976 this.el = Roo.DomHelper.append(document.body, elcfg , true);
38979 this.closable = false;
38980 this.loaded = false;
38981 this.active = false;
38984 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38986 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38988 this.wrapEl = this.el; //this.el.wrap();
38990 if (config.toolbar.items) {
38991 ti = config.toolbar.items ;
38992 delete config.toolbar.items ;
38996 this.toolbar.render(this.wrapEl, 'before');
38997 for(var i =0;i < ti.length;i++) {
38998 // Roo.log(['add child', items[i]]);
38999 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39001 this.toolbar.items = nitems;
39002 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39003 delete config.toolbar;
39007 // xtype created footer. - not sure if will work as we normally have to render first..
39008 if (this.footer && !this.footer.el && this.footer.xtype) {
39009 if (!this.wrapEl) {
39010 this.wrapEl = this.el.wrap();
39013 this.footer.container = this.wrapEl.createChild();
39015 this.footer = Roo.factory(this.footer, Roo);
39020 if(typeof config == "string"){
39021 this.title = config;
39023 Roo.apply(this, config);
39027 this.resizeEl = Roo.get(this.resizeEl, true);
39029 this.resizeEl = this.el;
39031 // handle view.xtype
39039 * Fires when this panel is activated.
39040 * @param {Roo.ContentPanel} this
39044 * @event deactivate
39045 * Fires when this panel is activated.
39046 * @param {Roo.ContentPanel} this
39048 "deactivate" : true,
39052 * Fires when this panel is resized if fitToFrame is true.
39053 * @param {Roo.ContentPanel} this
39054 * @param {Number} width The width after any component adjustments
39055 * @param {Number} height The height after any component adjustments
39061 * Fires when this tab is created
39062 * @param {Roo.ContentPanel} this
39073 if(this.autoScroll){
39074 this.resizeEl.setStyle("overflow", "auto");
39076 // fix randome scrolling
39077 //this.el.on('scroll', function() {
39078 // Roo.log('fix random scolling');
39079 // this.scrollTo('top',0);
39082 content = content || this.content;
39084 this.setContent(content);
39086 if(config && config.url){
39087 this.setUrl(this.url, this.params, this.loadOnce);
39092 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39094 if (this.view && typeof(this.view.xtype) != 'undefined') {
39095 this.view.el = this.el.appendChild(document.createElement("div"));
39096 this.view = Roo.factory(this.view);
39097 this.view.render && this.view.render(false, '');
39101 this.fireEvent('render', this);
39104 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39108 setRegion : function(region){
39109 this.region = region;
39110 this.setActiveClass(region && !this.background);
39114 setActiveClass: function(state)
39117 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39118 this.el.setStyle('position','relative');
39120 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39121 this.el.setStyle('position', 'absolute');
39126 * Returns the toolbar for this Panel if one was configured.
39127 * @return {Roo.Toolbar}
39129 getToolbar : function(){
39130 return this.toolbar;
39133 setActiveState : function(active)
39135 this.active = active;
39136 this.setActiveClass(active);
39138 if(this.fireEvent("deactivate", this) === false){
39143 this.fireEvent("activate", this);
39147 * Updates this panel's element
39148 * @param {String} content The new content
39149 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39151 setContent : function(content, loadScripts){
39152 this.el.update(content, loadScripts);
39155 ignoreResize : function(w, h){
39156 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39159 this.lastSize = {width: w, height: h};
39164 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39165 * @return {Roo.UpdateManager} The UpdateManager
39167 getUpdateManager : function(){
39168 return this.el.getUpdateManager();
39171 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39172 * @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:
39175 url: "your-url.php",
39176 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39177 callback: yourFunction,
39178 scope: yourObject, //(optional scope)
39181 text: "Loading...",
39186 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39187 * 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.
39188 * @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}
39189 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39190 * @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.
39191 * @return {Roo.ContentPanel} this
39194 var um = this.el.getUpdateManager();
39195 um.update.apply(um, arguments);
39201 * 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.
39202 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39203 * @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)
39204 * @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)
39205 * @return {Roo.UpdateManager} The UpdateManager
39207 setUrl : function(url, params, loadOnce){
39208 if(this.refreshDelegate){
39209 this.removeListener("activate", this.refreshDelegate);
39211 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39212 this.on("activate", this.refreshDelegate);
39213 return this.el.getUpdateManager();
39216 _handleRefresh : function(url, params, loadOnce){
39217 if(!loadOnce || !this.loaded){
39218 var updater = this.el.getUpdateManager();
39219 updater.update(url, params, this._setLoaded.createDelegate(this));
39223 _setLoaded : function(){
39224 this.loaded = true;
39228 * Returns this panel's id
39231 getId : function(){
39236 * Returns this panel's element - used by regiosn to add.
39237 * @return {Roo.Element}
39239 getEl : function(){
39240 return this.wrapEl || this.el;
39245 adjustForComponents : function(width, height)
39247 //Roo.log('adjustForComponents ');
39248 if(this.resizeEl != this.el){
39249 width -= this.el.getFrameWidth('lr');
39250 height -= this.el.getFrameWidth('tb');
39253 var te = this.toolbar.getEl();
39254 te.setWidth(width);
39255 height -= te.getHeight();
39258 var te = this.footer.getEl();
39259 te.setWidth(width);
39260 height -= te.getHeight();
39264 if(this.adjustments){
39265 width += this.adjustments[0];
39266 height += this.adjustments[1];
39268 return {"width": width, "height": height};
39271 setSize : function(width, height){
39272 if(this.fitToFrame && !this.ignoreResize(width, height)){
39273 if(this.fitContainer && this.resizeEl != this.el){
39274 this.el.setSize(width, height);
39276 var size = this.adjustForComponents(width, height);
39277 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39278 this.fireEvent('resize', this, size.width, size.height);
39283 * Returns this panel's title
39286 getTitle : function(){
39288 if (typeof(this.title) != 'object') {
39293 for (var k in this.title) {
39294 if (!this.title.hasOwnProperty(k)) {
39298 if (k.indexOf('-') >= 0) {
39299 var s = k.split('-');
39300 for (var i = 0; i<s.length; i++) {
39301 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39304 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39311 * Set this panel's title
39312 * @param {String} title
39314 setTitle : function(title){
39315 this.title = title;
39317 this.region.updatePanelTitle(this, title);
39322 * Returns true is this panel was configured to be closable
39323 * @return {Boolean}
39325 isClosable : function(){
39326 return this.closable;
39329 beforeSlide : function(){
39331 this.resizeEl.clip();
39334 afterSlide : function(){
39336 this.resizeEl.unclip();
39340 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39341 * Will fail silently if the {@link #setUrl} method has not been called.
39342 * This does not activate the panel, just updates its content.
39344 refresh : function(){
39345 if(this.refreshDelegate){
39346 this.loaded = false;
39347 this.refreshDelegate();
39352 * Destroys this panel
39354 destroy : function(){
39355 this.el.removeAllListeners();
39356 var tempEl = document.createElement("span");
39357 tempEl.appendChild(this.el.dom);
39358 tempEl.innerHTML = "";
39364 * form - if the content panel contains a form - this is a reference to it.
39365 * @type {Roo.form.Form}
39369 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39370 * This contains a reference to it.
39376 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39386 * @param {Object} cfg Xtype definition of item to add.
39390 getChildContainer: function () {
39391 return this.getEl();
39396 var ret = new Roo.factory(cfg);
39401 if (cfg.xtype.match(/^Form$/)) {
39404 //if (this.footer) {
39405 // el = this.footer.container.insertSibling(false, 'before');
39407 el = this.el.createChild();
39410 this.form = new Roo.form.Form(cfg);
39413 if ( this.form.allItems.length) {
39414 this.form.render(el.dom);
39418 // should only have one of theses..
39419 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39420 // views.. should not be just added - used named prop 'view''
39422 cfg.el = this.el.appendChild(document.createElement("div"));
39425 var ret = new Roo.factory(cfg);
39427 ret.render && ret.render(false, ''); // render blank..
39437 * @class Roo.bootstrap.panel.Grid
39438 * @extends Roo.bootstrap.panel.Content
39440 * Create a new GridPanel.
39441 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39442 * @param {Object} config A the config object
39448 Roo.bootstrap.panel.Grid = function(config)
39452 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39453 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39455 config.el = this.wrapper;
39456 //this.el = this.wrapper;
39458 if (config.container) {
39459 // ctor'ed from a Border/panel.grid
39462 this.wrapper.setStyle("overflow", "hidden");
39463 this.wrapper.addClass('roo-grid-container');
39468 if(config.toolbar){
39469 var tool_el = this.wrapper.createChild();
39470 this.toolbar = Roo.factory(config.toolbar);
39472 if (config.toolbar.items) {
39473 ti = config.toolbar.items ;
39474 delete config.toolbar.items ;
39478 this.toolbar.render(tool_el);
39479 for(var i =0;i < ti.length;i++) {
39480 // Roo.log(['add child', items[i]]);
39481 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39483 this.toolbar.items = nitems;
39485 delete config.toolbar;
39488 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39489 config.grid.scrollBody = true;;
39490 config.grid.monitorWindowResize = false; // turn off autosizing
39491 config.grid.autoHeight = false;
39492 config.grid.autoWidth = false;
39494 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39496 if (config.background) {
39497 // render grid on panel activation (if panel background)
39498 this.on('activate', function(gp) {
39499 if (!gp.grid.rendered) {
39500 gp.grid.render(this.wrapper);
39501 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39506 this.grid.render(this.wrapper);
39507 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39510 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39511 // ??? needed ??? config.el = this.wrapper;
39516 // xtype created footer. - not sure if will work as we normally have to render first..
39517 if (this.footer && !this.footer.el && this.footer.xtype) {
39519 var ctr = this.grid.getView().getFooterPanel(true);
39520 this.footer.dataSource = this.grid.dataSource;
39521 this.footer = Roo.factory(this.footer, Roo);
39522 this.footer.render(ctr);
39532 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39533 getId : function(){
39534 return this.grid.id;
39538 * Returns the grid for this panel
39539 * @return {Roo.bootstrap.Table}
39541 getGrid : function(){
39545 setSize : function(width, height){
39546 if(!this.ignoreResize(width, height)){
39547 var grid = this.grid;
39548 var size = this.adjustForComponents(width, height);
39549 var gridel = grid.getGridEl();
39550 gridel.setSize(size.width, size.height);
39552 var thd = grid.getGridEl().select('thead',true).first();
39553 var tbd = grid.getGridEl().select('tbody', true).first();
39555 tbd.setSize(width, height - thd.getHeight());
39564 beforeSlide : function(){
39565 this.grid.getView().scroller.clip();
39568 afterSlide : function(){
39569 this.grid.getView().scroller.unclip();
39572 destroy : function(){
39573 this.grid.destroy();
39575 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
39580 * @class Roo.bootstrap.panel.Nest
39581 * @extends Roo.bootstrap.panel.Content
39583 * Create a new Panel, that can contain a layout.Border.
39586 * @param {Roo.BorderLayout} layout The layout for this panel
39587 * @param {String/Object} config A string to set only the title or a config object
39589 Roo.bootstrap.panel.Nest = function(config)
39591 // construct with only one argument..
39592 /* FIXME - implement nicer consturctors
39593 if (layout.layout) {
39595 layout = config.layout;
39596 delete config.layout;
39598 if (layout.xtype && !layout.getEl) {
39599 // then layout needs constructing..
39600 layout = Roo.factory(layout, Roo);
39604 config.el = config.layout.getEl();
39606 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39608 config.layout.monitorWindowResize = false; // turn off autosizing
39609 this.layout = config.layout;
39610 this.layout.getEl().addClass("roo-layout-nested-layout");
39611 this.layout.parent = this;
39618 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39620 setSize : function(width, height){
39621 if(!this.ignoreResize(width, height)){
39622 var size = this.adjustForComponents(width, height);
39623 var el = this.layout.getEl();
39624 if (size.height < 1) {
39625 el.setWidth(size.width);
39627 el.setSize(size.width, size.height);
39629 var touch = el.dom.offsetWidth;
39630 this.layout.layout();
39631 // ie requires a double layout on the first pass
39632 if(Roo.isIE && !this.initialized){
39633 this.initialized = true;
39634 this.layout.layout();
39639 // activate all subpanels if not currently active..
39641 setActiveState : function(active){
39642 this.active = active;
39643 this.setActiveClass(active);
39646 this.fireEvent("deactivate", this);
39650 this.fireEvent("activate", this);
39651 // not sure if this should happen before or after..
39652 if (!this.layout) {
39653 return; // should not happen..
39656 for (var r in this.layout.regions) {
39657 reg = this.layout.getRegion(r);
39658 if (reg.getActivePanel()) {
39659 //reg.showPanel(reg.getActivePanel()); // force it to activate..
39660 reg.setActivePanel(reg.getActivePanel());
39663 if (!reg.panels.length) {
39666 reg.showPanel(reg.getPanel(0));
39675 * Returns the nested BorderLayout for this panel
39676 * @return {Roo.BorderLayout}
39678 getLayout : function(){
39679 return this.layout;
39683 * Adds a xtype elements to the layout of the nested panel
39687 xtype : 'ContentPanel',
39694 xtype : 'NestedLayoutPanel',
39700 items : [ ... list of content panels or nested layout panels.. ]
39704 * @param {Object} cfg Xtype definition of item to add.
39706 addxtype : function(cfg) {
39707 return this.layout.addxtype(cfg);
39712 * Ext JS Library 1.1.1
39713 * Copyright(c) 2006-2007, Ext JS, LLC.
39715 * Originally Released Under LGPL - original licence link has changed is not relivant.
39718 * <script type="text/javascript">
39721 * @class Roo.TabPanel
39722 * @extends Roo.util.Observable
39723 * A lightweight tab container.
39727 // basic tabs 1, built from existing content
39728 var tabs = new Roo.TabPanel("tabs1");
39729 tabs.addTab("script", "View Script");
39730 tabs.addTab("markup", "View Markup");
39731 tabs.activate("script");
39733 // more advanced tabs, built from javascript
39734 var jtabs = new Roo.TabPanel("jtabs");
39735 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39737 // set up the UpdateManager
39738 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39739 var updater = tab2.getUpdateManager();
39740 updater.setDefaultUrl("ajax1.htm");
39741 tab2.on('activate', updater.refresh, updater, true);
39743 // Use setUrl for Ajax loading
39744 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39745 tab3.setUrl("ajax2.htm", null, true);
39748 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39751 jtabs.activate("jtabs-1");
39754 * Create a new TabPanel.
39755 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39756 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39758 Roo.bootstrap.panel.Tabs = function(config){
39760 * The container element for this TabPanel.
39761 * @type Roo.Element
39763 this.el = Roo.get(config.el);
39766 if(typeof config == "boolean"){
39767 this.tabPosition = config ? "bottom" : "top";
39769 Roo.apply(this, config);
39773 if(this.tabPosition == "bottom"){
39774 // if tabs are at the bottom = create the body first.
39775 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39776 this.el.addClass("roo-tabs-bottom");
39778 // next create the tabs holders
39780 if (this.tabPosition == "west"){
39782 var reg = this.region; // fake it..
39784 if (!reg.mgr.parent) {
39787 reg = reg.mgr.parent.region;
39789 Roo.log("got nest?");
39791 if (reg.mgr.getRegion('west')) {
39792 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39793 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39794 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39795 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39796 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39804 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39805 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39806 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39807 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39812 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39815 // finally - if tabs are at the top, then create the body last..
39816 if(this.tabPosition != "bottom"){
39817 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39818 * @type Roo.Element
39820 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39821 this.el.addClass("roo-tabs-top");
39825 this.bodyEl.setStyle("position", "relative");
39827 this.active = null;
39828 this.activateDelegate = this.activate.createDelegate(this);
39833 * Fires when the active tab changes
39834 * @param {Roo.TabPanel} this
39835 * @param {Roo.TabPanelItem} activePanel The new active tab
39839 * @event beforetabchange
39840 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39841 * @param {Roo.TabPanel} this
39842 * @param {Object} e Set cancel to true on this object to cancel the tab change
39843 * @param {Roo.TabPanelItem} tab The tab being changed to
39845 "beforetabchange" : true
39848 Roo.EventManager.onWindowResize(this.onResize, this);
39849 this.cpad = this.el.getPadding("lr");
39850 this.hiddenCount = 0;
39853 // toolbar on the tabbar support...
39854 if (this.toolbar) {
39855 alert("no toolbar support yet");
39856 this.toolbar = false;
39858 var tcfg = this.toolbar;
39859 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
39860 this.toolbar = new Roo.Toolbar(tcfg);
39861 if (Roo.isSafari) {
39862 var tbl = tcfg.container.child('table', true);
39863 tbl.setAttribute('width', '100%');
39871 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39874 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39876 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39878 tabPosition : "top",
39880 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39882 currentTabWidth : 0,
39884 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39888 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39892 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39894 preferredTabWidth : 175,
39896 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39898 resizeTabs : false,
39900 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39902 monitorResize : true,
39904 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
39906 toolbar : false, // set by caller..
39908 region : false, /// set by caller
39910 disableTooltips : true, // not used yet...
39913 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39914 * @param {String} id The id of the div to use <b>or create</b>
39915 * @param {String} text The text for the tab
39916 * @param {String} content (optional) Content to put in the TabPanelItem body
39917 * @param {Boolean} closable (optional) True to create a close icon on the tab
39918 * @return {Roo.TabPanelItem} The created TabPanelItem
39920 addTab : function(id, text, content, closable, tpl)
39922 var item = new Roo.bootstrap.panel.TabItem({
39926 closable : closable,
39929 this.addTabItem(item);
39931 item.setContent(content);
39937 * Returns the {@link Roo.TabPanelItem} with the specified id/index
39938 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39939 * @return {Roo.TabPanelItem}
39941 getTab : function(id){
39942 return this.items[id];
39946 * Hides the {@link Roo.TabPanelItem} with the specified id/index
39947 * @param {String/Number} id The id or index of the TabPanelItem to hide.
39949 hideTab : function(id){
39950 var t = this.items[id];
39953 this.hiddenCount++;
39954 this.autoSizeTabs();
39959 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39960 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39962 unhideTab : function(id){
39963 var t = this.items[id];
39965 t.setHidden(false);
39966 this.hiddenCount--;
39967 this.autoSizeTabs();
39972 * Adds an existing {@link Roo.TabPanelItem}.
39973 * @param {Roo.TabPanelItem} item The TabPanelItem to add
39975 addTabItem : function(item)
39977 this.items[item.id] = item;
39978 this.items.push(item);
39979 this.autoSizeTabs();
39980 // if(this.resizeTabs){
39981 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39982 // this.autoSizeTabs();
39984 // item.autoSize();
39989 * Removes a {@link Roo.TabPanelItem}.
39990 * @param {String/Number} id The id or index of the TabPanelItem to remove.
39992 removeTab : function(id){
39993 var items = this.items;
39994 var tab = items[id];
39995 if(!tab) { return; }
39996 var index = items.indexOf(tab);
39997 if(this.active == tab && items.length > 1){
39998 var newTab = this.getNextAvailable(index);
40003 this.stripEl.dom.removeChild(tab.pnode.dom);
40004 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40005 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40007 items.splice(index, 1);
40008 delete this.items[tab.id];
40009 tab.fireEvent("close", tab);
40010 tab.purgeListeners();
40011 this.autoSizeTabs();
40014 getNextAvailable : function(start){
40015 var items = this.items;
40017 // look for a next tab that will slide over to
40018 // replace the one being removed
40019 while(index < items.length){
40020 var item = items[++index];
40021 if(item && !item.isHidden()){
40025 // if one isn't found select the previous tab (on the left)
40028 var item = items[--index];
40029 if(item && !item.isHidden()){
40037 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40038 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40040 disableTab : function(id){
40041 var tab = this.items[id];
40042 if(tab && this.active != tab){
40048 * Enables a {@link Roo.TabPanelItem} that is disabled.
40049 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40051 enableTab : function(id){
40052 var tab = this.items[id];
40057 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40058 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40059 * @return {Roo.TabPanelItem} The TabPanelItem.
40061 activate : function(id)
40063 //Roo.log('activite:' + id);
40065 var tab = this.items[id];
40069 if(tab == this.active || tab.disabled){
40073 this.fireEvent("beforetabchange", this, e, tab);
40074 if(e.cancel !== true && !tab.disabled){
40076 this.active.hide();
40078 this.active = this.items[id];
40079 this.active.show();
40080 this.fireEvent("tabchange", this, this.active);
40086 * Gets the active {@link Roo.TabPanelItem}.
40087 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40089 getActiveTab : function(){
40090 return this.active;
40094 * Updates the tab body element to fit the height of the container element
40095 * for overflow scrolling
40096 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40098 syncHeight : function(targetHeight){
40099 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40100 var bm = this.bodyEl.getMargins();
40101 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40102 this.bodyEl.setHeight(newHeight);
40106 onResize : function(){
40107 if(this.monitorResize){
40108 this.autoSizeTabs();
40113 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40115 beginUpdate : function(){
40116 this.updating = true;
40120 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40122 endUpdate : function(){
40123 this.updating = false;
40124 this.autoSizeTabs();
40128 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40130 autoSizeTabs : function()
40132 var count = this.items.length;
40133 var vcount = count - this.hiddenCount;
40136 this.stripEl.hide();
40138 this.stripEl.show();
40141 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40146 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40147 var availWidth = Math.floor(w / vcount);
40148 var b = this.stripBody;
40149 if(b.getWidth() > w){
40150 var tabs = this.items;
40151 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40152 if(availWidth < this.minTabWidth){
40153 /*if(!this.sleft){ // incomplete scrolling code
40154 this.createScrollButtons();
40157 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40160 if(this.currentTabWidth < this.preferredTabWidth){
40161 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40167 * Returns the number of tabs in this TabPanel.
40170 getCount : function(){
40171 return this.items.length;
40175 * Resizes all the tabs to the passed width
40176 * @param {Number} The new width
40178 setTabWidth : function(width){
40179 this.currentTabWidth = width;
40180 for(var i = 0, len = this.items.length; i < len; i++) {
40181 if(!this.items[i].isHidden()) {
40182 this.items[i].setWidth(width);
40188 * Destroys this TabPanel
40189 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40191 destroy : function(removeEl){
40192 Roo.EventManager.removeResizeListener(this.onResize, this);
40193 for(var i = 0, len = this.items.length; i < len; i++){
40194 this.items[i].purgeListeners();
40196 if(removeEl === true){
40197 this.el.update("");
40202 createStrip : function(container)
40204 var strip = document.createElement("nav");
40205 strip.className = Roo.bootstrap.version == 4 ?
40206 "navbar-light bg-light" :
40207 "navbar navbar-default"; //"x-tabs-wrap";
40208 container.appendChild(strip);
40212 createStripList : function(strip)
40214 // div wrapper for retard IE
40215 // returns the "tr" element.
40216 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40217 //'<div class="x-tabs-strip-wrap">'+
40218 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40219 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40220 return strip.firstChild; //.firstChild.firstChild.firstChild;
40222 createBody : function(container)
40224 var body = document.createElement("div");
40225 Roo.id(body, "tab-body");
40226 //Roo.fly(body).addClass("x-tabs-body");
40227 Roo.fly(body).addClass("tab-content");
40228 container.appendChild(body);
40231 createItemBody :function(bodyEl, id){
40232 var body = Roo.getDom(id);
40234 body = document.createElement("div");
40237 //Roo.fly(body).addClass("x-tabs-item-body");
40238 Roo.fly(body).addClass("tab-pane");
40239 bodyEl.insertBefore(body, bodyEl.firstChild);
40243 createStripElements : function(stripEl, text, closable, tpl)
40245 var td = document.createElement("li"); // was td..
40246 td.className = 'nav-item';
40248 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40251 stripEl.appendChild(td);
40253 td.className = "x-tabs-closable";
40254 if(!this.closeTpl){
40255 this.closeTpl = new Roo.Template(
40256 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40257 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40258 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40261 var el = this.closeTpl.overwrite(td, {"text": text});
40262 var close = el.getElementsByTagName("div")[0];
40263 var inner = el.getElementsByTagName("em")[0];
40264 return {"el": el, "close": close, "inner": inner};
40267 // not sure what this is..
40268 // if(!this.tabTpl){
40269 //this.tabTpl = new Roo.Template(
40270 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40271 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40273 // this.tabTpl = new Roo.Template(
40274 // '<a href="#">' +
40275 // '<span unselectable="on"' +
40276 // (this.disableTooltips ? '' : ' title="{text}"') +
40277 // ' >{text}</span></a>'
40283 var template = tpl || this.tabTpl || false;
40286 template = new Roo.Template(
40287 Roo.bootstrap.version == 4 ?
40289 '<a class="nav-link" href="#" unselectable="on"' +
40290 (this.disableTooltips ? '' : ' title="{text}"') +
40293 '<a class="nav-link" href="#">' +
40294 '<span unselectable="on"' +
40295 (this.disableTooltips ? '' : ' title="{text}"') +
40296 ' >{text}</span></a>'
40301 switch (typeof(template)) {
40305 template = new Roo.Template(template);
40311 var el = template.overwrite(td, {"text": text});
40313 var inner = el.getElementsByTagName("span")[0];
40315 return {"el": el, "inner": inner};
40323 * @class Roo.TabPanelItem
40324 * @extends Roo.util.Observable
40325 * Represents an individual item (tab plus body) in a TabPanel.
40326 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40327 * @param {String} id The id of this TabPanelItem
40328 * @param {String} text The text for the tab of this TabPanelItem
40329 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40331 Roo.bootstrap.panel.TabItem = function(config){
40333 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40334 * @type Roo.TabPanel
40336 this.tabPanel = config.panel;
40338 * The id for this TabPanelItem
40341 this.id = config.id;
40343 this.disabled = false;
40345 this.text = config.text;
40347 this.loaded = false;
40348 this.closable = config.closable;
40351 * The body element for this TabPanelItem.
40352 * @type Roo.Element
40354 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40355 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40356 this.bodyEl.setStyle("display", "block");
40357 this.bodyEl.setStyle("zoom", "1");
40358 //this.hideAction();
40360 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40362 this.el = Roo.get(els.el);
40363 this.inner = Roo.get(els.inner, true);
40364 this.textEl = Roo.bootstrap.version == 4 ?
40365 this.el : Roo.get(this.el.dom.firstChild, true);
40367 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40368 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40371 // this.el.on("mousedown", this.onTabMouseDown, this);
40372 this.el.on("click", this.onTabClick, this);
40374 if(config.closable){
40375 var c = Roo.get(els.close, true);
40376 c.dom.title = this.closeText;
40377 c.addClassOnOver("close-over");
40378 c.on("click", this.closeClick, this);
40384 * Fires when this tab becomes the active tab.
40385 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40386 * @param {Roo.TabPanelItem} this
40390 * @event beforeclose
40391 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40392 * @param {Roo.TabPanelItem} this
40393 * @param {Object} e Set cancel to true on this object to cancel the close.
40395 "beforeclose": true,
40398 * Fires when this tab is closed.
40399 * @param {Roo.TabPanelItem} this
40403 * @event deactivate
40404 * Fires when this tab is no longer the active tab.
40405 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40406 * @param {Roo.TabPanelItem} this
40408 "deactivate" : true
40410 this.hidden = false;
40412 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40415 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40417 purgeListeners : function(){
40418 Roo.util.Observable.prototype.purgeListeners.call(this);
40419 this.el.removeAllListeners();
40422 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40425 this.status_node.addClass("active");
40428 this.tabPanel.stripWrap.repaint();
40430 this.fireEvent("activate", this.tabPanel, this);
40434 * Returns true if this tab is the active tab.
40435 * @return {Boolean}
40437 isActive : function(){
40438 return this.tabPanel.getActiveTab() == this;
40442 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40445 this.status_node.removeClass("active");
40447 this.fireEvent("deactivate", this.tabPanel, this);
40450 hideAction : function(){
40451 this.bodyEl.hide();
40452 this.bodyEl.setStyle("position", "absolute");
40453 this.bodyEl.setLeft("-20000px");
40454 this.bodyEl.setTop("-20000px");
40457 showAction : function(){
40458 this.bodyEl.setStyle("position", "relative");
40459 this.bodyEl.setTop("");
40460 this.bodyEl.setLeft("");
40461 this.bodyEl.show();
40465 * Set the tooltip for the tab.
40466 * @param {String} tooltip The tab's tooltip
40468 setTooltip : function(text){
40469 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40470 this.textEl.dom.qtip = text;
40471 this.textEl.dom.removeAttribute('title');
40473 this.textEl.dom.title = text;
40477 onTabClick : function(e){
40478 e.preventDefault();
40479 this.tabPanel.activate(this.id);
40482 onTabMouseDown : function(e){
40483 e.preventDefault();
40484 this.tabPanel.activate(this.id);
40487 getWidth : function(){
40488 return this.inner.getWidth();
40491 setWidth : function(width){
40492 var iwidth = width - this.linode.getPadding("lr");
40493 this.inner.setWidth(iwidth);
40494 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40495 this.linode.setWidth(width);
40499 * Show or hide the tab
40500 * @param {Boolean} hidden True to hide or false to show.
40502 setHidden : function(hidden){
40503 this.hidden = hidden;
40504 this.linode.setStyle("display", hidden ? "none" : "");
40508 * Returns true if this tab is "hidden"
40509 * @return {Boolean}
40511 isHidden : function(){
40512 return this.hidden;
40516 * Returns the text for this tab
40519 getText : function(){
40523 autoSize : function(){
40524 //this.el.beginMeasure();
40525 this.textEl.setWidth(1);
40527 * #2804 [new] Tabs in Roojs
40528 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40530 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40531 //this.el.endMeasure();
40535 * Sets the text for the tab (Note: this also sets the tooltip text)
40536 * @param {String} text The tab's text and tooltip
40538 setText : function(text){
40540 this.textEl.update(text);
40541 this.setTooltip(text);
40542 //if(!this.tabPanel.resizeTabs){
40543 // this.autoSize();
40547 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40549 activate : function(){
40550 this.tabPanel.activate(this.id);
40554 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40556 disable : function(){
40557 if(this.tabPanel.active != this){
40558 this.disabled = true;
40559 this.status_node.addClass("disabled");
40564 * Enables this TabPanelItem if it was previously disabled.
40566 enable : function(){
40567 this.disabled = false;
40568 this.status_node.removeClass("disabled");
40572 * Sets the content for this TabPanelItem.
40573 * @param {String} content The content
40574 * @param {Boolean} loadScripts true to look for and load scripts
40576 setContent : function(content, loadScripts){
40577 this.bodyEl.update(content, loadScripts);
40581 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40582 * @return {Roo.UpdateManager} The UpdateManager
40584 getUpdateManager : function(){
40585 return this.bodyEl.getUpdateManager();
40589 * Set a URL to be used to load the content for this TabPanelItem.
40590 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40591 * @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)
40592 * @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)
40593 * @return {Roo.UpdateManager} The UpdateManager
40595 setUrl : function(url, params, loadOnce){
40596 if(this.refreshDelegate){
40597 this.un('activate', this.refreshDelegate);
40599 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40600 this.on("activate", this.refreshDelegate);
40601 return this.bodyEl.getUpdateManager();
40605 _handleRefresh : function(url, params, loadOnce){
40606 if(!loadOnce || !this.loaded){
40607 var updater = this.bodyEl.getUpdateManager();
40608 updater.update(url, params, this._setLoaded.createDelegate(this));
40613 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40614 * Will fail silently if the setUrl method has not been called.
40615 * This does not activate the panel, just updates its content.
40617 refresh : function(){
40618 if(this.refreshDelegate){
40619 this.loaded = false;
40620 this.refreshDelegate();
40625 _setLoaded : function(){
40626 this.loaded = true;
40630 closeClick : function(e){
40633 this.fireEvent("beforeclose", this, o);
40634 if(o.cancel !== true){
40635 this.tabPanel.removeTab(this.id);
40639 * The text displayed in the tooltip for the close icon.
40642 closeText : "Close this tab"
40645 * This script refer to:
40646 * Title: International Telephone Input
40647 * Author: Jack O'Connor
40648 * Code version: v12.1.12
40649 * Availability: https://github.com/jackocnr/intl-tel-input.git
40652 Roo.bootstrap.PhoneInputData = function() {
40655 "Afghanistan (افغانستان)",
40660 "Albania (Shqipëri)",
40665 "Algeria (الجزائر)",
40690 "Antigua and Barbuda",
40700 "Armenia (Հայաստան)",
40716 "Austria (Österreich)",
40721 "Azerbaijan (Azərbaycan)",
40731 "Bahrain (البحرين)",
40736 "Bangladesh (বাংলাদেশ)",
40746 "Belarus (Беларусь)",
40751 "Belgium (België)",
40781 "Bosnia and Herzegovina (Босна и Херцеговина)",
40796 "British Indian Ocean Territory",
40801 "British Virgin Islands",
40811 "Bulgaria (България)",
40821 "Burundi (Uburundi)",
40826 "Cambodia (កម្ពុជា)",
40831 "Cameroon (Cameroun)",
40840 ["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"]
40843 "Cape Verde (Kabu Verdi)",
40848 "Caribbean Netherlands",
40859 "Central African Republic (République centrafricaine)",
40879 "Christmas Island",
40885 "Cocos (Keeling) Islands",
40896 "Comoros (جزر القمر)",
40901 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40906 "Congo (Republic) (Congo-Brazzaville)",
40926 "Croatia (Hrvatska)",
40947 "Czech Republic (Česká republika)",
40952 "Denmark (Danmark)",
40967 "Dominican Republic (República Dominicana)",
40971 ["809", "829", "849"]
40989 "Equatorial Guinea (Guinea Ecuatorial)",
41009 "Falkland Islands (Islas Malvinas)",
41014 "Faroe Islands (Føroyar)",
41035 "French Guiana (Guyane française)",
41040 "French Polynesia (Polynésie française)",
41055 "Georgia (საქართველო)",
41060 "Germany (Deutschland)",
41080 "Greenland (Kalaallit Nunaat)",
41117 "Guinea-Bissau (Guiné Bissau)",
41142 "Hungary (Magyarország)",
41147 "Iceland (Ísland)",
41167 "Iraq (العراق)",
41183 "Israel (ישראל)",
41210 "Jordan (الأردن)",
41215 "Kazakhstan (Казахстан)",
41236 "Kuwait (الكويت)",
41241 "Kyrgyzstan (Кыргызстан)",
41251 "Latvia (Latvija)",
41256 "Lebanon (لبنان)",
41271 "Libya (ليبيا)",
41281 "Lithuania (Lietuva)",
41296 "Macedonia (FYROM) (Македонија)",
41301 "Madagascar (Madagasikara)",
41331 "Marshall Islands",
41341 "Mauritania (موريتانيا)",
41346 "Mauritius (Moris)",
41367 "Moldova (Republica Moldova)",
41377 "Mongolia (Монгол)",
41382 "Montenegro (Crna Gora)",
41392 "Morocco (المغرب)",
41398 "Mozambique (Moçambique)",
41403 "Myanmar (Burma) (မြန်မာ)",
41408 "Namibia (Namibië)",
41423 "Netherlands (Nederland)",
41428 "New Caledonia (Nouvelle-Calédonie)",
41463 "North Korea (조선 민주주의 인민 공화국)",
41468 "Northern Mariana Islands",
41484 "Pakistan (پاکستان)",
41494 "Palestine (فلسطين)",
41504 "Papua New Guinea",
41546 "Réunion (La Réunion)",
41552 "Romania (România)",
41568 "Saint Barthélemy",
41579 "Saint Kitts and Nevis",
41589 "Saint Martin (Saint-Martin (partie française))",
41595 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41600 "Saint Vincent and the Grenadines",
41615 "São Tomé and Príncipe (São Tomé e Príncipe)",
41620 "Saudi Arabia (المملكة العربية السعودية)",
41625 "Senegal (Sénégal)",
41655 "Slovakia (Slovensko)",
41660 "Slovenia (Slovenija)",
41670 "Somalia (Soomaaliya)",
41680 "South Korea (대한민국)",
41685 "South Sudan (جنوب السودان)",
41695 "Sri Lanka (ශ්රී ලංකාව)",
41700 "Sudan (السودان)",
41710 "Svalbard and Jan Mayen",
41721 "Sweden (Sverige)",
41726 "Switzerland (Schweiz)",
41731 "Syria (سوريا)",
41776 "Trinidad and Tobago",
41781 "Tunisia (تونس)",
41786 "Turkey (Türkiye)",
41796 "Turks and Caicos Islands",
41806 "U.S. Virgin Islands",
41816 "Ukraine (Україна)",
41821 "United Arab Emirates (الإمارات العربية المتحدة)",
41843 "Uzbekistan (Oʻzbekiston)",
41853 "Vatican City (Città del Vaticano)",
41864 "Vietnam (Việt Nam)",
41869 "Wallis and Futuna (Wallis-et-Futuna)",
41874 "Western Sahara (الصحراء الغربية)",
41880 "Yemen (اليمن)",
41904 * This script refer to:
41905 * Title: International Telephone Input
41906 * Author: Jack O'Connor
41907 * Code version: v12.1.12
41908 * Availability: https://github.com/jackocnr/intl-tel-input.git
41912 * @class Roo.bootstrap.PhoneInput
41913 * @extends Roo.bootstrap.TriggerField
41914 * An input with International dial-code selection
41916 * @cfg {String} defaultDialCode default '+852'
41917 * @cfg {Array} preferedCountries default []
41920 * Create a new PhoneInput.
41921 * @param {Object} config Configuration options
41924 Roo.bootstrap.PhoneInput = function(config) {
41925 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41928 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41930 listWidth: undefined,
41932 selectedClass: 'active',
41934 invalidClass : "has-warning",
41936 validClass: 'has-success',
41938 allowed: '0123456789',
41943 * @cfg {String} defaultDialCode The default dial code when initializing the input
41945 defaultDialCode: '+852',
41948 * @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
41950 preferedCountries: false,
41952 getAutoCreate : function()
41954 var data = Roo.bootstrap.PhoneInputData();
41955 var align = this.labelAlign || this.parentLabelAlign();
41958 this.allCountries = [];
41959 this.dialCodeMapping = [];
41961 for (var i = 0; i < data.length; i++) {
41963 this.allCountries[i] = {
41967 priority: c[3] || 0,
41968 areaCodes: c[4] || null
41970 this.dialCodeMapping[c[2]] = {
41973 priority: c[3] || 0,
41974 areaCodes: c[4] || null
41986 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41987 maxlength: this.max_length,
41988 cls : 'form-control tel-input',
41989 autocomplete: 'new-password'
41992 var hiddenInput = {
41995 cls: 'hidden-tel-input'
41999 hiddenInput.name = this.name;
42002 if (this.disabled) {
42003 input.disabled = true;
42006 var flag_container = {
42023 cls: this.hasFeedback ? 'has-feedback' : '',
42029 cls: 'dial-code-holder',
42036 cls: 'roo-select2-container input-group',
42043 if (this.fieldLabel.length) {
42046 tooltip: 'This field is required'
42052 cls: 'control-label',
42058 html: this.fieldLabel
42061 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42067 if(this.indicatorpos == 'right') {
42068 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42075 if(align == 'left') {
42083 if(this.labelWidth > 12){
42084 label.style = "width: " + this.labelWidth + 'px';
42086 if(this.labelWidth < 13 && this.labelmd == 0){
42087 this.labelmd = this.labelWidth;
42089 if(this.labellg > 0){
42090 label.cls += ' col-lg-' + this.labellg;
42091 input.cls += ' col-lg-' + (12 - this.labellg);
42093 if(this.labelmd > 0){
42094 label.cls += ' col-md-' + this.labelmd;
42095 container.cls += ' col-md-' + (12 - this.labelmd);
42097 if(this.labelsm > 0){
42098 label.cls += ' col-sm-' + this.labelsm;
42099 container.cls += ' col-sm-' + (12 - this.labelsm);
42101 if(this.labelxs > 0){
42102 label.cls += ' col-xs-' + this.labelxs;
42103 container.cls += ' col-xs-' + (12 - this.labelxs);
42113 var settings = this;
42115 ['xs','sm','md','lg'].map(function(size){
42116 if (settings[size]) {
42117 cfg.cls += ' col-' + size + '-' + settings[size];
42121 this.store = new Roo.data.Store({
42122 proxy : new Roo.data.MemoryProxy({}),
42123 reader : new Roo.data.JsonReader({
42134 'name' : 'dialCode',
42138 'name' : 'priority',
42142 'name' : 'areaCodes',
42149 if(!this.preferedCountries) {
42150 this.preferedCountries = [
42157 var p = this.preferedCountries.reverse();
42160 for (var i = 0; i < p.length; i++) {
42161 for (var j = 0; j < this.allCountries.length; j++) {
42162 if(this.allCountries[j].iso2 == p[i]) {
42163 var t = this.allCountries[j];
42164 this.allCountries.splice(j,1);
42165 this.allCountries.unshift(t);
42171 this.store.proxy.data = {
42173 data: this.allCountries
42179 initEvents : function()
42182 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42184 this.indicator = this.indicatorEl();
42185 this.flag = this.flagEl();
42186 this.dialCodeHolder = this.dialCodeHolderEl();
42188 this.trigger = this.el.select('div.flag-box',true).first();
42189 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42194 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42195 _this.list.setWidth(lw);
42198 this.list.on('mouseover', this.onViewOver, this);
42199 this.list.on('mousemove', this.onViewMove, this);
42200 this.inputEl().on("keyup", this.onKeyUp, this);
42201 this.inputEl().on("keypress", this.onKeyPress, this);
42203 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42205 this.view = new Roo.View(this.list, this.tpl, {
42206 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42209 this.view.on('click', this.onViewClick, this);
42210 this.setValue(this.defaultDialCode);
42213 onTriggerClick : function(e)
42215 Roo.log('trigger click');
42220 if(this.isExpanded()){
42222 this.hasFocus = false;
42224 this.store.load({});
42225 this.hasFocus = true;
42230 isExpanded : function()
42232 return this.list.isVisible();
42235 collapse : function()
42237 if(!this.isExpanded()){
42241 Roo.get(document).un('mousedown', this.collapseIf, this);
42242 Roo.get(document).un('mousewheel', this.collapseIf, this);
42243 this.fireEvent('collapse', this);
42247 expand : function()
42251 if(this.isExpanded() || !this.hasFocus){
42255 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42256 this.list.setWidth(lw);
42259 this.restrictHeight();
42261 Roo.get(document).on('mousedown', this.collapseIf, this);
42262 Roo.get(document).on('mousewheel', this.collapseIf, this);
42264 this.fireEvent('expand', this);
42267 restrictHeight : function()
42269 this.list.alignTo(this.inputEl(), this.listAlign);
42270 this.list.alignTo(this.inputEl(), this.listAlign);
42273 onViewOver : function(e, t)
42275 if(this.inKeyMode){
42278 var item = this.view.findItemFromChild(t);
42281 var index = this.view.indexOf(item);
42282 this.select(index, false);
42287 onViewClick : function(view, doFocus, el, e)
42289 var index = this.view.getSelectedIndexes()[0];
42291 var r = this.store.getAt(index);
42294 this.onSelect(r, index);
42296 if(doFocus !== false && !this.blockFocus){
42297 this.inputEl().focus();
42301 onViewMove : function(e, t)
42303 this.inKeyMode = false;
42306 select : function(index, scrollIntoView)
42308 this.selectedIndex = index;
42309 this.view.select(index);
42310 if(scrollIntoView !== false){
42311 var el = this.view.getNode(index);
42313 this.list.scrollChildIntoView(el, false);
42318 createList : function()
42320 this.list = Roo.get(document.body).createChild({
42322 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42323 style: 'display:none'
42326 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42329 collapseIf : function(e)
42331 var in_combo = e.within(this.el);
42332 var in_list = e.within(this.list);
42333 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42335 if (in_combo || in_list || is_list) {
42341 onSelect : function(record, index)
42343 if(this.fireEvent('beforeselect', this, record, index) !== false){
42345 this.setFlagClass(record.data.iso2);
42346 this.setDialCode(record.data.dialCode);
42347 this.hasFocus = false;
42349 this.fireEvent('select', this, record, index);
42353 flagEl : function()
42355 var flag = this.el.select('div.flag',true).first();
42362 dialCodeHolderEl : function()
42364 var d = this.el.select('input.dial-code-holder',true).first();
42371 setDialCode : function(v)
42373 this.dialCodeHolder.dom.value = '+'+v;
42376 setFlagClass : function(n)
42378 this.flag.dom.className = 'flag '+n;
42381 getValue : function()
42383 var v = this.inputEl().getValue();
42384 if(this.dialCodeHolder) {
42385 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42390 setValue : function(v)
42392 var d = this.getDialCode(v);
42394 //invalid dial code
42395 if(v.length == 0 || !d || d.length == 0) {
42397 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42398 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42404 this.setFlagClass(this.dialCodeMapping[d].iso2);
42405 this.setDialCode(d);
42406 this.inputEl().dom.value = v.replace('+'+d,'');
42407 this.hiddenEl().dom.value = this.getValue();
42412 getDialCode : function(v)
42416 if (v.length == 0) {
42417 return this.dialCodeHolder.dom.value;
42421 if (v.charAt(0) != "+") {
42424 var numericChars = "";
42425 for (var i = 1; i < v.length; i++) {
42426 var c = v.charAt(i);
42429 if (this.dialCodeMapping[numericChars]) {
42430 dialCode = v.substr(1, i);
42432 if (numericChars.length == 4) {
42442 this.setValue(this.defaultDialCode);
42446 hiddenEl : function()
42448 return this.el.select('input.hidden-tel-input',true).first();
42451 // after setting val
42452 onKeyUp : function(e){
42453 this.setValue(this.getValue());
42456 onKeyPress : function(e){
42457 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42464 * @class Roo.bootstrap.MoneyField
42465 * @extends Roo.bootstrap.ComboBox
42466 * Bootstrap MoneyField class
42469 * Create a new MoneyField.
42470 * @param {Object} config Configuration options
42473 Roo.bootstrap.MoneyField = function(config) {
42475 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42479 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42482 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42484 allowDecimals : true,
42486 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42488 decimalSeparator : ".",
42490 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42492 decimalPrecision : 0,
42494 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42496 allowNegative : true,
42498 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42502 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42504 minValue : Number.NEGATIVE_INFINITY,
42506 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42508 maxValue : Number.MAX_VALUE,
42510 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42512 minText : "The minimum value for this field is {0}",
42514 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42516 maxText : "The maximum value for this field is {0}",
42518 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
42519 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42521 nanText : "{0} is not a valid number",
42523 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42527 * @cfg {String} defaults currency of the MoneyField
42528 * value should be in lkey
42530 defaultCurrency : false,
42532 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42534 thousandsDelimiter : false,
42536 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42547 getAutoCreate : function()
42549 var align = this.labelAlign || this.parentLabelAlign();
42561 cls : 'form-control roo-money-amount-input',
42562 autocomplete: 'new-password'
42565 var hiddenInput = {
42569 cls: 'hidden-number-input'
42572 if(this.max_length) {
42573 input.maxlength = this.max_length;
42577 hiddenInput.name = this.name;
42580 if (this.disabled) {
42581 input.disabled = true;
42584 var clg = 12 - this.inputlg;
42585 var cmd = 12 - this.inputmd;
42586 var csm = 12 - this.inputsm;
42587 var cxs = 12 - this.inputxs;
42591 cls : 'row roo-money-field',
42595 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42599 cls: 'roo-select2-container input-group',
42603 cls : 'form-control roo-money-currency-input',
42604 autocomplete: 'new-password',
42606 name : this.currencyName
42610 cls : 'input-group-addon',
42624 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42628 cls: this.hasFeedback ? 'has-feedback' : '',
42639 if (this.fieldLabel.length) {
42642 tooltip: 'This field is required'
42648 cls: 'control-label',
42654 html: this.fieldLabel
42657 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42663 if(this.indicatorpos == 'right') {
42664 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42671 if(align == 'left') {
42679 if(this.labelWidth > 12){
42680 label.style = "width: " + this.labelWidth + 'px';
42682 if(this.labelWidth < 13 && this.labelmd == 0){
42683 this.labelmd = this.labelWidth;
42685 if(this.labellg > 0){
42686 label.cls += ' col-lg-' + this.labellg;
42687 input.cls += ' col-lg-' + (12 - this.labellg);
42689 if(this.labelmd > 0){
42690 label.cls += ' col-md-' + this.labelmd;
42691 container.cls += ' col-md-' + (12 - this.labelmd);
42693 if(this.labelsm > 0){
42694 label.cls += ' col-sm-' + this.labelsm;
42695 container.cls += ' col-sm-' + (12 - this.labelsm);
42697 if(this.labelxs > 0){
42698 label.cls += ' col-xs-' + this.labelxs;
42699 container.cls += ' col-xs-' + (12 - this.labelxs);
42710 var settings = this;
42712 ['xs','sm','md','lg'].map(function(size){
42713 if (settings[size]) {
42714 cfg.cls += ' col-' + size + '-' + settings[size];
42721 initEvents : function()
42723 this.indicator = this.indicatorEl();
42725 this.initCurrencyEvent();
42727 this.initNumberEvent();
42730 initCurrencyEvent : function()
42733 throw "can not find store for combo";
42736 this.store = Roo.factory(this.store, Roo.data);
42737 this.store.parent = this;
42741 this.triggerEl = this.el.select('.input-group-addon', true).first();
42743 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42748 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42749 _this.list.setWidth(lw);
42752 this.list.on('mouseover', this.onViewOver, this);
42753 this.list.on('mousemove', this.onViewMove, this);
42754 this.list.on('scroll', this.onViewScroll, this);
42757 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42760 this.view = new Roo.View(this.list, this.tpl, {
42761 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42764 this.view.on('click', this.onViewClick, this);
42766 this.store.on('beforeload', this.onBeforeLoad, this);
42767 this.store.on('load', this.onLoad, this);
42768 this.store.on('loadexception', this.onLoadException, this);
42770 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42771 "up" : function(e){
42772 this.inKeyMode = true;
42776 "down" : function(e){
42777 if(!this.isExpanded()){
42778 this.onTriggerClick();
42780 this.inKeyMode = true;
42785 "enter" : function(e){
42788 if(this.fireEvent("specialkey", this, e)){
42789 this.onViewClick(false);
42795 "esc" : function(e){
42799 "tab" : function(e){
42802 if(this.fireEvent("specialkey", this, e)){
42803 this.onViewClick(false);
42811 doRelay : function(foo, bar, hname){
42812 if(hname == 'down' || this.scope.isExpanded()){
42813 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42821 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42825 initNumberEvent : function(e)
42827 this.inputEl().on("keydown" , this.fireKey, this);
42828 this.inputEl().on("focus", this.onFocus, this);
42829 this.inputEl().on("blur", this.onBlur, this);
42831 this.inputEl().relayEvent('keyup', this);
42833 if(this.indicator){
42834 this.indicator.addClass('invisible');
42837 this.originalValue = this.getValue();
42839 if(this.validationEvent == 'keyup'){
42840 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42841 this.inputEl().on('keyup', this.filterValidation, this);
42843 else if(this.validationEvent !== false){
42844 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42847 if(this.selectOnFocus){
42848 this.on("focus", this.preFocus, this);
42851 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42852 this.inputEl().on("keypress", this.filterKeys, this);
42854 this.inputEl().relayEvent('keypress', this);
42857 var allowed = "0123456789";
42859 if(this.allowDecimals){
42860 allowed += this.decimalSeparator;
42863 if(this.allowNegative){
42867 if(this.thousandsDelimiter) {
42871 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42873 var keyPress = function(e){
42875 var k = e.getKey();
42877 var c = e.getCharCode();
42880 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42881 allowed.indexOf(String.fromCharCode(c)) === -1
42887 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42891 if(allowed.indexOf(String.fromCharCode(c)) === -1){
42896 this.inputEl().on("keypress", keyPress, this);
42900 onTriggerClick : function(e)
42907 this.loadNext = false;
42909 if(this.isExpanded()){
42914 this.hasFocus = true;
42916 if(this.triggerAction == 'all') {
42917 this.doQuery(this.allQuery, true);
42921 this.doQuery(this.getRawValue());
42924 getCurrency : function()
42926 var v = this.currencyEl().getValue();
42931 restrictHeight : function()
42933 this.list.alignTo(this.currencyEl(), this.listAlign);
42934 this.list.alignTo(this.currencyEl(), this.listAlign);
42937 onViewClick : function(view, doFocus, el, e)
42939 var index = this.view.getSelectedIndexes()[0];
42941 var r = this.store.getAt(index);
42944 this.onSelect(r, index);
42948 onSelect : function(record, index){
42950 if(this.fireEvent('beforeselect', this, record, index) !== false){
42952 this.setFromCurrencyData(index > -1 ? record.data : false);
42956 this.fireEvent('select', this, record, index);
42960 setFromCurrencyData : function(o)
42964 this.lastCurrency = o;
42966 if (this.currencyField) {
42967 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42969 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
42972 this.lastSelectionText = currency;
42974 //setting default currency
42975 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42976 this.setCurrency(this.defaultCurrency);
42980 this.setCurrency(currency);
42983 setFromData : function(o)
42987 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42989 this.setFromCurrencyData(c);
42994 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42996 Roo.log('no value set for '+ (this.name ? this.name : this.id));
42999 this.setValue(value);
43003 setCurrency : function(v)
43005 this.currencyValue = v;
43008 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43013 setValue : function(v)
43015 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43021 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43023 this.inputEl().dom.value = (v == '') ? '' :
43024 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43026 if(!this.allowZero && v === '0') {
43027 this.hiddenEl().dom.value = '';
43028 this.inputEl().dom.value = '';
43035 getRawValue : function()
43037 var v = this.inputEl().getValue();
43042 getValue : function()
43044 return this.fixPrecision(this.parseValue(this.getRawValue()));
43047 parseValue : function(value)
43049 if(this.thousandsDelimiter) {
43051 r = new RegExp(",", "g");
43052 value = value.replace(r, "");
43055 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43056 return isNaN(value) ? '' : value;
43060 fixPrecision : function(value)
43062 if(this.thousandsDelimiter) {
43064 r = new RegExp(",", "g");
43065 value = value.replace(r, "");
43068 var nan = isNaN(value);
43070 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43071 return nan ? '' : value;
43073 return parseFloat(value).toFixed(this.decimalPrecision);
43076 decimalPrecisionFcn : function(v)
43078 return Math.floor(v);
43081 validateValue : function(value)
43083 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43087 var num = this.parseValue(value);
43090 this.markInvalid(String.format(this.nanText, value));
43094 if(num < this.minValue){
43095 this.markInvalid(String.format(this.minText, this.minValue));
43099 if(num > this.maxValue){
43100 this.markInvalid(String.format(this.maxText, this.maxValue));
43107 validate : function()
43109 if(this.disabled || this.allowBlank){
43114 var currency = this.getCurrency();
43116 if(this.validateValue(this.getRawValue()) && currency.length){
43121 this.markInvalid();
43125 getName: function()
43130 beforeBlur : function()
43136 var v = this.parseValue(this.getRawValue());
43143 onBlur : function()
43147 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43148 //this.el.removeClass(this.focusClass);
43151 this.hasFocus = false;
43153 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43157 var v = this.getValue();
43159 if(String(v) !== String(this.startValue)){
43160 this.fireEvent('change', this, v, this.startValue);
43163 this.fireEvent("blur", this);
43166 inputEl : function()
43168 return this.el.select('.roo-money-amount-input', true).first();
43171 currencyEl : function()
43173 return this.el.select('.roo-money-currency-input', true).first();
43176 hiddenEl : function()
43178 return this.el.select('input.hidden-number-input',true).first();
43182 * @class Roo.bootstrap.BezierSignature
43183 * @extends Roo.bootstrap.Component
43184 * Bootstrap BezierSignature class
43185 * This script refer to:
43186 * Title: Signature Pad
43188 * Availability: https://github.com/szimek/signature_pad
43191 * Create a new BezierSignature
43192 * @param {Object} config The config object
43195 Roo.bootstrap.BezierSignature = function(config){
43196 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43202 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43209 mouse_btn_down: true,
43212 * @cfg {int} canvas height
43214 canvas_height: '200px',
43217 * @cfg {float|function} Radius of a single dot.
43222 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43227 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43232 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43237 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43242 * @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.
43244 bg_color: 'rgba(0, 0, 0, 0)',
43247 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43249 dot_color: 'black',
43252 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43254 velocity_filter_weight: 0.7,
43257 * @cfg {function} Callback when stroke begin.
43262 * @cfg {function} Callback when stroke end.
43266 getAutoCreate : function()
43268 var cls = 'roo-signature column';
43271 cls += ' ' + this.cls;
43281 for(var i = 0; i < col_sizes.length; i++) {
43282 if(this[col_sizes[i]]) {
43283 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43293 cls: 'roo-signature-body',
43297 cls: 'roo-signature-body-canvas',
43298 height: this.canvas_height,
43299 width: this.canvas_width
43306 style: 'display: none'
43314 initEvents: function()
43316 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43318 var canvas = this.canvasEl();
43320 // mouse && touch event swapping...
43321 canvas.dom.style.touchAction = 'none';
43322 canvas.dom.style.msTouchAction = 'none';
43324 this.mouse_btn_down = false;
43325 canvas.on('mousedown', this._handleMouseDown, this);
43326 canvas.on('mousemove', this._handleMouseMove, this);
43327 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43329 if (window.PointerEvent) {
43330 canvas.on('pointerdown', this._handleMouseDown, this);
43331 canvas.on('pointermove', this._handleMouseMove, this);
43332 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43335 if ('ontouchstart' in window) {
43336 canvas.on('touchstart', this._handleTouchStart, this);
43337 canvas.on('touchmove', this._handleTouchMove, this);
43338 canvas.on('touchend', this._handleTouchEnd, this);
43341 Roo.EventManager.onWindowResize(this.resize, this, true);
43343 // file input event
43344 this.fileEl().on('change', this.uploadImage, this);
43351 resize: function(){
43353 var canvas = this.canvasEl().dom;
43354 var ctx = this.canvasElCtx();
43355 var img_data = false;
43357 if(canvas.width > 0) {
43358 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43360 // setting canvas width will clean img data
43363 var style = window.getComputedStyle ?
43364 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43366 var padding_left = parseInt(style.paddingLeft) || 0;
43367 var padding_right = parseInt(style.paddingRight) || 0;
43369 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43372 ctx.putImageData(img_data, 0, 0);
43376 _handleMouseDown: function(e)
43378 if (e.browserEvent.which === 1) {
43379 this.mouse_btn_down = true;
43380 this.strokeBegin(e);
43384 _handleMouseMove: function (e)
43386 if (this.mouse_btn_down) {
43387 this.strokeMoveUpdate(e);
43391 _handleMouseUp: function (e)
43393 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43394 this.mouse_btn_down = false;
43399 _handleTouchStart: function (e) {
43401 e.preventDefault();
43402 if (e.browserEvent.targetTouches.length === 1) {
43403 // var touch = e.browserEvent.changedTouches[0];
43404 // this.strokeBegin(touch);
43406 this.strokeBegin(e); // assume e catching the correct xy...
43410 _handleTouchMove: function (e) {
43411 e.preventDefault();
43412 // var touch = event.targetTouches[0];
43413 // _this._strokeMoveUpdate(touch);
43414 this.strokeMoveUpdate(e);
43417 _handleTouchEnd: function (e) {
43418 var wasCanvasTouched = e.target === this.canvasEl().dom;
43419 if (wasCanvasTouched) {
43420 e.preventDefault();
43421 // var touch = event.changedTouches[0];
43422 // _this._strokeEnd(touch);
43427 reset: function () {
43428 this._lastPoints = [];
43429 this._lastVelocity = 0;
43430 this._lastWidth = (this.min_width + this.max_width) / 2;
43431 this.canvasElCtx().fillStyle = this.dot_color;
43434 strokeMoveUpdate: function(e)
43436 this.strokeUpdate(e);
43438 if (this.throttle) {
43439 this.throttleStroke(this.strokeUpdate, this.throttle);
43442 this.strokeUpdate(e);
43446 strokeBegin: function(e)
43448 var newPointGroup = {
43449 color: this.dot_color,
43453 if (typeof this.onBegin === 'function') {
43457 this.curve_data.push(newPointGroup);
43459 this.strokeUpdate(e);
43462 strokeUpdate: function(e)
43464 var rect = this.canvasEl().dom.getBoundingClientRect();
43465 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43466 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43467 var lastPoints = lastPointGroup.points;
43468 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43469 var isLastPointTooClose = lastPoint
43470 ? point.distanceTo(lastPoint) <= this.min_distance
43472 var color = lastPointGroup.color;
43473 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43474 var curve = this.addPoint(point);
43476 this.drawDot({color: color, point: point});
43479 this.drawCurve({color: color, curve: curve});
43489 strokeEnd: function(e)
43491 this.strokeUpdate(e);
43492 if (typeof this.onEnd === 'function') {
43497 addPoint: function (point) {
43498 var _lastPoints = this._lastPoints;
43499 _lastPoints.push(point);
43500 if (_lastPoints.length > 2) {
43501 if (_lastPoints.length === 3) {
43502 _lastPoints.unshift(_lastPoints[0]);
43504 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43505 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43506 _lastPoints.shift();
43512 calculateCurveWidths: function (startPoint, endPoint) {
43513 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43514 (1 - this.velocity_filter_weight) * this._lastVelocity;
43516 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43519 start: this._lastWidth
43522 this._lastVelocity = velocity;
43523 this._lastWidth = newWidth;
43527 drawDot: function (_a) {
43528 var color = _a.color, point = _a.point;
43529 var ctx = this.canvasElCtx();
43530 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43532 this.drawCurveSegment(point.x, point.y, width);
43534 ctx.fillStyle = color;
43538 drawCurve: function (_a) {
43539 var color = _a.color, curve = _a.curve;
43540 var ctx = this.canvasElCtx();
43541 var widthDelta = curve.endWidth - curve.startWidth;
43542 var drawSteps = Math.floor(curve.length()) * 2;
43544 ctx.fillStyle = color;
43545 for (var i = 0; i < drawSteps; i += 1) {
43546 var t = i / drawSteps;
43552 var x = uuu * curve.startPoint.x;
43553 x += 3 * uu * t * curve.control1.x;
43554 x += 3 * u * tt * curve.control2.x;
43555 x += ttt * curve.endPoint.x;
43556 var y = uuu * curve.startPoint.y;
43557 y += 3 * uu * t * curve.control1.y;
43558 y += 3 * u * tt * curve.control2.y;
43559 y += ttt * curve.endPoint.y;
43560 var width = curve.startWidth + ttt * widthDelta;
43561 this.drawCurveSegment(x, y, width);
43567 drawCurveSegment: function (x, y, width) {
43568 var ctx = this.canvasElCtx();
43570 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43571 this.is_empty = false;
43576 var ctx = this.canvasElCtx();
43577 var canvas = this.canvasEl().dom;
43578 ctx.fillStyle = this.bg_color;
43579 ctx.clearRect(0, 0, canvas.width, canvas.height);
43580 ctx.fillRect(0, 0, canvas.width, canvas.height);
43581 this.curve_data = [];
43583 this.is_empty = true;
43588 return this.el.select('input',true).first();
43591 canvasEl: function()
43593 return this.el.select('canvas',true).first();
43596 canvasElCtx: function()
43598 return this.el.select('canvas',true).first().dom.getContext('2d');
43601 getImage: function(type)
43603 if(this.is_empty) {
43608 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43611 drawFromImage: function(img_src)
43613 var img = new Image();
43615 img.onload = function(){
43616 this.canvasElCtx().drawImage(img, 0, 0);
43621 this.is_empty = false;
43624 selectImage: function()
43626 this.fileEl().dom.click();
43629 uploadImage: function(e)
43631 var reader = new FileReader();
43633 reader.onload = function(e){
43634 var img = new Image();
43635 img.onload = function(){
43637 this.canvasElCtx().drawImage(img, 0, 0);
43639 img.src = e.target.result;
43642 reader.readAsDataURL(e.target.files[0]);
43645 // Bezier Point Constructor
43646 Point: (function () {
43647 function Point(x, y, time) {
43650 this.time = time || Date.now();
43652 Point.prototype.distanceTo = function (start) {
43653 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43655 Point.prototype.equals = function (other) {
43656 return this.x === other.x && this.y === other.y && this.time === other.time;
43658 Point.prototype.velocityFrom = function (start) {
43659 return this.time !== start.time
43660 ? this.distanceTo(start) / (this.time - start.time)
43667 // Bezier Constructor
43668 Bezier: (function () {
43669 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43670 this.startPoint = startPoint;
43671 this.control2 = control2;
43672 this.control1 = control1;
43673 this.endPoint = endPoint;
43674 this.startWidth = startWidth;
43675 this.endWidth = endWidth;
43677 Bezier.fromPoints = function (points, widths, scope) {
43678 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43679 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43680 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43682 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43683 var dx1 = s1.x - s2.x;
43684 var dy1 = s1.y - s2.y;
43685 var dx2 = s2.x - s3.x;
43686 var dy2 = s2.y - s3.y;
43687 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43688 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43689 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43690 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43691 var dxm = m1.x - m2.x;
43692 var dym = m1.y - m2.y;
43693 var k = l2 / (l1 + l2);
43694 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43695 var tx = s2.x - cm.x;
43696 var ty = s2.y - cm.y;
43698 c1: new scope.Point(m1.x + tx, m1.y + ty),
43699 c2: new scope.Point(m2.x + tx, m2.y + ty)
43702 Bezier.prototype.length = function () {
43707 for (var i = 0; i <= steps; i += 1) {
43709 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43710 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43712 var xdiff = cx - px;
43713 var ydiff = cy - py;
43714 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43721 Bezier.prototype.point = function (t, start, c1, c2, end) {
43722 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43723 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43724 + (3.0 * c2 * (1.0 - t) * t * t)
43725 + (end * t * t * t);
43730 throttleStroke: function(fn, wait) {
43731 if (wait === void 0) { wait = 250; }
43733 var timeout = null;
43737 var later = function () {
43738 previous = Date.now();
43740 result = fn.apply(storedContext, storedArgs);
43742 storedContext = null;
43746 return function wrapper() {
43748 for (var _i = 0; _i < arguments.length; _i++) {
43749 args[_i] = arguments[_i];
43751 var now = Date.now();
43752 var remaining = wait - (now - previous);
43753 storedContext = this;
43755 if (remaining <= 0 || remaining > wait) {
43757 clearTimeout(timeout);
43761 result = fn.apply(storedContext, storedArgs);
43763 storedContext = null;
43767 else if (!timeout) {
43768 timeout = window.setTimeout(later, remaining);