2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<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>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
366 * Fetch the element to display the tooltip on.
367 * @return {Roo.Element} defaults to this.el
369 tooltipEl : function()
374 addxtype : function(tree,cntr)
378 cn = Roo.factory(tree);
379 //Roo.log(['addxtype', cn]);
381 cn.parentType = this.xtype; //??
382 cn.parentId = this.id;
384 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385 if (typeof(cn.container_method) == 'string') {
386 cntr = cn.container_method;
390 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
392 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
394 var build_from_html = Roo.XComponent.build_from_html;
396 var is_body = (tree.xtype == 'Body') ;
398 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
400 var self_cntr_el = Roo.get(this[cntr](false));
402 // do not try and build conditional elements
403 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
407 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409 return this.addxtypeChild(tree,cntr, is_body);
412 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
415 return this.addxtypeChild(Roo.apply({}, tree),cntr);
418 Roo.log('skipping render');
424 if (!build_from_html) {
428 // this i think handles overlaying multiple children of the same type
429 // with the sam eelement.. - which might be buggy..
431 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
441 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448 addxtypeChild : function (tree, cntr, is_body)
450 Roo.debug && Roo.log('addxtypeChild:' + cntr);
452 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
455 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456 (typeof(tree['flexy:foreach']) != 'undefined');
460 skip_children = false;
461 // render the element if it's not BODY.
464 // if parent was disabled, then do not try and create the children..
465 if(!this[cntr](true)){
470 cn = Roo.factory(tree);
472 cn.parentType = this.xtype; //??
473 cn.parentId = this.id;
475 var build_from_html = Roo.XComponent.build_from_html;
478 // does the container contain child eleemnts with 'xtype' attributes.
479 // that match this xtype..
480 // note - when we render we create these as well..
481 // so we should check to see if body has xtype set.
482 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
484 var self_cntr_el = Roo.get(this[cntr](false));
485 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
487 //Roo.log(Roo.XComponent.build_from_html);
488 //Roo.log("got echild:");
491 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492 // and are not displayed -this causes this to use up the wrong element when matching.
493 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
496 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503 //echild.dom.removeAttribute('xtype');
505 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506 Roo.debug && Roo.log(self_cntr_el);
507 Roo.debug && Roo.log(echild);
508 Roo.debug && Roo.log(cn);
514 // if object has flexy:if - then it may or may not be rendered.
515 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
516 // skip a flexy if element.
517 Roo.debug && Roo.log('skipping render');
518 Roo.debug && Roo.log(tree);
520 Roo.debug && Roo.log('skipping all children');
521 skip_children = true;
526 // actually if flexy:foreach is found, we really want to create
527 // multiple copies here...
529 //Roo.log(this[cntr]());
530 // some elements do not have render methods.. like the layouts...
532 if(this[cntr](true) === false){
537 cn.render && cn.render(this[cntr](true));
540 // then add the element..
547 if (typeof (tree.menu) != 'undefined') {
548 tree.menu.parentType = cn.xtype;
549 tree.menu.triggerEl = cn.el;
550 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
554 if (!tree.items || !tree.items.length) {
556 //Roo.log(["no children", this]);
561 var items = tree.items;
564 //Roo.log(items.length);
566 if (!skip_children) {
567 for(var i =0;i < items.length;i++) {
568 // Roo.log(['add child', items[i]]);
569 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575 //Roo.log("fire childrenrendered");
577 cn.fireEvent('childrenrendered', this);
583 * Set the element that will be used to show or hide
585 setVisibilityEl : function(el)
587 this.visibilityEl = el;
591 * Get the element that will be used to show or hide
593 getVisibilityEl : function()
595 if (typeof(this.visibilityEl) == 'object') {
596 return this.visibilityEl;
599 if (typeof(this.visibilityEl) == 'string') {
600 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607 * Show a component - removes 'hidden' class
611 if(!this.getVisibilityEl()){
615 this.getVisibilityEl().removeClass(['hidden','d-none']);
617 this.fireEvent('show', this);
622 * Hide a component - adds 'hidden' class
626 if(!this.getVisibilityEl()){
630 this.getVisibilityEl().addClass(['hidden','d-none']);
632 this.fireEvent('hide', this);
645 * @class Roo.bootstrap.Element
646 * @extends Roo.bootstrap.Component
647 * Bootstrap Element class
648 * @cfg {String} html contents of the element
649 * @cfg {String} tag tag of the element
650 * @cfg {String} cls class of the element
651 * @cfg {Boolean} preventDefault (true|false) default false
652 * @cfg {Boolean} clickable (true|false) default false
655 * Create a new Element
656 * @param {Object} config The config object
659 Roo.bootstrap.Element = function(config){
660 Roo.bootstrap.Element.superclass.constructor.call(this, config);
666 * When a element is chick
667 * @param {Roo.bootstrap.Element} this
668 * @param {Roo.EventObject} e
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
679 preventDefault: false,
682 getAutoCreate : function(){
686 // cls: this.cls, double assign in parent class Component.js :: onRender
693 initEvents: function()
695 Roo.bootstrap.Element.superclass.initEvents.call(this);
698 this.el.on('click', this.onClick, this);
703 onClick : function(e)
705 if(this.preventDefault){
709 this.fireEvent('click', this, e);
712 getValue : function()
714 return this.el.dom.innerHTML;
717 setValue : function(value)
719 this.el.dom.innerHTML = value;
734 * @class Roo.bootstrap.DropTarget
735 * @extends Roo.bootstrap.Element
736 * Bootstrap DropTarget class
738 * @cfg {string} name dropable name
741 * Create a new Dropable Area
742 * @param {Object} config The config object
745 Roo.bootstrap.DropTarget = function(config){
746 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
752 * When a element is chick
753 * @param {Roo.bootstrap.Element} this
754 * @param {Roo.EventObject} e
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
763 getAutoCreate : function(){
768 initEvents: function()
770 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
774 drop : this.dragDrop.createDelegate(this),
775 enter : this.dragEnter.createDelegate(this),
776 out : this.dragOut.createDelegate(this),
777 over : this.dragOver.createDelegate(this)
781 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
784 dragDrop : function(source,e,data)
786 // user has to decide how to impliment this.
789 //this.fireEvent('drop', this, source, e ,data);
793 dragEnter : function(n, dd, e, data)
795 // probably want to resize the element to match the dropped element..
797 this.originalSize = this.el.getSize();
798 this.el.setSize( n.el.getSize());
799 this.dropZone.DDM.refreshCache(this.name);
800 Roo.log([n, dd, e, data]);
803 dragOut : function(value)
805 // resize back to normal
807 this.el.setSize(this.originalSize);
808 this.dropZone.resetConstraints();
811 dragOver : function()
828 * @class Roo.bootstrap.Body
829 * @extends Roo.bootstrap.Component
830 * Bootstrap Body class
834 * @param {Object} config The config object
837 Roo.bootstrap.Body = function(config){
839 config = config || {};
841 Roo.bootstrap.Body.superclass.constructor.call(this, config);
842 this.el = Roo.get(config.el ? config.el : document.body );
843 if (this.cls && this.cls.length) {
844 Roo.get(document.body).addClass(this.cls);
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
850 is_body : true,// just to make sure it's constructed?
855 onRender : function(ct, position)
857 /* Roo.log("Roo.bootstrap.Body - onRender");
858 if (this.cls && this.cls.length) {
859 Roo.get(document.body).addClass(this.cls);
878 * @class Roo.bootstrap.ButtonGroup
879 * @extends Roo.bootstrap.Component
880 * Bootstrap ButtonGroup class
881 * @cfg {String} size lg | sm | xs (default empty normal)
882 * @cfg {String} align vertical | justified (default none)
883 * @cfg {String} direction up | down (default down)
884 * @cfg {Boolean} toolbar false | true
885 * @cfg {Boolean} btn true | false
890 * @param {Object} config The config object
893 Roo.bootstrap.ButtonGroup = function(config){
894 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
905 getAutoCreate : function(){
911 cfg.html = this.html || cfg.html;
922 if (['vertical','justified'].indexOf(this.align)!==-1) {
923 cfg.cls = 'btn-group-' + this.align;
925 if (this.align == 'justified') {
926 console.log(this.items);
930 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931 cfg.cls += ' btn-group-' + this.size;
934 if (this.direction == 'up') {
935 cfg.cls += ' dropup' ;
941 * Add a button to the group (similar to NavItem API.)
943 addItem : function(cfg)
945 var cn = new Roo.bootstrap.Button(cfg);
947 cn.parentId = this.id;
948 cn.onRender(this.el, null);
962 * @class Roo.bootstrap.Button
963 * @extends Roo.bootstrap.Component
964 * Bootstrap Button class
965 * @cfg {String} html The button content
966 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969 * @cfg {String} size (lg|sm|xs)
970 * @cfg {String} tag (a|input|submit)
971 * @cfg {String} href empty or href
972 * @cfg {Boolean} disabled default false;
973 * @cfg {Boolean} isClose default false;
974 * @cfg {String} glyphicon depricated - use fa
975 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976 * @cfg {String} badge text for badge
977 * @cfg {String} theme (default|glow)
978 * @cfg {Boolean} inverse dark themed version
979 * @cfg {Boolean} toggle is it a slidy toggle button
980 * @cfg {Boolean} pressed default null - if the button ahs active state
981 * @cfg {String} ontext text for on slidy toggle state
982 * @cfg {String} offtext text for off slidy toggle state
983 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
984 * @cfg {Boolean} removeClass remove the standard class..
985 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
988 * Create a new button
989 * @param {Object} config The config object
993 Roo.bootstrap.Button = function(config){
994 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1000 * When a butotn is pressed
1001 * @param {Roo.bootstrap.Button} btn
1002 * @param {Roo.EventObject} e
1007 * After the button has been toggles
1008 * @param {Roo.bootstrap.Button} btn
1009 * @param {Roo.EventObject} e
1010 * @param {boolean} pressed (also available as button.pressed)
1016 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1037 preventDefault: true,
1045 getAutoCreate : function(){
1053 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1054 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1055 this.tag = 'button';
1059 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1061 if (this.toggle == true) {
1064 cls: 'slider-frame roo-button',
1068 'data-on-text':'ON',
1069 'data-off-text':'OFF',
1070 cls: 'slider-button',
1075 // why are we validating the weights?
1076 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1077 cfg.cls += ' ' + this.weight;
1084 cfg.cls += ' close';
1086 cfg["aria-hidden"] = true;
1088 cfg.html = "×";
1094 if (this.theme==='default') {
1095 cfg.cls = 'btn roo-button';
1097 //if (this.parentType != 'Navbar') {
1098 this.weight = this.weight.length ? this.weight : 'default';
1100 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1102 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1103 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1104 cfg.cls += ' btn-' + outline + weight;
1105 if (this.weight == 'default') {
1107 cfg.cls += ' btn-' + this.weight;
1110 } else if (this.theme==='glow') {
1113 cfg.cls = 'btn-glow roo-button';
1115 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1117 cfg.cls += ' ' + this.weight;
1123 this.cls += ' inverse';
1127 if (this.active || this.pressed === true) {
1128 cfg.cls += ' active';
1131 if (this.disabled) {
1132 cfg.disabled = 'disabled';
1136 Roo.log('changing to ul' );
1138 this.glyphicon = 'caret';
1139 if (Roo.bootstrap.version == 4) {
1140 this.fa = 'caret-down';
1145 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1147 //gsRoo.log(this.parentType);
1148 if (this.parentType === 'Navbar' && !this.parent().bar) {
1149 Roo.log('changing to li?');
1158 href : this.href || '#'
1161 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1162 cfg.cls += ' dropdown';
1169 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1171 if (this.glyphicon) {
1172 cfg.html = ' ' + cfg.html;
1177 cls: 'glyphicon glyphicon-' + this.glyphicon
1182 cfg.html = ' ' + cfg.html;
1187 cls: 'fa fas fa-' + this.fa
1197 // cfg.cls='btn roo-button';
1201 var value = cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon,
1213 cls: 'fa fas fa-' + this.fa,
1218 var bw = this.badge_weight.length ? this.badge_weight :
1219 (this.weight.length ? this.weight : 'secondary');
1220 bw = bw == 'default' ? 'secondary' : bw;
1226 cls: 'badge badge-' + bw,
1235 cfg.cls += ' dropdown';
1236 cfg.html = typeof(cfg.html) != 'undefined' ?
1237 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1240 if (cfg.tag !== 'a' && this.href !== '') {
1241 throw "Tag must be a to set href.";
1242 } else if (this.href.length > 0) {
1243 cfg.href = this.href;
1246 if(this.removeClass){
1251 cfg.target = this.target;
1256 initEvents: function() {
1257 // Roo.log('init events?');
1258 // Roo.log(this.el.dom);
1261 if (typeof (this.menu) != 'undefined') {
1262 this.menu.parentType = this.xtype;
1263 this.menu.triggerEl = this.el;
1264 this.addxtype(Roo.apply({}, this.menu));
1268 if (this.el.hasClass('roo-button')) {
1269 this.el.on('click', this.onClick, this);
1271 this.el.select('.roo-button').on('click', this.onClick, this);
1274 if(this.removeClass){
1275 this.el.on('click', this.onClick, this);
1278 this.el.enableDisplayMode();
1281 onClick : function(e)
1283 if (this.disabled) {
1287 Roo.log('button on click ');
1288 if(this.preventDefault){
1292 if (this.pressed === true || this.pressed === false) {
1293 this.toggleActive(e);
1297 this.fireEvent('click', this, e);
1301 * Enables this button
1305 this.disabled = false;
1306 this.el.removeClass('disabled');
1310 * Disable this button
1312 disable : function()
1314 this.disabled = true;
1315 this.el.addClass('disabled');
1318 * sets the active state on/off,
1319 * @param {Boolean} state (optional) Force a particular state
1321 setActive : function(v) {
1323 this.el[v ? 'addClass' : 'removeClass']('active');
1327 * toggles the current active state
1329 toggleActive : function(e)
1331 this.setActive(!this.pressed); // this modifies pressed...
1332 this.fireEvent('toggle', this, e, this.pressed);
1335 * get the current active state
1336 * @return {boolean} true if it's active
1338 isActive : function()
1340 return this.el.hasClass('active');
1343 * set the text of the first selected button
1345 setText : function(str)
1347 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1350 * get the text of the first selected button
1352 getText : function()
1354 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1357 setWeight : function(str)
1359 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1360 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1362 var outline = this.outline ? 'outline-' : '';
1363 if (str == 'default') {
1364 this.el.addClass('btn-default btn-outline-secondary');
1367 this.el.addClass('btn-' + outline + str);
1372 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1374 Roo.bootstrap.Button.weights = [
1394 * @class Roo.bootstrap.Column
1395 * @extends Roo.bootstrap.Component
1396 * Bootstrap Column class
1397 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1398 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1399 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1400 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1401 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1402 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1403 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1404 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1407 * @cfg {Boolean} hidden (true|false) hide the element
1408 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1409 * @cfg {String} fa (ban|check|...) font awesome icon
1410 * @cfg {Number} fasize (1|2|....) font awsome size
1412 * @cfg {String} icon (info-sign|check|...) glyphicon name
1414 * @cfg {String} html content of column.
1417 * Create a new Column
1418 * @param {Object} config The config object
1421 Roo.bootstrap.Column = function(config){
1422 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1425 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1443 getAutoCreate : function(){
1444 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1452 var sizes = ['xs','sm','md','lg'];
1453 sizes.map(function(size ,ix){
1454 //Roo.log( size + ':' + settings[size]);
1456 if (settings[size+'off'] !== false) {
1457 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1460 if (settings[size] === false) {
1464 if (!settings[size]) { // 0 = hidden
1465 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1467 for (var i = ix; i > -1; i--) {
1468 cfg.cls += ' d-' + sizes[i] + '-none';
1474 cfg.cls += ' col-' + size + '-' + settings[size] + (
1475 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1481 cfg.cls += ' hidden';
1484 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1485 cfg.cls +=' alert alert-' + this.alert;
1489 if (this.html.length) {
1490 cfg.html = this.html;
1494 if (this.fasize > 1) {
1495 fasize = ' fa-' + this.fasize + 'x';
1497 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1502 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1521 * @class Roo.bootstrap.Container
1522 * @extends Roo.bootstrap.Component
1523 * Bootstrap Container class
1524 * @cfg {Boolean} jumbotron is it a jumbotron element
1525 * @cfg {String} html content of element
1526 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1527 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1528 * @cfg {String} header content of header (for panel)
1529 * @cfg {String} footer content of footer (for panel)
1530 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1531 * @cfg {String} tag (header|aside|section) type of HTML tag.
1532 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1533 * @cfg {String} fa font awesome icon
1534 * @cfg {String} icon (info-sign|check|...) glyphicon name
1535 * @cfg {Boolean} hidden (true|false) hide the element
1536 * @cfg {Boolean} expandable (true|false) default false
1537 * @cfg {Boolean} expanded (true|false) default true
1538 * @cfg {String} rheader contet on the right of header
1539 * @cfg {Boolean} clickable (true|false) default false
1543 * Create a new Container
1544 * @param {Object} config The config object
1547 Roo.bootstrap.Container = function(config){
1548 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1554 * After the panel has been expand
1556 * @param {Roo.bootstrap.Container} this
1561 * After the panel has been collapsed
1563 * @param {Roo.bootstrap.Container} this
1568 * When a element is chick
1569 * @param {Roo.bootstrap.Container} this
1570 * @param {Roo.EventObject} e
1576 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1594 getChildContainer : function() {
1600 if (this.panel.length) {
1601 return this.el.select('.panel-body',true).first();
1608 getAutoCreate : function(){
1611 tag : this.tag || 'div',
1615 if (this.jumbotron) {
1616 cfg.cls = 'jumbotron';
1621 // - this is applied by the parent..
1623 // cfg.cls = this.cls + '';
1626 if (this.sticky.length) {
1628 var bd = Roo.get(document.body);
1629 if (!bd.hasClass('bootstrap-sticky')) {
1630 bd.addClass('bootstrap-sticky');
1631 Roo.select('html',true).setStyle('height', '100%');
1634 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1638 if (this.well.length) {
1639 switch (this.well) {
1642 cfg.cls +=' well well-' +this.well;
1651 cfg.cls += ' hidden';
1655 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1656 cfg.cls +=' alert alert-' + this.alert;
1661 if (this.panel.length) {
1662 cfg.cls += ' panel panel-' + this.panel;
1664 if (this.header.length) {
1668 if(this.expandable){
1670 cfg.cls = cfg.cls + ' expandable';
1674 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1682 cls : 'panel-title',
1683 html : (this.expandable ? ' ' : '') + this.header
1687 cls: 'panel-header-right',
1693 cls : 'panel-heading',
1694 style : this.expandable ? 'cursor: pointer' : '',
1702 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1707 if (this.footer.length) {
1709 cls : 'panel-footer',
1718 body.html = this.html || cfg.html;
1719 // prefix with the icons..
1721 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1724 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1729 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1730 cfg.cls = 'container';
1736 initEvents: function()
1738 if(this.expandable){
1739 var headerEl = this.headerEl();
1742 headerEl.on('click', this.onToggleClick, this);
1747 this.el.on('click', this.onClick, this);
1752 onToggleClick : function()
1754 var headerEl = this.headerEl();
1770 if(this.fireEvent('expand', this)) {
1772 this.expanded = true;
1774 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1776 this.el.select('.panel-body',true).first().removeClass('hide');
1778 var toggleEl = this.toggleEl();
1784 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1789 collapse : function()
1791 if(this.fireEvent('collapse', this)) {
1793 this.expanded = false;
1795 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1796 this.el.select('.panel-body',true).first().addClass('hide');
1798 var toggleEl = this.toggleEl();
1804 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1808 toggleEl : function()
1810 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1814 return this.el.select('.panel-heading .fa',true).first();
1817 headerEl : function()
1819 if(!this.el || !this.panel.length || !this.header.length){
1823 return this.el.select('.panel-heading',true).first()
1828 if(!this.el || !this.panel.length){
1832 return this.el.select('.panel-body',true).first()
1835 titleEl : function()
1837 if(!this.el || !this.panel.length || !this.header.length){
1841 return this.el.select('.panel-title',true).first();
1844 setTitle : function(v)
1846 var titleEl = this.titleEl();
1852 titleEl.dom.innerHTML = v;
1855 getTitle : function()
1858 var titleEl = this.titleEl();
1864 return titleEl.dom.innerHTML;
1867 setRightTitle : function(v)
1869 var t = this.el.select('.panel-header-right',true).first();
1875 t.dom.innerHTML = v;
1878 onClick : function(e)
1882 this.fireEvent('click', this, e);
1889 * This is BS4's Card element.. - similar to our containers probably..
1893 * @class Roo.bootstrap.Card
1894 * @extends Roo.bootstrap.Component
1895 * Bootstrap Card class
1898 * possible... may not be implemented..
1899 * @cfg {String} header_image src url of image.
1900 * @cfg {String|Object} header
1901 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1903 * @cfg {String} title
1904 * @cfg {String} subtitle
1905 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1906 * @cfg {String} footer
1908 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1910 * @cfg {String} margin (0|1|2|3|4|5|auto)
1911 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1912 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1913 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1914 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1915 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1916 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1918 * @cfg {String} padding (0|1|2|3|4|5)
1919 * @cfg {String} padding_top (0|1|2|3|4|5)
1920 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1921 * @cfg {String} padding_left (0|1|2|3|4|5)
1922 * @cfg {String} padding_right (0|1|2|3|4|5)
1923 * @cfg {String} padding_x (0|1|2|3|4|5)
1924 * @cfg {String} padding_y (0|1|2|3|4|5)
1926 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1927 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1928 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1929 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1930 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1932 * @config {Boolean} dragable if this card can be dragged.
1933 * @config {String} drag_group group for drag
1934 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1935 * @config {String} drop_group group for drag
1937 * @config {Boolean} collapsable can the body be collapsed.
1938 * @config {Boolean} collapsed is the body collapsed when rendered...
1939 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1940 * @config {Boolean} rotated is the body rotated when rendered...
1943 * Create a new Container
1944 * @param {Object} config The config object
1947 Roo.bootstrap.Card = function(config){
1948 Roo.bootstrap.Card.superclass.constructor.call(this, config);
1954 * When a element a card is dropped
1955 * @param {Roo.bootstrap.Card} this
1958 * @param {Roo.bootstrap.Card} move_card the card being dropped?
1959 * @param {String} position 'above' or 'below'
1960 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
1966 * When a element a card is rotate
1967 * @param {Roo.bootstrap.Element} this
1968 * @param {Roo.Element} n the node being dropped?
1969 * @param {Boolean} rotate status
1977 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
1982 margin: '', /// may be better in component?
2012 collapsable : false,
2021 childContainer : false,
2022 dropEl : false, /// the dom placeholde element that indicates drop location.
2024 layoutCls : function()
2028 Roo.log(this.margin_bottom.length);
2029 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2030 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2032 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2033 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2035 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2036 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2040 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2041 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2042 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2046 // more generic support?
2054 // Roo.log("Call onRender: " + this.xtype);
2055 /* We are looking at something like this.
2057 <img src="..." class="card-img-top" alt="...">
2058 <div class="card-body">
2059 <h5 class="card-title">Card title</h5>
2060 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2062 >> this bit is really the body...
2063 <div> << we will ad dthis in hopefully it will not break shit.
2065 ** card text does not actually have any styling...
2067 <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>
2070 <a href="#" class="card-link">Card link</a>
2073 <div class="card-footer">
2074 <small class="text-muted">Last updated 3 mins ago</small>
2078 getAutoCreate : function(){
2086 if (this.weight.length && this.weight != 'light') {
2087 cfg.cls += ' text-white';
2089 cfg.cls += ' text-dark'; // need as it's nested..
2091 if (this.weight.length) {
2092 cfg.cls += ' bg-' + this.weight;
2095 cfg.cls += this.layoutCls();
2098 var hdr_ctr = false;
2099 if (this.header.length) {
2101 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2102 cls : 'card-header',
2110 cls : 'card-header d-none',
2116 if (this.collapsable) {
2119 cls : 'd-block user-select-none',
2123 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2128 hdr.cn.push(hdr_ctr);
2133 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2138 if (this.header_image.length) {
2141 cls : 'card-img-top',
2142 src: this.header_image // escape?
2147 cls : 'card-img-top d-none'
2153 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2157 if (this.collapsable || this.rotateable) {
2160 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2167 if (this.title.length) {
2171 src: this.title // escape?
2175 if (this.subtitle.length) {
2179 src: this.subtitle // escape?
2185 cls : 'roo-card-body-ctr'
2188 if (this.html.length) {
2194 // fixme ? handle objects?
2196 if (this.footer.length) {
2199 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2204 cfg.cn.push({cls : 'card-footer d-none'});
2213 getCardHeader : function()
2215 var ret = this.el.select('.card-header',true).first();
2216 if (ret.hasClass('d-none')) {
2217 ret.removeClass('d-none');
2222 getCardFooter : function()
2224 var ret = this.el.select('.card-footer',true).first();
2225 if (ret.hasClass('d-none')) {
2226 ret.removeClass('d-none');
2231 getCardImageTop : function()
2233 var ret = this.el.select('.card-img-top',true).first();
2234 if (ret.hasClass('d-none')) {
2235 ret.removeClass('d-none');
2241 getChildContainer : function()
2247 return this.el.select('.roo-card-body-ctr',true).first();
2250 initEvents: function()
2253 this.bodyEl = this.getChildContainer();
2255 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2256 containerScroll: true,
2257 ddGroup: this.drag_group || 'default_card_drag_group'
2259 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2261 if (this.dropable) {
2262 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2263 containerScroll: true,
2264 ddGroup: this.drop_group || 'default_card_drag_group'
2266 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2267 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2268 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2269 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2270 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2273 if (this.collapsable) {
2274 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2276 if (this.rotateable) {
2277 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2279 this.collapsableEl = this.el.select('.roo-collapsable').first();
2281 this.footerEl = this.el.select('.card-footer').first();
2282 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2283 this.headerEl = this.el.select('.roo-card-header-ctr').first();
2286 this.el.addClass('roo-card-rotated');
2287 this.fireEvent('rotate', this, true);
2291 getDragData : function(e)
2293 var target = this.getEl();
2295 //this.handleSelection(e);
2300 nodes: this.getEl(),
2305 dragData.ddel = target.dom ; // the div element
2306 Roo.log(target.getWidth( ));
2307 dragData.ddel.style.width = target.getWidth() + 'px';
2314 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2315 * whole Element becomes the target, and this causes the drop gesture to append.
2317 getTargetFromEvent : function(e, dragged_card_el)
2319 var target = e.getTarget();
2320 while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2321 target = target.parentNode;
2332 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2333 // see if target is one of the 'cards'...
2336 //Roo.log(this.items.length);
2339 var last_card_n = 0;
2341 for (var i = 0;i< this.items.length;i++) {
2343 if (!this.items[i].el.hasClass('card')) {
2346 pos = this.getDropPoint(e, this.items[i].el.dom);
2348 cards_len = ret.cards.length;
2349 //Roo.log(this.items[i].el.dom.id);
2350 ret.cards.push(this.items[i]);
2352 if (ret.card_n < 0 && pos == 'above') {
2353 ret.position = cards_len > 0 ? 'below' : pos;
2354 ret.items_n = i > 0 ? i - 1 : 0;
2355 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2356 ret.card = ret.cards[ret.card_n];
2359 if (!ret.cards.length) {
2361 ret.position = 'below';
2365 // could not find a card.. stick it at the end..
2366 if (ret.card_n < 0) {
2367 ret.card_n = last_card_n;
2368 ret.card = ret.cards[last_card_n];
2369 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2370 ret.position = 'below';
2373 if (this.items[ret.items_n].el == dragged_card_el) {
2377 if (ret.position == 'below') {
2378 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2380 if (card_after && card_after.el == dragged_card_el) {
2387 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2389 if (card_before && card_before.el == dragged_card_el) {
2396 onNodeEnter : function(n, dd, e, data){
2399 onNodeOver : function(n, dd, e, data)
2402 var target_info = this.getTargetFromEvent(e,data.source.el);
2403 if (target_info === false) {
2404 this.dropPlaceHolder('hide');
2407 Roo.log(['getTargetFromEvent', target_info ]);
2410 this.dropPlaceHolder('show', target_info,data);
2414 onNodeOut : function(n, dd, e, data){
2415 this.dropPlaceHolder('hide');
2418 onNodeDrop : function(n, dd, e, data)
2421 // call drop - return false if
2423 // this could actually fail - if the Network drops..
2424 // we will ignore this at present..- client should probably reload
2425 // the whole set of cards if stuff like that fails.
2428 var info = this.getTargetFromEvent(e,data.source.el);
2429 if (info === false) {
2432 this.dropPlaceHolder('hide');
2438 this.acceptCard(data.source, info.position, info.card, info.items_n);
2442 firstChildCard : function()
2444 for (var i = 0;i< this.items.length;i++) {
2446 if (!this.items[i].el.hasClass('card')) {
2449 return this.items[i];
2451 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2456 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2459 acceptCard : function(move_card, position, next_to_card )
2461 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2465 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2468 var dom = move_card.el.dom;
2469 dom.parentNode.removeChild(dom);
2472 if (next_to_card !== false) {
2473 var cardel = next_to_card.el.dom;
2475 if (position == 'above') {
2476 cardel.parentNode.insertBefore(dom, cardel);
2477 } else if (cardel.nextSibling) {
2478 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2480 cardel.parentNode.append(dom);
2483 // card container???
2484 this.bodyEl.dom.append(dom);
2487 //FIXME HANDLE card = true
2489 // add this to the correct place in items.
2493 // remove Card from items.
2495 var old_parent = move_card.parent();
2497 old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2499 if (this.items.length) {
2501 //Roo.log([info.items_n, info.position, this.items.length]);
2502 for (var i =0; i < this.items.length; i++) {
2503 if (i == to_items_n && position == 'above') {
2504 nitems.push(move_card);
2506 nitems.push(this.items[i]);
2507 if (i == to_items_n && position == 'below') {
2508 nitems.push(move_card);
2511 this.items = nitems;
2512 Roo.log(this.items);
2514 this.items.push(move_card);
2517 move_card.parentId = this.id;
2525 /** Decide whether to drop above or below a View node. */
2526 getDropPoint : function(e, n, dd)
2531 if (n == this.bodyEl.dom) {
2534 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2535 var c = t + (b - t) / 2;
2536 var y = Roo.lib.Event.getPageY(e);
2543 onToggleCollapse : function(e)
2545 if (this.collapsed) {
2546 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2547 this.collapsableEl.addClass('show');
2548 this.collapsed = false;
2551 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2552 this.collapsableEl.removeClass('show');
2553 this.collapsed = true;
2558 onToggleRotate : function(e)
2560 this.collapsableEl.removeClass('show');
2561 this.footerEl.removeClass('d-none');
2562 this.el.removeClass('roo-card-rotated');
2563 this.el.removeClass('d-none');
2566 this.collapsableEl.addClass('show');
2567 this.rotated = false;
2568 this.fireEvent('rotate', this, this.rotated);
2571 this.el.addClass('roo-card-rotated');
2572 this.footerEl.addClass('d-none');
2573 this.el.select('.roo-collapsable').removeClass('show');
2575 this.rotated = true;
2576 this.fireEvent('rotate', this, this.rotated);
2580 dropPlaceHolder: function (action, info, data)
2582 if (this.dropEl === false) {
2583 this.dropEl = Roo.DomHelper.append(this.bodyEl, {
2587 this.dropEl.removeClass(['d-none', 'd-block']);
2588 if (action == 'hide') {
2590 this.dropEl.addClass('d-none');
2593 // FIXME - info.card == true!!!
2594 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2596 if (info.card !== true) {
2597 var cardel = info.card.el.dom;
2599 if (info.position == 'above') {
2600 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2601 } else if (cardel.nextSibling) {
2602 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2604 cardel.parentNode.append(this.dropEl.dom);
2607 // card container???
2608 this.bodyEl.dom.append(this.dropEl.dom);
2611 this.dropEl.addClass('d-block roo-card-dropzone');
2613 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2620 setHeaderText: function(html)
2622 this.headerEl.dom.innerHTML = html;
2631 * Card header - holder for the card header elements.
2636 * @class Roo.bootstrap.CardHeader
2637 * @extends Roo.bootstrap.Element
2638 * Bootstrap CardHeader class
2640 * Create a new Card Header - that you can embed children into
2641 * @param {Object} config The config object
2644 Roo.bootstrap.CardHeader = function(config){
2645 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2648 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2651 container_method : 'getCardHeader'
2664 * Card footer - holder for the card footer elements.
2669 * @class Roo.bootstrap.CardFooter
2670 * @extends Roo.bootstrap.Element
2671 * Bootstrap CardFooter class
2673 * Create a new Card Footer - that you can embed children into
2674 * @param {Object} config The config object
2677 Roo.bootstrap.CardFooter = function(config){
2678 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2681 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2684 container_method : 'getCardFooter'
2697 * Card header - holder for the card header elements.
2702 * @class Roo.bootstrap.CardImageTop
2703 * @extends Roo.bootstrap.Element
2704 * Bootstrap CardImageTop class
2706 * Create a new Card Image Top container
2707 * @param {Object} config The config object
2710 Roo.bootstrap.CardImageTop = function(config){
2711 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2714 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2717 container_method : 'getCardImageTop'
2735 * @class Roo.bootstrap.Img
2736 * @extends Roo.bootstrap.Component
2737 * Bootstrap Img class
2738 * @cfg {Boolean} imgResponsive false | true
2739 * @cfg {String} border rounded | circle | thumbnail
2740 * @cfg {String} src image source
2741 * @cfg {String} alt image alternative text
2742 * @cfg {String} href a tag href
2743 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2744 * @cfg {String} xsUrl xs image source
2745 * @cfg {String} smUrl sm image source
2746 * @cfg {String} mdUrl md image source
2747 * @cfg {String} lgUrl lg image source
2750 * Create a new Input
2751 * @param {Object} config The config object
2754 Roo.bootstrap.Img = function(config){
2755 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2761 * The img click event for the img.
2762 * @param {Roo.EventObject} e
2768 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2770 imgResponsive: true,
2780 getAutoCreate : function()
2782 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2783 return this.createSingleImg();
2788 cls: 'roo-image-responsive-group',
2793 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2795 if(!_this[size + 'Url']){
2801 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2802 html: _this.html || cfg.html,
2803 src: _this[size + 'Url']
2806 img.cls += ' roo-image-responsive-' + size;
2808 var s = ['xs', 'sm', 'md', 'lg'];
2810 s.splice(s.indexOf(size), 1);
2812 Roo.each(s, function(ss){
2813 img.cls += ' hidden-' + ss;
2816 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2817 cfg.cls += ' img-' + _this.border;
2821 cfg.alt = _this.alt;
2834 a.target = _this.target;
2838 cfg.cn.push((_this.href) ? a : img);
2845 createSingleImg : function()
2849 cls: (this.imgResponsive) ? 'img-responsive' : '',
2851 src : 'about:blank' // just incase src get's set to undefined?!?
2854 cfg.html = this.html || cfg.html;
2856 cfg.src = this.src || cfg.src;
2858 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2859 cfg.cls += ' img-' + this.border;
2876 a.target = this.target;
2881 return (this.href) ? a : cfg;
2884 initEvents: function()
2887 this.el.on('click', this.onClick, this);
2892 onClick : function(e)
2894 Roo.log('img onclick');
2895 this.fireEvent('click', this, e);
2898 * Sets the url of the image - used to update it
2899 * @param {String} url the url of the image
2902 setSrc : function(url)
2906 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2907 this.el.dom.src = url;
2911 this.el.select('img', true).first().dom.src = url;
2927 * @class Roo.bootstrap.Link
2928 * @extends Roo.bootstrap.Component
2929 * Bootstrap Link Class
2930 * @cfg {String} alt image alternative text
2931 * @cfg {String} href a tag href
2932 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2933 * @cfg {String} html the content of the link.
2934 * @cfg {String} anchor name for the anchor link
2935 * @cfg {String} fa - favicon
2937 * @cfg {Boolean} preventDefault (true | false) default false
2941 * Create a new Input
2942 * @param {Object} config The config object
2945 Roo.bootstrap.Link = function(config){
2946 Roo.bootstrap.Link.superclass.constructor.call(this, config);
2952 * The img click event for the img.
2953 * @param {Roo.EventObject} e
2959 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
2963 preventDefault: false,
2969 getAutoCreate : function()
2971 var html = this.html || '';
2973 if (this.fa !== false) {
2974 html = '<i class="fa fa-' + this.fa + '"></i>';
2979 // anchor's do not require html/href...
2980 if (this.anchor === false) {
2982 cfg.href = this.href || '#';
2984 cfg.name = this.anchor;
2985 if (this.html !== false || this.fa !== false) {
2988 if (this.href !== false) {
2989 cfg.href = this.href;
2993 if(this.alt !== false){
2998 if(this.target !== false) {
2999 cfg.target = this.target;
3005 initEvents: function() {
3007 if(!this.href || this.preventDefault){
3008 this.el.on('click', this.onClick, this);
3012 onClick : function(e)
3014 if(this.preventDefault){
3017 //Roo.log('img onclick');
3018 this.fireEvent('click', this, e);
3031 * @class Roo.bootstrap.Header
3032 * @extends Roo.bootstrap.Component
3033 * Bootstrap Header class
3034 * @cfg {String} html content of header
3035 * @cfg {Number} level (1|2|3|4|5|6) default 1
3038 * Create a new Header
3039 * @param {Object} config The config object
3043 Roo.bootstrap.Header = function(config){
3044 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3047 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3055 getAutoCreate : function(){
3060 tag: 'h' + (1 *this.level),
3061 html: this.html || ''
3073 * Ext JS Library 1.1.1
3074 * Copyright(c) 2006-2007, Ext JS, LLC.
3076 * Originally Released Under LGPL - original licence link has changed is not relivant.
3079 * <script type="text/javascript">
3083 * @class Roo.bootstrap.MenuMgr
3084 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3087 Roo.bootstrap.MenuMgr = function(){
3088 var menus, active, groups = {}, attached = false, lastShow = new Date();
3090 // private - called when first menu is created
3093 active = new Roo.util.MixedCollection();
3094 Roo.get(document).addKeyListener(27, function(){
3095 if(active.length > 0){
3103 if(active && active.length > 0){
3104 var c = active.clone();
3114 if(active.length < 1){
3115 Roo.get(document).un("mouseup", onMouseDown);
3123 var last = active.last();
3124 lastShow = new Date();
3127 Roo.get(document).on("mouseup", onMouseDown);
3132 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3133 m.parentMenu.activeChild = m;
3134 }else if(last && last.isVisible()){
3135 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3140 function onBeforeHide(m){
3142 m.activeChild.hide();
3144 if(m.autoHideTimer){
3145 clearTimeout(m.autoHideTimer);
3146 delete m.autoHideTimer;
3151 function onBeforeShow(m){
3152 var pm = m.parentMenu;
3153 if(!pm && !m.allowOtherMenus){
3155 }else if(pm && pm.activeChild && active != m){
3156 pm.activeChild.hide();
3160 // private this should really trigger on mouseup..
3161 function onMouseDown(e){
3162 Roo.log("on Mouse Up");
3164 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3165 Roo.log("MenuManager hideAll");
3174 function onBeforeCheck(mi, state){
3176 var g = groups[mi.group];
3177 for(var i = 0, l = g.length; i < l; i++){
3179 g[i].setChecked(false);
3188 * Hides all menus that are currently visible
3190 hideAll : function(){
3195 register : function(menu){
3199 menus[menu.id] = menu;
3200 menu.on("beforehide", onBeforeHide);
3201 menu.on("hide", onHide);
3202 menu.on("beforeshow", onBeforeShow);
3203 menu.on("show", onShow);
3205 if(g && menu.events["checkchange"]){
3209 groups[g].push(menu);
3210 menu.on("checkchange", onCheck);
3215 * Returns a {@link Roo.menu.Menu} object
3216 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3217 * be used to generate and return a new Menu instance.
3219 get : function(menu){
3220 if(typeof menu == "string"){ // menu id
3222 }else if(menu.events){ // menu instance
3225 /*else if(typeof menu.length == 'number'){ // array of menu items?
3226 return new Roo.bootstrap.Menu({items:menu});
3227 }else{ // otherwise, must be a config
3228 return new Roo.bootstrap.Menu(menu);
3235 unregister : function(menu){
3236 delete menus[menu.id];
3237 menu.un("beforehide", onBeforeHide);
3238 menu.un("hide", onHide);
3239 menu.un("beforeshow", onBeforeShow);
3240 menu.un("show", onShow);
3242 if(g && menu.events["checkchange"]){
3243 groups[g].remove(menu);
3244 menu.un("checkchange", onCheck);
3249 registerCheckable : function(menuItem){
3250 var g = menuItem.group;
3255 groups[g].push(menuItem);
3256 menuItem.on("beforecheckchange", onBeforeCheck);
3261 unregisterCheckable : function(menuItem){
3262 var g = menuItem.group;
3264 groups[g].remove(menuItem);
3265 menuItem.un("beforecheckchange", onBeforeCheck);
3277 * @class Roo.bootstrap.Menu
3278 * @extends Roo.bootstrap.Component
3279 * Bootstrap Menu class - container for MenuItems
3280 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3281 * @cfg {bool} hidden if the menu should be hidden when rendered.
3282 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3283 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3287 * @param {Object} config The config object
3291 Roo.bootstrap.Menu = function(config){
3292 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3293 if (this.registerMenu && this.type != 'treeview') {
3294 Roo.bootstrap.MenuMgr.register(this);
3301 * Fires before this menu is displayed (return false to block)
3302 * @param {Roo.menu.Menu} this
3307 * Fires before this menu is hidden (return false to block)
3308 * @param {Roo.menu.Menu} this
3313 * Fires after this menu is displayed
3314 * @param {Roo.menu.Menu} this
3319 * Fires after this menu is hidden
3320 * @param {Roo.menu.Menu} this
3325 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3326 * @param {Roo.menu.Menu} this
3327 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3328 * @param {Roo.EventObject} e
3333 * Fires when the mouse is hovering over this menu
3334 * @param {Roo.menu.Menu} this
3335 * @param {Roo.EventObject} e
3336 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3341 * Fires when the mouse exits this menu
3342 * @param {Roo.menu.Menu} this
3343 * @param {Roo.EventObject} e
3344 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3349 * Fires when a menu item contained in this menu is clicked
3350 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3351 * @param {Roo.EventObject} e
3355 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3358 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3362 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3365 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3367 registerMenu : true,
3369 menuItems :false, // stores the menu items..
3379 getChildContainer : function() {
3383 getAutoCreate : function(){
3385 //if (['right'].indexOf(this.align)!==-1) {
3386 // cfg.cn[1].cls += ' pull-right'
3392 cls : 'dropdown-menu' ,
3393 style : 'z-index:1000'
3397 if (this.type === 'submenu') {
3398 cfg.cls = 'submenu active';
3400 if (this.type === 'treeview') {
3401 cfg.cls = 'treeview-menu';
3406 initEvents : function() {
3408 // Roo.log("ADD event");
3409 // Roo.log(this.triggerEl.dom);
3411 this.triggerEl.on('click', this.onTriggerClick, this);
3413 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3416 if (this.triggerEl.hasClass('nav-item')) {
3417 // dropdown toggle on the 'a' in BS4?
3418 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3420 this.triggerEl.addClass('dropdown-toggle');
3423 this.el.on('touchstart' , this.onTouch, this);
3425 this.el.on('click' , this.onClick, this);
3427 this.el.on("mouseover", this.onMouseOver, this);
3428 this.el.on("mouseout", this.onMouseOut, this);
3432 findTargetItem : function(e)
3434 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3438 //Roo.log(t); Roo.log(t.id);
3440 //Roo.log(this.menuitems);
3441 return this.menuitems.get(t.id);
3443 //return this.items.get(t.menuItemId);
3449 onTouch : function(e)
3451 Roo.log("menu.onTouch");
3452 //e.stopEvent(); this make the user popdown broken
3456 onClick : function(e)
3458 Roo.log("menu.onClick");
3460 var t = this.findTargetItem(e);
3461 if(!t || t.isContainer){
3466 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3467 if(t == this.activeItem && t.shouldDeactivate(e)){
3468 this.activeItem.deactivate();
3469 delete this.activeItem;
3473 this.setActiveItem(t, true);
3481 Roo.log('pass click event');
3485 this.fireEvent("click", this, t, e);
3489 if(!t.href.length || t.href == '#'){
3490 (function() { _this.hide(); }).defer(100);
3495 onMouseOver : function(e){
3496 var t = this.findTargetItem(e);
3499 // if(t.canActivate && !t.disabled){
3500 // this.setActiveItem(t, true);
3504 this.fireEvent("mouseover", this, e, t);
3506 isVisible : function(){
3507 return !this.hidden;
3509 onMouseOut : function(e){
3510 var t = this.findTargetItem(e);
3513 // if(t == this.activeItem && t.shouldDeactivate(e)){
3514 // this.activeItem.deactivate();
3515 // delete this.activeItem;
3518 this.fireEvent("mouseout", this, e, t);
3523 * Displays this menu relative to another element
3524 * @param {String/HTMLElement/Roo.Element} element The element to align to
3525 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3526 * the element (defaults to this.defaultAlign)
3527 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3529 show : function(el, pos, parentMenu)
3531 if (false === this.fireEvent("beforeshow", this)) {
3532 Roo.log("show canceled");
3535 this.parentMenu = parentMenu;
3540 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3543 * Displays this menu at a specific xy position
3544 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3545 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3547 showAt : function(xy, parentMenu, /* private: */_e){
3548 this.parentMenu = parentMenu;
3553 this.fireEvent("beforeshow", this);
3554 //xy = this.el.adjustForConstraints(xy);
3558 this.hideMenuItems();
3559 this.hidden = false;
3560 this.triggerEl.addClass('open');
3561 this.el.addClass('show');
3563 // reassign x when hitting right
3564 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3565 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3568 // reassign y when hitting bottom
3569 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3570 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3573 // but the list may align on trigger left or trigger top... should it be a properity?
3575 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3580 this.fireEvent("show", this);
3586 this.doFocus.defer(50, this);
3590 doFocus : function(){
3592 this.focusEl.focus();
3597 * Hides this menu and optionally all parent menus
3598 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3600 hide : function(deep)
3602 if (false === this.fireEvent("beforehide", this)) {
3603 Roo.log("hide canceled");
3606 this.hideMenuItems();
3607 if(this.el && this.isVisible()){
3609 if(this.activeItem){
3610 this.activeItem.deactivate();
3611 this.activeItem = null;
3613 this.triggerEl.removeClass('open');;
3614 this.el.removeClass('show');
3616 this.fireEvent("hide", this);
3618 if(deep === true && this.parentMenu){
3619 this.parentMenu.hide(true);
3623 onTriggerClick : function(e)
3625 Roo.log('trigger click');
3627 var target = e.getTarget();
3629 Roo.log(target.nodeName.toLowerCase());
3631 if(target.nodeName.toLowerCase() === 'i'){
3637 onTriggerPress : function(e)
3639 Roo.log('trigger press');
3640 //Roo.log(e.getTarget());
3641 // Roo.log(this.triggerEl.dom);
3643 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3644 var pel = Roo.get(e.getTarget());
3645 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3646 Roo.log('is treeview or dropdown?');
3650 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3654 if (this.isVisible()) {
3659 this.show(this.triggerEl, '?', false);
3662 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3669 hideMenuItems : function()
3671 Roo.log("hide Menu Items");
3676 this.el.select('.open',true).each(function(aa) {
3678 aa.removeClass('open');
3682 addxtypeChild : function (tree, cntr) {
3683 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3685 this.menuitems.add(comp);
3697 this.getEl().dom.innerHTML = '';
3698 this.menuitems.clear();
3712 * @class Roo.bootstrap.MenuItem
3713 * @extends Roo.bootstrap.Component
3714 * Bootstrap MenuItem class
3715 * @cfg {String} html the menu label
3716 * @cfg {String} href the link
3717 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3718 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3719 * @cfg {Boolean} active used on sidebars to highlight active itesm
3720 * @cfg {String} fa favicon to show on left of menu item.
3721 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3725 * Create a new MenuItem
3726 * @param {Object} config The config object
3730 Roo.bootstrap.MenuItem = function(config){
3731 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3736 * The raw click event for the entire grid.
3737 * @param {Roo.bootstrap.MenuItem} this
3738 * @param {Roo.EventObject} e
3744 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3748 preventDefault: false,
3749 isContainer : false,
3753 getAutoCreate : function(){
3755 if(this.isContainer){
3758 cls: 'dropdown-menu-item '
3768 cls : 'dropdown-item',
3773 if (this.fa !== false) {
3776 cls : 'fa fa-' + this.fa
3785 cls: 'dropdown-menu-item',
3788 if (this.parent().type == 'treeview') {
3789 cfg.cls = 'treeview-menu';
3792 cfg.cls += ' active';
3797 anc.href = this.href || cfg.cn[0].href ;
3798 ctag.html = this.html || cfg.cn[0].html ;
3802 initEvents: function()
3804 if (this.parent().type == 'treeview') {
3805 this.el.select('a').on('click', this.onClick, this);
3809 this.menu.parentType = this.xtype;
3810 this.menu.triggerEl = this.el;
3811 this.menu = this.addxtype(Roo.apply({}, this.menu));
3815 onClick : function(e)
3817 Roo.log('item on click ');
3819 if(this.preventDefault){
3822 //this.parent().hideMenuItems();
3824 this.fireEvent('click', this, e);
3843 * @class Roo.bootstrap.MenuSeparator
3844 * @extends Roo.bootstrap.Component
3845 * Bootstrap MenuSeparator class
3848 * Create a new MenuItem
3849 * @param {Object} config The config object
3853 Roo.bootstrap.MenuSeparator = function(config){
3854 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3857 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3859 getAutoCreate : function(){
3878 * @class Roo.bootstrap.Modal
3879 * @extends Roo.bootstrap.Component
3880 * Bootstrap Modal class
3881 * @cfg {String} title Title of dialog
3882 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3883 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3884 * @cfg {Boolean} specificTitle default false
3885 * @cfg {Array} buttons Array of buttons or standard button set..
3886 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3887 * @cfg {Boolean} animate default true
3888 * @cfg {Boolean} allow_close default true
3889 * @cfg {Boolean} fitwindow default false
3890 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3891 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3892 * @cfg {String} size (sm|lg|xl) default empty
3893 * @cfg {Number} max_width set the max width of modal
3894 * @cfg {Boolean} editableTitle can the title be edited
3899 * Create a new Modal Dialog
3900 * @param {Object} config The config object
3903 Roo.bootstrap.Modal = function(config){
3904 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3909 * The raw btnclick event for the button
3910 * @param {Roo.EventObject} e
3915 * Fire when dialog resize
3916 * @param {Roo.bootstrap.Modal} this
3917 * @param {Roo.EventObject} e
3921 * @event titlechanged
3922 * Fire when the editable title has been changed
3923 * @param {Roo.bootstrap.Modal} this
3924 * @param {Roo.EventObject} value
3926 "titlechanged" : true
3929 this.buttons = this.buttons || [];
3932 this.tmpl = Roo.factory(this.tmpl);
3937 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3939 title : 'test dialog',
3949 specificTitle: false,
3951 buttonPosition: 'right',
3973 editableTitle : false,
3975 onRender : function(ct, position)
3977 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3980 var cfg = Roo.apply({}, this.getAutoCreate());
3983 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3985 //if (!cfg.name.length) {
3989 cfg.cls += ' ' + this.cls;
3992 cfg.style = this.style;
3994 this.el = Roo.get(document.body).createChild(cfg, position);
3996 //var type = this.el.dom.type;
3999 if(this.tabIndex !== undefined){
4000 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4003 this.dialogEl = this.el.select('.modal-dialog',true).first();
4004 this.bodyEl = this.el.select('.modal-body',true).first();
4005 this.closeEl = this.el.select('.modal-header .close', true).first();
4006 this.headerEl = this.el.select('.modal-header',true).first();
4007 this.titleEl = this.el.select('.modal-title',true).first();
4008 this.footerEl = this.el.select('.modal-footer',true).first();
4010 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4012 //this.el.addClass("x-dlg-modal");
4014 if (this.buttons.length) {
4015 Roo.each(this.buttons, function(bb) {
4016 var b = Roo.apply({}, bb);
4017 b.xns = b.xns || Roo.bootstrap;
4018 b.xtype = b.xtype || 'Button';
4019 if (typeof(b.listeners) == 'undefined') {
4020 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4023 var btn = Roo.factory(b);
4025 btn.render(this.getButtonContainer());
4029 // render the children.
4032 if(typeof(this.items) != 'undefined'){
4033 var items = this.items;
4036 for(var i =0;i < items.length;i++) {
4037 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4041 this.items = nitems;
4043 // where are these used - they used to be body/close/footer
4047 //this.el.addClass([this.fieldClass, this.cls]);
4051 getAutoCreate : function()
4053 // we will default to modal-body-overflow - might need to remove or make optional later.
4055 cls : 'modal-body enable-modal-body-overflow ',
4056 html : this.html || ''
4061 cls : 'modal-title',
4065 if(this.specificTitle){ // WTF is this?
4070 if (this.allow_close && Roo.bootstrap.version == 3) {
4080 if (this.editableTitle) {
4082 cls: 'form-control roo-editable-title d-none',
4088 if (this.allow_close && Roo.bootstrap.version == 4) {
4098 if(this.size.length){
4099 size = 'modal-' + this.size;
4102 var footer = Roo.bootstrap.version == 3 ?
4104 cls : 'modal-footer',
4108 cls: 'btn-' + this.buttonPosition
4113 { // BS4 uses mr-auto on left buttons....
4114 cls : 'modal-footer'
4125 cls: "modal-dialog " + size,
4128 cls : "modal-content",
4131 cls : 'modal-header',
4146 modal.cls += ' fade';
4152 getChildContainer : function() {
4157 getButtonContainer : function() {
4159 return Roo.bootstrap.version == 4 ?
4160 this.el.select('.modal-footer',true).first()
4161 : this.el.select('.modal-footer div',true).first();
4164 initEvents : function()
4166 if (this.allow_close) {
4167 this.closeEl.on('click', this.hide, this);
4169 Roo.EventManager.onWindowResize(this.resize, this, true);
4170 if (this.editableTitle) {
4171 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4172 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4173 this.headerEditEl.on('keyup', function(e) {
4174 if(e.isNavKeyPress()){
4175 this.toggleHeaderInput(false)
4178 this.headerEditEl.on('blur', function(e) {
4179 this.toggleHeaderInput(false)
4188 this.maskEl.setSize(
4189 Roo.lib.Dom.getViewWidth(true),
4190 Roo.lib.Dom.getViewHeight(true)
4193 if (this.fitwindow) {
4197 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4198 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4203 if(this.max_width !== 0) {
4205 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4208 this.setSize(w, this.height);
4212 if(this.max_height) {
4213 this.setSize(w,Math.min(
4215 Roo.lib.Dom.getViewportHeight(true) - 60
4221 if(!this.fit_content) {
4222 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4226 this.setSize(w, Math.min(
4228 this.headerEl.getHeight() +
4229 this.footerEl.getHeight() +
4230 this.getChildHeight(this.bodyEl.dom.childNodes),
4231 Roo.lib.Dom.getViewportHeight(true) - 60)
4237 setSize : function(w,h)
4248 if (!this.rendered) {
4252 //this.el.setStyle('display', 'block');
4253 this.el.removeClass('hideing');
4254 this.el.dom.style.display='block';
4256 Roo.get(document.body).addClass('modal-open');
4258 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4261 this.el.addClass('show');
4262 this.el.addClass('in');
4265 this.el.addClass('show');
4266 this.el.addClass('in');
4269 // not sure how we can show data in here..
4271 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4274 Roo.get(document.body).addClass("x-body-masked");
4276 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4277 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4278 this.maskEl.dom.style.display = 'block';
4279 this.maskEl.addClass('show');
4284 this.fireEvent('show', this);
4286 // set zindex here - otherwise it appears to be ignored...
4287 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4290 this.items.forEach( function(e) {
4291 e.layout ? e.layout() : false;
4299 if(this.fireEvent("beforehide", this) !== false){
4301 this.maskEl.removeClass('show');
4303 this.maskEl.dom.style.display = '';
4304 Roo.get(document.body).removeClass("x-body-masked");
4305 this.el.removeClass('in');
4306 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4308 if(this.animate){ // why
4309 this.el.addClass('hideing');
4310 this.el.removeClass('show');
4312 if (!this.el.hasClass('hideing')) {
4313 return; // it's been shown again...
4316 this.el.dom.style.display='';
4318 Roo.get(document.body).removeClass('modal-open');
4319 this.el.removeClass('hideing');
4323 this.el.removeClass('show');
4324 this.el.dom.style.display='';
4325 Roo.get(document.body).removeClass('modal-open');
4328 this.fireEvent('hide', this);
4331 isVisible : function()
4334 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4338 addButton : function(str, cb)
4342 var b = Roo.apply({}, { html : str } );
4343 b.xns = b.xns || Roo.bootstrap;
4344 b.xtype = b.xtype || 'Button';
4345 if (typeof(b.listeners) == 'undefined') {
4346 b.listeners = { click : cb.createDelegate(this) };
4349 var btn = Roo.factory(b);
4351 btn.render(this.getButtonContainer());
4357 setDefaultButton : function(btn)
4359 //this.el.select('.modal-footer').()
4362 resizeTo: function(w,h)
4364 this.dialogEl.setWidth(w);
4366 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4368 this.bodyEl.setHeight(h - diff);
4370 this.fireEvent('resize', this);
4373 setContentSize : function(w, h)
4377 onButtonClick: function(btn,e)
4380 this.fireEvent('btnclick', btn.name, e);
4383 * Set the title of the Dialog
4384 * @param {String} str new Title
4386 setTitle: function(str) {
4387 this.titleEl.dom.innerHTML = str;
4391 * Set the body of the Dialog
4392 * @param {String} str new Title
4394 setBody: function(str) {
4395 this.bodyEl.dom.innerHTML = str;
4398 * Set the body of the Dialog using the template
4399 * @param {Obj} data - apply this data to the template and replace the body contents.
4401 applyBody: function(obj)
4404 Roo.log("Error - using apply Body without a template");
4407 this.tmpl.overwrite(this.bodyEl, obj);
4410 getChildHeight : function(child_nodes)
4414 child_nodes.length == 0
4419 var child_height = 0;
4421 for(var i = 0; i < child_nodes.length; i++) {
4424 * for modal with tabs...
4425 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4427 var layout_childs = child_nodes[i].childNodes;
4429 for(var j = 0; j < layout_childs.length; j++) {
4431 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4433 var layout_body_childs = layout_childs[j].childNodes;
4435 for(var k = 0; k < layout_body_childs.length; k++) {
4437 if(layout_body_childs[k].classList.contains('navbar')) {
4438 child_height += layout_body_childs[k].offsetHeight;
4442 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4444 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4446 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4448 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4449 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4464 child_height += child_nodes[i].offsetHeight;
4465 // Roo.log(child_nodes[i].offsetHeight);
4468 return child_height;
4470 toggleHeaderInput : function(is_edit)
4473 if (is_edit && this.is_header_editing) {
4474 return; // already editing..
4478 this.headerEditEl.dom.value = this.title;
4479 this.headerEditEl.removeClass('d-none');
4480 this.headerEditEl.dom.focus();
4481 this.titleEl.addClass('d-none');
4483 this.is_header_editing = true;
4486 // flip back to not editing.
4487 this.title = this.headerEditEl.dom.value;
4488 this.headerEditEl.addClass('d-none');
4489 this.titleEl.removeClass('d-none');
4490 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4491 this.is_header_editing = false;
4492 this.fireEvent('titlechanged', this, this.title);
4501 Roo.apply(Roo.bootstrap.Modal, {
4503 * Button config that displays a single OK button
4512 * Button config that displays Yes and No buttons
4528 * Button config that displays OK and Cancel buttons
4543 * Button config that displays Yes, No and Cancel buttons
4568 * messagebox - can be used as a replace
4572 * @class Roo.MessageBox
4573 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4577 Roo.Msg.alert('Status', 'Changes saved successfully.');
4579 // Prompt for user data:
4580 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4582 // process text value...
4586 // Show a dialog using config options:
4588 title:'Save Changes?',
4589 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4590 buttons: Roo.Msg.YESNOCANCEL,
4597 Roo.bootstrap.MessageBox = function(){
4598 var dlg, opt, mask, waitTimer;
4599 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4600 var buttons, activeTextEl, bwidth;
4604 var handleButton = function(button){
4606 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4610 var handleHide = function(){
4612 dlg.el.removeClass(opt.cls);
4615 // Roo.TaskMgr.stop(waitTimer);
4616 // waitTimer = null;
4621 var updateButtons = function(b){
4624 buttons["ok"].hide();
4625 buttons["cancel"].hide();
4626 buttons["yes"].hide();
4627 buttons["no"].hide();
4628 dlg.footerEl.hide();
4632 dlg.footerEl.show();
4633 for(var k in buttons){
4634 if(typeof buttons[k] != "function"){
4637 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4638 width += buttons[k].el.getWidth()+15;
4648 var handleEsc = function(d, k, e){
4649 if(opt && opt.closable !== false){
4659 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4660 * @return {Roo.BasicDialog} The BasicDialog element
4662 getDialog : function(){
4664 dlg = new Roo.bootstrap.Modal( {
4667 //constraintoviewport:false,
4669 //collapsible : false,
4674 //buttonAlign:"center",
4675 closeClick : function(){
4676 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4679 handleButton("cancel");
4684 dlg.on("hide", handleHide);
4686 //dlg.addKeyListener(27, handleEsc);
4688 this.buttons = buttons;
4689 var bt = this.buttonText;
4690 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4691 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4692 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4693 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4695 bodyEl = dlg.bodyEl.createChild({
4697 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4698 '<textarea class="roo-mb-textarea"></textarea>' +
4699 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4701 msgEl = bodyEl.dom.firstChild;
4702 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4703 textboxEl.enableDisplayMode();
4704 textboxEl.addKeyListener([10,13], function(){
4705 if(dlg.isVisible() && opt && opt.buttons){
4708 }else if(opt.buttons.yes){
4709 handleButton("yes");
4713 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4714 textareaEl.enableDisplayMode();
4715 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4716 progressEl.enableDisplayMode();
4718 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4719 var pf = progressEl.dom.firstChild;
4721 pp = Roo.get(pf.firstChild);
4722 pp.setHeight(pf.offsetHeight);
4730 * Updates the message box body text
4731 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4732 * the XHTML-compliant non-breaking space character '&#160;')
4733 * @return {Roo.MessageBox} This message box
4735 updateText : function(text)
4737 if(!dlg.isVisible() && !opt.width){
4738 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4739 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4741 msgEl.innerHTML = text || ' ';
4743 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4744 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4746 Math.min(opt.width || cw , this.maxWidth),
4747 Math.max(opt.minWidth || this.minWidth, bwidth)
4750 activeTextEl.setWidth(w);
4752 if(dlg.isVisible()){
4753 dlg.fixedcenter = false;
4755 // to big, make it scroll. = But as usual stupid IE does not support
4758 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4759 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4760 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4762 bodyEl.dom.style.height = '';
4763 bodyEl.dom.style.overflowY = '';
4766 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4768 bodyEl.dom.style.overflowX = '';
4771 dlg.setContentSize(w, bodyEl.getHeight());
4772 if(dlg.isVisible()){
4773 dlg.fixedcenter = true;
4779 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4780 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4781 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4782 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4783 * @return {Roo.MessageBox} This message box
4785 updateProgress : function(value, text){
4787 this.updateText(text);
4790 if (pp) { // weird bug on my firefox - for some reason this is not defined
4791 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4792 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4798 * Returns true if the message box is currently displayed
4799 * @return {Boolean} True if the message box is visible, else false
4801 isVisible : function(){
4802 return dlg && dlg.isVisible();
4806 * Hides the message box if it is displayed
4809 if(this.isVisible()){
4815 * Displays a new message box, or reinitializes an existing message box, based on the config options
4816 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4817 * The following config object properties are supported:
4819 Property Type Description
4820 ---------- --------------- ------------------------------------------------------------------------------------
4821 animEl String/Element An id or Element from which the message box should animate as it opens and
4822 closes (defaults to undefined)
4823 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4824 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4825 closable Boolean False to hide the top-right close button (defaults to true). Note that
4826 progress and wait dialogs will ignore this property and always hide the
4827 close button as they can only be closed programmatically.
4828 cls String A custom CSS class to apply to the message box element
4829 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4830 displayed (defaults to 75)
4831 fn Function A callback function to execute after closing the dialog. The arguments to the
4832 function will be btn (the name of the button that was clicked, if applicable,
4833 e.g. "ok"), and text (the value of the active text field, if applicable).
4834 Progress and wait dialogs will ignore this option since they do not respond to
4835 user actions and can only be closed programmatically, so any required function
4836 should be called by the same code after it closes the dialog.
4837 icon String A CSS class that provides a background image to be used as an icon for
4838 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4839 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4840 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4841 modal Boolean False to allow user interaction with the page while the message box is
4842 displayed (defaults to true)
4843 msg String A string that will replace the existing message box body text (defaults
4844 to the XHTML-compliant non-breaking space character ' ')
4845 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4846 progress Boolean True to display a progress bar (defaults to false)
4847 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4848 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4849 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4850 title String The title text
4851 value String The string value to set into the active textbox element if displayed
4852 wait Boolean True to display a progress bar (defaults to false)
4853 width Number The width of the dialog in pixels
4860 msg: 'Please enter your address:',
4862 buttons: Roo.MessageBox.OKCANCEL,
4865 animEl: 'addAddressBtn'
4868 * @param {Object} config Configuration options
4869 * @return {Roo.MessageBox} This message box
4871 show : function(options)
4874 // this causes nightmares if you show one dialog after another
4875 // especially on callbacks..
4877 if(this.isVisible()){
4880 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4881 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4882 Roo.log("New Dialog Message:" + options.msg )
4883 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4884 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4887 var d = this.getDialog();
4889 d.setTitle(opt.title || " ");
4890 d.closeEl.setDisplayed(opt.closable !== false);
4891 activeTextEl = textboxEl;
4892 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4897 textareaEl.setHeight(typeof opt.multiline == "number" ?
4898 opt.multiline : this.defaultTextHeight);
4899 activeTextEl = textareaEl;
4908 progressEl.setDisplayed(opt.progress === true);
4910 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4912 this.updateProgress(0);
4913 activeTextEl.dom.value = opt.value || "";
4915 dlg.setDefaultButton(activeTextEl);
4917 var bs = opt.buttons;
4921 }else if(bs && bs.yes){
4922 db = buttons["yes"];
4924 dlg.setDefaultButton(db);
4926 bwidth = updateButtons(opt.buttons);
4927 this.updateText(opt.msg);
4929 d.el.addClass(opt.cls);
4931 d.proxyDrag = opt.proxyDrag === true;
4932 d.modal = opt.modal !== false;
4933 d.mask = opt.modal !== false ? mask : false;
4935 // force it to the end of the z-index stack so it gets a cursor in FF
4936 document.body.appendChild(dlg.el.dom);
4937 d.animateTarget = null;
4938 d.show(options.animEl);
4944 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
4945 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4946 * and closing the message box when the process is complete.
4947 * @param {String} title The title bar text
4948 * @param {String} msg The message box body text
4949 * @return {Roo.MessageBox} This message box
4951 progress : function(title, msg){
4958 minWidth: this.minProgressWidth,
4965 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4966 * If a callback function is passed it will be called after the user clicks the button, and the
4967 * id of the button that was clicked will be passed as the only parameter to the callback
4968 * (could also be the top-right close button).
4969 * @param {String} title The title bar text
4970 * @param {String} msg The message box body text
4971 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4972 * @param {Object} scope (optional) The scope of the callback function
4973 * @return {Roo.MessageBox} This message box
4975 alert : function(title, msg, fn, scope)
4990 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
4991 * interaction while waiting for a long-running process to complete that does not have defined intervals.
4992 * You are responsible for closing the message box when the process is complete.
4993 * @param {String} msg The message box body text
4994 * @param {String} title (optional) The title bar text
4995 * @return {Roo.MessageBox} This message box
4997 wait : function(msg, title){
5008 waitTimer = Roo.TaskMgr.start({
5010 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5018 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5019 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5020 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5021 * @param {String} title The title bar text
5022 * @param {String} msg The message box body text
5023 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5024 * @param {Object} scope (optional) The scope of the callback function
5025 * @return {Roo.MessageBox} This message box
5027 confirm : function(title, msg, fn, scope){
5031 buttons: this.YESNO,
5040 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5041 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5042 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5043 * (could also be the top-right close button) and the text that was entered will be passed as the two
5044 * parameters to the callback.
5045 * @param {String} title The title bar text
5046 * @param {String} msg The message box body text
5047 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5048 * @param {Object} scope (optional) The scope of the callback function
5049 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5050 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5051 * @return {Roo.MessageBox} This message box
5053 prompt : function(title, msg, fn, scope, multiline){
5057 buttons: this.OKCANCEL,
5062 multiline: multiline,
5069 * Button config that displays a single OK button
5074 * Button config that displays Yes and No buttons
5077 YESNO : {yes:true, no:true},
5079 * Button config that displays OK and Cancel buttons
5082 OKCANCEL : {ok:true, cancel:true},
5084 * Button config that displays Yes, No and Cancel buttons
5087 YESNOCANCEL : {yes:true, no:true, cancel:true},
5090 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5093 defaultTextHeight : 75,
5095 * The maximum width in pixels of the message box (defaults to 600)
5100 * The minimum width in pixels of the message box (defaults to 100)
5105 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5106 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5109 minProgressWidth : 250,
5111 * An object containing the default button text strings that can be overriden for localized language support.
5112 * Supported properties are: ok, cancel, yes and no.
5113 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5126 * Shorthand for {@link Roo.MessageBox}
5128 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5129 Roo.Msg = Roo.Msg || Roo.MessageBox;
5138 * @class Roo.bootstrap.Navbar
5139 * @extends Roo.bootstrap.Component
5140 * Bootstrap Navbar class
5143 * Create a new Navbar
5144 * @param {Object} config The config object
5148 Roo.bootstrap.Navbar = function(config){
5149 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5153 * @event beforetoggle
5154 * Fire before toggle the menu
5155 * @param {Roo.EventObject} e
5157 "beforetoggle" : true
5161 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5170 getAutoCreate : function(){
5173 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5177 initEvents :function ()
5179 //Roo.log(this.el.select('.navbar-toggle',true));
5180 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5187 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5189 var size = this.el.getSize();
5190 this.maskEl.setSize(size.width, size.height);
5191 this.maskEl.enableDisplayMode("block");
5200 getChildContainer : function()
5202 if (this.el && this.el.select('.collapse').getCount()) {
5203 return this.el.select('.collapse',true).first();
5218 onToggle : function()
5221 if(this.fireEvent('beforetoggle', this) === false){
5224 var ce = this.el.select('.navbar-collapse',true).first();
5226 if (!ce.hasClass('show')) {
5236 * Expand the navbar pulldown
5238 expand : function ()
5241 var ce = this.el.select('.navbar-collapse',true).first();
5242 if (ce.hasClass('collapsing')) {
5245 ce.dom.style.height = '';
5247 ce.addClass('in'); // old...
5248 ce.removeClass('collapse');
5249 ce.addClass('show');
5250 var h = ce.getHeight();
5252 ce.removeClass('show');
5253 // at this point we should be able to see it..
5254 ce.addClass('collapsing');
5256 ce.setHeight(0); // resize it ...
5257 ce.on('transitionend', function() {
5258 //Roo.log('done transition');
5259 ce.removeClass('collapsing');
5260 ce.addClass('show');
5261 ce.removeClass('collapse');
5263 ce.dom.style.height = '';
5264 }, this, { single: true} );
5266 ce.dom.scrollTop = 0;
5269 * Collapse the navbar pulldown
5271 collapse : function()
5273 var ce = this.el.select('.navbar-collapse',true).first();
5275 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5276 // it's collapsed or collapsing..
5279 ce.removeClass('in'); // old...
5280 ce.setHeight(ce.getHeight());
5281 ce.removeClass('show');
5282 ce.addClass('collapsing');
5284 ce.on('transitionend', function() {
5285 ce.dom.style.height = '';
5286 ce.removeClass('collapsing');
5287 ce.addClass('collapse');
5288 }, this, { single: true} );
5308 * @class Roo.bootstrap.NavSimplebar
5309 * @extends Roo.bootstrap.Navbar
5310 * Bootstrap Sidebar class
5312 * @cfg {Boolean} inverse is inverted color
5314 * @cfg {String} type (nav | pills | tabs)
5315 * @cfg {Boolean} arrangement stacked | justified
5316 * @cfg {String} align (left | right) alignment
5318 * @cfg {Boolean} main (true|false) main nav bar? default false
5319 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5321 * @cfg {String} tag (header|footer|nav|div) default is nav
5323 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5327 * Create a new Sidebar
5328 * @param {Object} config The config object
5332 Roo.bootstrap.NavSimplebar = function(config){
5333 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5336 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5352 getAutoCreate : function(){
5356 tag : this.tag || 'div',
5357 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5359 if (['light','white'].indexOf(this.weight) > -1) {
5360 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5362 cfg.cls += ' bg-' + this.weight;
5365 cfg.cls += ' navbar-inverse';
5369 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5371 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5380 cls: 'nav nav-' + this.xtype,
5386 this.type = this.type || 'nav';
5387 if (['tabs','pills'].indexOf(this.type) != -1) {
5388 cfg.cn[0].cls += ' nav-' + this.type
5392 if (this.type!=='nav') {
5393 Roo.log('nav type must be nav/tabs/pills')
5395 cfg.cn[0].cls += ' navbar-nav'
5401 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5402 cfg.cn[0].cls += ' nav-' + this.arrangement;
5406 if (this.align === 'right') {
5407 cfg.cn[0].cls += ' navbar-right';
5432 * navbar-expand-md fixed-top
5436 * @class Roo.bootstrap.NavHeaderbar
5437 * @extends Roo.bootstrap.NavSimplebar
5438 * Bootstrap Sidebar class
5440 * @cfg {String} brand what is brand
5441 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5442 * @cfg {String} brand_href href of the brand
5443 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5444 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5445 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5446 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5449 * Create a new Sidebar
5450 * @param {Object} config The config object
5454 Roo.bootstrap.NavHeaderbar = function(config){
5455 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5459 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5466 desktopCenter : false,
5469 getAutoCreate : function(){
5472 tag: this.nav || 'nav',
5473 cls: 'navbar navbar-expand-md',
5479 if (this.desktopCenter) {
5480 cn.push({cls : 'container', cn : []});
5488 cls: 'navbar-toggle navbar-toggler',
5489 'data-toggle': 'collapse',
5494 html: 'Toggle navigation'
5498 cls: 'icon-bar navbar-toggler-icon'
5511 cn.push( Roo.bootstrap.version == 4 ? btn : {
5513 cls: 'navbar-header',
5522 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5526 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5528 if (['light','white'].indexOf(this.weight) > -1) {
5529 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5531 cfg.cls += ' bg-' + this.weight;
5534 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5535 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5537 // tag can override this..
5539 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5542 if (this.brand !== '') {
5543 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5544 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5546 href: this.brand_href ? this.brand_href : '#',
5547 cls: 'navbar-brand',
5555 cfg.cls += ' main-nav';
5563 getHeaderChildContainer : function()
5565 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5566 return this.el.select('.navbar-header',true).first();
5569 return this.getChildContainer();
5572 getChildContainer : function()
5575 return this.el.select('.roo-navbar-collapse',true).first();
5580 initEvents : function()
5582 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5584 if (this.autohide) {
5589 Roo.get(document).on('scroll',function(e) {
5590 var ns = Roo.get(document).getScroll().top;
5591 var os = prevScroll;
5595 ft.removeClass('slideDown');
5596 ft.addClass('slideUp');
5599 ft.removeClass('slideUp');
5600 ft.addClass('slideDown');
5621 * @class Roo.bootstrap.NavSidebar
5622 * @extends Roo.bootstrap.Navbar
5623 * Bootstrap Sidebar class
5626 * Create a new Sidebar
5627 * @param {Object} config The config object
5631 Roo.bootstrap.NavSidebar = function(config){
5632 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5635 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5637 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5639 getAutoCreate : function(){
5644 cls: 'sidebar sidebar-nav'
5666 * @class Roo.bootstrap.NavGroup
5667 * @extends Roo.bootstrap.Component
5668 * Bootstrap NavGroup class
5669 * @cfg {String} align (left|right)
5670 * @cfg {Boolean} inverse
5671 * @cfg {String} type (nav|pills|tab) default nav
5672 * @cfg {String} navId - reference Id for navbar.
5676 * Create a new nav group
5677 * @param {Object} config The config object
5680 Roo.bootstrap.NavGroup = function(config){
5681 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5684 Roo.bootstrap.NavGroup.register(this);
5688 * Fires when the active item changes
5689 * @param {Roo.bootstrap.NavGroup} this
5690 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5691 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5698 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5709 getAutoCreate : function()
5711 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5717 if (Roo.bootstrap.version == 4) {
5718 if (['tabs','pills'].indexOf(this.type) != -1) {
5719 cfg.cls += ' nav-' + this.type;
5721 // trying to remove so header bar can right align top?
5722 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5723 // do not use on header bar...
5724 cfg.cls += ' navbar-nav';
5729 if (['tabs','pills'].indexOf(this.type) != -1) {
5730 cfg.cls += ' nav-' + this.type
5732 if (this.type !== 'nav') {
5733 Roo.log('nav type must be nav/tabs/pills')
5735 cfg.cls += ' navbar-nav'
5739 if (this.parent() && this.parent().sidebar) {
5742 cls: 'dashboard-menu sidebar-menu'
5748 if (this.form === true) {
5751 cls: 'navbar-form form-inline'
5753 //nav navbar-right ml-md-auto
5754 if (this.align === 'right') {
5755 cfg.cls += ' navbar-right ml-md-auto';
5757 cfg.cls += ' navbar-left';
5761 if (this.align === 'right') {
5762 cfg.cls += ' navbar-right ml-md-auto';
5764 cfg.cls += ' mr-auto';
5768 cfg.cls += ' navbar-inverse';
5776 * sets the active Navigation item
5777 * @param {Roo.bootstrap.NavItem} the new current navitem
5779 setActiveItem : function(item)
5782 Roo.each(this.navItems, function(v){
5787 v.setActive(false, true);
5794 item.setActive(true, true);
5795 this.fireEvent('changed', this, item, prev);
5800 * gets the active Navigation item
5801 * @return {Roo.bootstrap.NavItem} the current navitem
5803 getActive : function()
5807 Roo.each(this.navItems, function(v){
5818 indexOfNav : function()
5822 Roo.each(this.navItems, function(v,i){
5833 * adds a Navigation item
5834 * @param {Roo.bootstrap.NavItem} the navitem to add
5836 addItem : function(cfg)
5838 if (this.form && Roo.bootstrap.version == 4) {
5841 var cn = new Roo.bootstrap.NavItem(cfg);
5843 cn.parentId = this.id;
5844 cn.onRender(this.el, null);
5848 * register a Navigation item
5849 * @param {Roo.bootstrap.NavItem} the navitem to add
5851 register : function(item)
5853 this.navItems.push( item);
5854 item.navId = this.navId;
5859 * clear all the Navigation item
5862 clearAll : function()
5865 this.el.dom.innerHTML = '';
5868 getNavItem: function(tabId)
5871 Roo.each(this.navItems, function(e) {
5872 if (e.tabId == tabId) {
5882 setActiveNext : function()
5884 var i = this.indexOfNav(this.getActive());
5885 if (i > this.navItems.length) {
5888 this.setActiveItem(this.navItems[i+1]);
5890 setActivePrev : function()
5892 var i = this.indexOfNav(this.getActive());
5896 this.setActiveItem(this.navItems[i-1]);
5898 clearWasActive : function(except) {
5899 Roo.each(this.navItems, function(e) {
5900 if (e.tabId != except.tabId && e.was_active) {
5901 e.was_active = false;
5908 getWasActive : function ()
5911 Roo.each(this.navItems, function(e) {
5926 Roo.apply(Roo.bootstrap.NavGroup, {
5930 * register a Navigation Group
5931 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5933 register : function(navgrp)
5935 this.groups[navgrp.navId] = navgrp;
5939 * fetch a Navigation Group based on the navigation ID
5940 * @param {string} the navgroup to add
5941 * @returns {Roo.bootstrap.NavGroup} the navgroup
5943 get: function(navId) {
5944 if (typeof(this.groups[navId]) == 'undefined') {
5946 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5948 return this.groups[navId] ;
5963 * @class Roo.bootstrap.NavItem
5964 * @extends Roo.bootstrap.Component
5965 * Bootstrap Navbar.NavItem class
5966 * @cfg {String} href link to
5967 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5969 * @cfg {String} html content of button
5970 * @cfg {String} badge text inside badge
5971 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5972 * @cfg {String} glyphicon DEPRICATED - use fa
5973 * @cfg {String} icon DEPRICATED - use fa
5974 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5975 * @cfg {Boolean} active Is item active
5976 * @cfg {Boolean} disabled Is item disabled
5978 * @cfg {Boolean} preventDefault (true | false) default false
5979 * @cfg {String} tabId the tab that this item activates.
5980 * @cfg {String} tagtype (a|span) render as a href or span?
5981 * @cfg {Boolean} animateRef (true|false) link to element default false
5984 * Create a new Navbar Item
5985 * @param {Object} config The config object
5987 Roo.bootstrap.NavItem = function(config){
5988 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5993 * The raw click event for the entire grid.
5994 * @param {Roo.EventObject} e
5999 * Fires when the active item active state changes
6000 * @param {Roo.bootstrap.NavItem} this
6001 * @param {boolean} state the new state
6007 * Fires when scroll to element
6008 * @param {Roo.bootstrap.NavItem} this
6009 * @param {Object} options
6010 * @param {Roo.EventObject} e
6018 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6027 preventDefault : false,
6035 button_outline : false,
6039 getAutoCreate : function(){
6047 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6049 if (this.disabled) {
6050 cfg.cls += ' disabled';
6054 if (this.button_weight.length) {
6055 cfg.tag = this.href ? 'a' : 'button';
6056 cfg.html = this.html || '';
6057 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6059 cfg.href = this.href;
6062 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6065 // menu .. should add dropdown-menu class - so no need for carat..
6067 if (this.badge !== '') {
6069 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6074 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6078 href : this.href || "#",
6079 html: this.html || ''
6082 if (this.tagtype == 'a') {
6083 cfg.cn[0].cls = 'nav-link';
6086 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6089 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6091 if(this.glyphicon) {
6092 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6097 cfg.cn[0].html += " <span class='caret'></span>";
6101 if (this.badge !== '') {
6103 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6111 onRender : function(ct, position)
6113 // Roo.log("Call onRender: " + this.xtype);
6114 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6118 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6119 this.navLink = this.el.select('.nav-link',true).first();
6124 initEvents: function()
6126 if (typeof (this.menu) != 'undefined') {
6127 this.menu.parentType = this.xtype;
6128 this.menu.triggerEl = this.el;
6129 this.menu = this.addxtype(Roo.apply({}, this.menu));
6132 this.el.select('a',true).on('click', this.onClick, this);
6134 if(this.tagtype == 'span'){
6135 this.el.select('span',true).on('click', this.onClick, this);
6138 // at this point parent should be available..
6139 this.parent().register(this);
6142 onClick : function(e)
6144 if (e.getTarget('.dropdown-menu-item')) {
6145 // did you click on a menu itemm.... - then don't trigger onclick..
6150 this.preventDefault ||
6153 Roo.log("NavItem - prevent Default?");
6157 if (this.disabled) {
6161 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6162 if (tg && tg.transition) {
6163 Roo.log("waiting for the transitionend");
6169 //Roo.log("fire event clicked");
6170 if(this.fireEvent('click', this, e) === false){
6174 if(this.tagtype == 'span'){
6178 //Roo.log(this.href);
6179 var ael = this.el.select('a',true).first();
6182 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6183 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6184 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6185 return; // ignore... - it's a 'hash' to another page.
6187 Roo.log("NavItem - prevent Default?");
6189 this.scrollToElement(e);
6193 var p = this.parent();
6195 if (['tabs','pills'].indexOf(p.type)!==-1) {
6196 if (typeof(p.setActiveItem) !== 'undefined') {
6197 p.setActiveItem(this);
6201 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6202 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6203 // remove the collapsed menu expand...
6204 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6208 isActive: function () {
6211 setActive : function(state, fire, is_was_active)
6213 if (this.active && !state && this.navId) {
6214 this.was_active = true;
6215 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6217 nv.clearWasActive(this);
6221 this.active = state;
6224 this.el.removeClass('active');
6225 this.navLink ? this.navLink.removeClass('active') : false;
6226 } else if (!this.el.hasClass('active')) {
6228 this.el.addClass('active');
6229 if (Roo.bootstrap.version == 4 && this.navLink ) {
6230 this.navLink.addClass('active');
6235 this.fireEvent('changed', this, state);
6238 // show a panel if it's registered and related..
6240 if (!this.navId || !this.tabId || !state || is_was_active) {
6244 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6248 var pan = tg.getPanelByName(this.tabId);
6252 // if we can not flip to new panel - go back to old nav highlight..
6253 if (false == tg.showPanel(pan)) {
6254 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6256 var onav = nv.getWasActive();
6258 onav.setActive(true, false, true);
6267 // this should not be here...
6268 setDisabled : function(state)
6270 this.disabled = state;
6272 this.el.removeClass('disabled');
6273 } else if (!this.el.hasClass('disabled')) {
6274 this.el.addClass('disabled');
6280 * Fetch the element to display the tooltip on.
6281 * @return {Roo.Element} defaults to this.el
6283 tooltipEl : function()
6285 return this.el.select('' + this.tagtype + '', true).first();
6288 scrollToElement : function(e)
6290 var c = document.body;
6293 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6295 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6296 c = document.documentElement;
6299 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6305 var o = target.calcOffsetsTo(c);
6312 this.fireEvent('scrollto', this, options, e);
6314 Roo.get(c).scrollTo('top', options.value, true);
6327 * <span> icon </span>
6328 * <span> text </span>
6329 * <span>badge </span>
6333 * @class Roo.bootstrap.NavSidebarItem
6334 * @extends Roo.bootstrap.NavItem
6335 * Bootstrap Navbar.NavSidebarItem class
6336 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6337 * {Boolean} open is the menu open
6338 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6339 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6340 * {String} buttonSize (sm|md|lg)the extra classes for the button
6341 * {Boolean} showArrow show arrow next to the text (default true)
6343 * Create a new Navbar Button
6344 * @param {Object} config The config object
6346 Roo.bootstrap.NavSidebarItem = function(config){
6347 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6352 * The raw click event for the entire grid.
6353 * @param {Roo.EventObject} e
6358 * Fires when the active item active state changes
6359 * @param {Roo.bootstrap.NavSidebarItem} this
6360 * @param {boolean} state the new state
6368 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6370 badgeWeight : 'default',
6376 buttonWeight : 'default',
6382 getAutoCreate : function(){
6387 href : this.href || '#',
6393 if(this.buttonView){
6396 href : this.href || '#',
6397 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6410 cfg.cls += ' active';
6413 if (this.disabled) {
6414 cfg.cls += ' disabled';
6417 cfg.cls += ' open x-open';
6420 if (this.glyphicon || this.icon) {
6421 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6422 a.cn.push({ tag : 'i', cls : c }) ;
6425 if(!this.buttonView){
6428 html : this.html || ''
6435 if (this.badge !== '') {
6436 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6442 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6445 a.cls += ' dropdown-toggle treeview' ;
6451 initEvents : function()
6453 if (typeof (this.menu) != 'undefined') {
6454 this.menu.parentType = this.xtype;
6455 this.menu.triggerEl = this.el;
6456 this.menu = this.addxtype(Roo.apply({}, this.menu));
6459 this.el.on('click', this.onClick, this);
6461 if(this.badge !== ''){
6462 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6467 onClick : function(e)
6474 if(this.preventDefault){
6478 this.fireEvent('click', this, e);
6481 disable : function()
6483 this.setDisabled(true);
6488 this.setDisabled(false);
6491 setDisabled : function(state)
6493 if(this.disabled == state){
6497 this.disabled = state;
6500 this.el.addClass('disabled');
6504 this.el.removeClass('disabled');
6509 setActive : function(state)
6511 if(this.active == state){
6515 this.active = state;
6518 this.el.addClass('active');
6522 this.el.removeClass('active');
6527 isActive: function ()
6532 setBadge : function(str)
6538 this.badgeEl.dom.innerHTML = str;
6555 * @class Roo.bootstrap.Row
6556 * @extends Roo.bootstrap.Component
6557 * Bootstrap Row class (contains columns...)
6561 * @param {Object} config The config object
6564 Roo.bootstrap.Row = function(config){
6565 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6568 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6570 getAutoCreate : function(){
6589 * @class Roo.bootstrap.Pagination
6590 * @extends Roo.bootstrap.Component
6591 * Bootstrap Pagination class
6592 * @cfg {String} size xs | sm | md | lg
6593 * @cfg {Boolean} inverse false | true
6596 * Create a new Pagination
6597 * @param {Object} config The config object
6600 Roo.bootstrap.Pagination = function(config){
6601 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6604 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6610 getAutoCreate : function(){
6616 cfg.cls += ' inverse';
6622 cfg.cls += " " + this.cls;
6640 * @class Roo.bootstrap.PaginationItem
6641 * @extends Roo.bootstrap.Component
6642 * Bootstrap PaginationItem class
6643 * @cfg {String} html text
6644 * @cfg {String} href the link
6645 * @cfg {Boolean} preventDefault (true | false) default true
6646 * @cfg {Boolean} active (true | false) default false
6647 * @cfg {Boolean} disabled default false
6651 * Create a new PaginationItem
6652 * @param {Object} config The config object
6656 Roo.bootstrap.PaginationItem = function(config){
6657 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6662 * The raw click event for the entire grid.
6663 * @param {Roo.EventObject} e
6669 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6673 preventDefault: true,
6678 getAutoCreate : function(){
6684 href : this.href ? this.href : '#',
6685 html : this.html ? this.html : ''
6695 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6699 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6705 initEvents: function() {
6707 this.el.on('click', this.onClick, this);
6710 onClick : function(e)
6712 Roo.log('PaginationItem on click ');
6713 if(this.preventDefault){
6721 this.fireEvent('click', this, e);
6737 * @class Roo.bootstrap.Slider
6738 * @extends Roo.bootstrap.Component
6739 * Bootstrap Slider class
6742 * Create a new Slider
6743 * @param {Object} config The config object
6746 Roo.bootstrap.Slider = function(config){
6747 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6750 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6752 getAutoCreate : function(){
6756 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6760 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6772 * Ext JS Library 1.1.1
6773 * Copyright(c) 2006-2007, Ext JS, LLC.
6775 * Originally Released Under LGPL - original licence link has changed is not relivant.
6778 * <script type="text/javascript">
6783 * @class Roo.grid.ColumnModel
6784 * @extends Roo.util.Observable
6785 * This is the default implementation of a ColumnModel used by the Grid. It defines
6786 * the columns in the grid.
6789 var colModel = new Roo.grid.ColumnModel([
6790 {header: "Ticker", width: 60, sortable: true, locked: true},
6791 {header: "Company Name", width: 150, sortable: true},
6792 {header: "Market Cap.", width: 100, sortable: true},
6793 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6794 {header: "Employees", width: 100, sortable: true, resizable: false}
6799 * The config options listed for this class are options which may appear in each
6800 * individual column definition.
6801 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6803 * @param {Object} config An Array of column config objects. See this class's
6804 * config objects for details.
6806 Roo.grid.ColumnModel = function(config){
6808 * The config passed into the constructor
6810 this.config = config;
6813 // if no id, create one
6814 // if the column does not have a dataIndex mapping,
6815 // map it to the order it is in the config
6816 for(var i = 0, len = config.length; i < len; i++){
6818 if(typeof c.dataIndex == "undefined"){
6821 if(typeof c.renderer == "string"){
6822 c.renderer = Roo.util.Format[c.renderer];
6824 if(typeof c.id == "undefined"){
6827 if(c.editor && c.editor.xtype){
6828 c.editor = Roo.factory(c.editor, Roo.grid);
6830 if(c.editor && c.editor.isFormField){
6831 c.editor = new Roo.grid.GridEditor(c.editor);
6833 this.lookup[c.id] = c;
6837 * The width of columns which have no width specified (defaults to 100)
6840 this.defaultWidth = 100;
6843 * Default sortable of columns which have no sortable specified (defaults to false)
6846 this.defaultSortable = false;
6850 * @event widthchange
6851 * Fires when the width of a column changes.
6852 * @param {ColumnModel} this
6853 * @param {Number} columnIndex The column index
6854 * @param {Number} newWidth The new width
6856 "widthchange": true,
6858 * @event headerchange
6859 * Fires when the text of a header changes.
6860 * @param {ColumnModel} this
6861 * @param {Number} columnIndex The column index
6862 * @param {Number} newText The new header text
6864 "headerchange": true,
6866 * @event hiddenchange
6867 * Fires when a column is hidden or "unhidden".
6868 * @param {ColumnModel} this
6869 * @param {Number} columnIndex The column index
6870 * @param {Boolean} hidden true if hidden, false otherwise
6872 "hiddenchange": true,
6874 * @event columnmoved
6875 * Fires when a column is moved.
6876 * @param {ColumnModel} this
6877 * @param {Number} oldIndex
6878 * @param {Number} newIndex
6880 "columnmoved" : true,
6882 * @event columlockchange
6883 * Fires when a column's locked state is changed
6884 * @param {ColumnModel} this
6885 * @param {Number} colIndex
6886 * @param {Boolean} locked true if locked
6888 "columnlockchange" : true
6890 Roo.grid.ColumnModel.superclass.constructor.call(this);
6892 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6894 * @cfg {String} header The header text to display in the Grid view.
6897 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6898 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6899 * specified, the column's index is used as an index into the Record's data Array.
6902 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6903 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6906 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6907 * Defaults to the value of the {@link #defaultSortable} property.
6908 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6911 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6914 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6917 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6920 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6923 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6924 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6925 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6926 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6929 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6932 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6935 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6938 * @cfg {String} cursor (Optional)
6941 * @cfg {String} tooltip (Optional)
6944 * @cfg {Number} xs (Optional)
6947 * @cfg {Number} sm (Optional)
6950 * @cfg {Number} md (Optional)
6953 * @cfg {Number} lg (Optional)
6956 * Returns the id of the column at the specified index.
6957 * @param {Number} index The column index
6958 * @return {String} the id
6960 getColumnId : function(index){
6961 return this.config[index].id;
6965 * Returns the column for a specified id.
6966 * @param {String} id The column id
6967 * @return {Object} the column
6969 getColumnById : function(id){
6970 return this.lookup[id];
6975 * Returns the column for a specified dataIndex.
6976 * @param {String} dataIndex The column dataIndex
6977 * @return {Object|Boolean} the column or false if not found
6979 getColumnByDataIndex: function(dataIndex){
6980 var index = this.findColumnIndex(dataIndex);
6981 return index > -1 ? this.config[index] : false;
6985 * Returns the index for a specified column id.
6986 * @param {String} id The column id
6987 * @return {Number} the index, or -1 if not found
6989 getIndexById : function(id){
6990 for(var i = 0, len = this.config.length; i < len; i++){
6991 if(this.config[i].id == id){
6999 * Returns the index for a specified column dataIndex.
7000 * @param {String} dataIndex The column dataIndex
7001 * @return {Number} the index, or -1 if not found
7004 findColumnIndex : function(dataIndex){
7005 for(var i = 0, len = this.config.length; i < len; i++){
7006 if(this.config[i].dataIndex == dataIndex){
7014 moveColumn : function(oldIndex, newIndex){
7015 var c = this.config[oldIndex];
7016 this.config.splice(oldIndex, 1);
7017 this.config.splice(newIndex, 0, c);
7018 this.dataMap = null;
7019 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7022 isLocked : function(colIndex){
7023 return this.config[colIndex].locked === true;
7026 setLocked : function(colIndex, value, suppressEvent){
7027 if(this.isLocked(colIndex) == value){
7030 this.config[colIndex].locked = value;
7032 this.fireEvent("columnlockchange", this, colIndex, value);
7036 getTotalLockedWidth : function(){
7038 for(var i = 0; i < this.config.length; i++){
7039 if(this.isLocked(i) && !this.isHidden(i)){
7040 this.totalWidth += this.getColumnWidth(i);
7046 getLockedCount : function(){
7047 for(var i = 0, len = this.config.length; i < len; i++){
7048 if(!this.isLocked(i)){
7053 return this.config.length;
7057 * Returns the number of columns.
7060 getColumnCount : function(visibleOnly){
7061 if(visibleOnly === true){
7063 for(var i = 0, len = this.config.length; i < len; i++){
7064 if(!this.isHidden(i)){
7070 return this.config.length;
7074 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7075 * @param {Function} fn
7076 * @param {Object} scope (optional)
7077 * @return {Array} result
7079 getColumnsBy : function(fn, scope){
7081 for(var i = 0, len = this.config.length; i < len; i++){
7082 var c = this.config[i];
7083 if(fn.call(scope||this, c, i) === true){
7091 * Returns true if the specified column is sortable.
7092 * @param {Number} col The column index
7095 isSortable : function(col){
7096 if(typeof this.config[col].sortable == "undefined"){
7097 return this.defaultSortable;
7099 return this.config[col].sortable;
7103 * Returns the rendering (formatting) function defined for the column.
7104 * @param {Number} col The column index.
7105 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7107 getRenderer : function(col){
7108 if(!this.config[col].renderer){
7109 return Roo.grid.ColumnModel.defaultRenderer;
7111 return this.config[col].renderer;
7115 * Sets the rendering (formatting) function for a column.
7116 * @param {Number} col The column index
7117 * @param {Function} fn The function to use to process the cell's raw data
7118 * to return HTML markup for the grid view. The render function is called with
7119 * the following parameters:<ul>
7120 * <li>Data value.</li>
7121 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7122 * <li>css A CSS style string to apply to the table cell.</li>
7123 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7124 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7125 * <li>Row index</li>
7126 * <li>Column index</li>
7127 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7129 setRenderer : function(col, fn){
7130 this.config[col].renderer = fn;
7134 * Returns the width for the specified column.
7135 * @param {Number} col The column index
7138 getColumnWidth : function(col){
7139 return this.config[col].width * 1 || this.defaultWidth;
7143 * Sets the width for a column.
7144 * @param {Number} col The column index
7145 * @param {Number} width The new width
7147 setColumnWidth : function(col, width, suppressEvent){
7148 this.config[col].width = width;
7149 this.totalWidth = null;
7151 this.fireEvent("widthchange", this, col, width);
7156 * Returns the total width of all columns.
7157 * @param {Boolean} includeHidden True to include hidden column widths
7160 getTotalWidth : function(includeHidden){
7161 if(!this.totalWidth){
7162 this.totalWidth = 0;
7163 for(var i = 0, len = this.config.length; i < len; i++){
7164 if(includeHidden || !this.isHidden(i)){
7165 this.totalWidth += this.getColumnWidth(i);
7169 return this.totalWidth;
7173 * Returns the header for the specified column.
7174 * @param {Number} col The column index
7177 getColumnHeader : function(col){
7178 return this.config[col].header;
7182 * Sets the header for a column.
7183 * @param {Number} col The column index
7184 * @param {String} header The new header
7186 setColumnHeader : function(col, header){
7187 this.config[col].header = header;
7188 this.fireEvent("headerchange", this, col, header);
7192 * Returns the tooltip for the specified column.
7193 * @param {Number} col The column index
7196 getColumnTooltip : function(col){
7197 return this.config[col].tooltip;
7200 * Sets the tooltip for a column.
7201 * @param {Number} col The column index
7202 * @param {String} tooltip The new tooltip
7204 setColumnTooltip : function(col, tooltip){
7205 this.config[col].tooltip = tooltip;
7209 * Returns the dataIndex for the specified column.
7210 * @param {Number} col The column index
7213 getDataIndex : function(col){
7214 return this.config[col].dataIndex;
7218 * Sets the dataIndex for a column.
7219 * @param {Number} col The column index
7220 * @param {Number} dataIndex The new dataIndex
7222 setDataIndex : function(col, dataIndex){
7223 this.config[col].dataIndex = dataIndex;
7229 * Returns true if the cell is editable.
7230 * @param {Number} colIndex The column index
7231 * @param {Number} rowIndex The row index - this is nto actually used..?
7234 isCellEditable : function(colIndex, rowIndex){
7235 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7239 * Returns the editor defined for the cell/column.
7240 * return false or null to disable editing.
7241 * @param {Number} colIndex The column index
7242 * @param {Number} rowIndex The row index
7245 getCellEditor : function(colIndex, rowIndex){
7246 return this.config[colIndex].editor;
7250 * Sets if a column is editable.
7251 * @param {Number} col The column index
7252 * @param {Boolean} editable True if the column is editable
7254 setEditable : function(col, editable){
7255 this.config[col].editable = editable;
7260 * Returns true if the column is hidden.
7261 * @param {Number} colIndex The column index
7264 isHidden : function(colIndex){
7265 return this.config[colIndex].hidden;
7270 * Returns true if the column width cannot be changed
7272 isFixed : function(colIndex){
7273 return this.config[colIndex].fixed;
7277 * Returns true if the column can be resized
7280 isResizable : function(colIndex){
7281 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7284 * Sets if a column is hidden.
7285 * @param {Number} colIndex The column index
7286 * @param {Boolean} hidden True if the column is hidden
7288 setHidden : function(colIndex, hidden){
7289 this.config[colIndex].hidden = hidden;
7290 this.totalWidth = null;
7291 this.fireEvent("hiddenchange", this, colIndex, hidden);
7295 * Sets the editor for a column.
7296 * @param {Number} col The column index
7297 * @param {Object} editor The editor object
7299 setEditor : function(col, editor){
7300 this.config[col].editor = editor;
7304 Roo.grid.ColumnModel.defaultRenderer = function(value)
7306 if(typeof value == "object") {
7309 if(typeof value == "string" && value.length < 1){
7313 return String.format("{0}", value);
7316 // Alias for backwards compatibility
7317 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7320 * Ext JS Library 1.1.1
7321 * Copyright(c) 2006-2007, Ext JS, LLC.
7323 * Originally Released Under LGPL - original licence link has changed is not relivant.
7326 * <script type="text/javascript">
7330 * @class Roo.LoadMask
7331 * A simple utility class for generically masking elements while loading data. If the element being masked has
7332 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7333 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7334 * element's UpdateManager load indicator and will be destroyed after the initial load.
7336 * Create a new LoadMask
7337 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7338 * @param {Object} config The config object
7340 Roo.LoadMask = function(el, config){
7341 this.el = Roo.get(el);
7342 Roo.apply(this, config);
7344 this.store.on('beforeload', this.onBeforeLoad, this);
7345 this.store.on('load', this.onLoad, this);
7346 this.store.on('loadexception', this.onLoadException, this);
7347 this.removeMask = false;
7349 var um = this.el.getUpdateManager();
7350 um.showLoadIndicator = false; // disable the default indicator
7351 um.on('beforeupdate', this.onBeforeLoad, this);
7352 um.on('update', this.onLoad, this);
7353 um.on('failure', this.onLoad, this);
7354 this.removeMask = true;
7358 Roo.LoadMask.prototype = {
7360 * @cfg {Boolean} removeMask
7361 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7362 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7366 * The text to display in a centered loading message box (defaults to 'Loading...')
7370 * @cfg {String} msgCls
7371 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7373 msgCls : 'x-mask-loading',
7376 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7382 * Disables the mask to prevent it from being displayed
7384 disable : function(){
7385 this.disabled = true;
7389 * Enables the mask so that it can be displayed
7391 enable : function(){
7392 this.disabled = false;
7395 onLoadException : function()
7399 if (typeof(arguments[3]) != 'undefined') {
7400 Roo.MessageBox.alert("Error loading",arguments[3]);
7404 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7405 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7412 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7417 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7421 onBeforeLoad : function(){
7423 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7428 destroy : function(){
7430 this.store.un('beforeload', this.onBeforeLoad, this);
7431 this.store.un('load', this.onLoad, this);
7432 this.store.un('loadexception', this.onLoadException, this);
7434 var um = this.el.getUpdateManager();
7435 um.un('beforeupdate', this.onBeforeLoad, this);
7436 um.un('update', this.onLoad, this);
7437 um.un('failure', this.onLoad, this);
7448 * @class Roo.bootstrap.Table
7449 * @extends Roo.bootstrap.Component
7450 * Bootstrap Table class
7451 * @cfg {String} cls table class
7452 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7453 * @cfg {String} bgcolor Specifies the background color for a table
7454 * @cfg {Number} border Specifies whether the table cells should have borders or not
7455 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7456 * @cfg {Number} cellspacing Specifies the space between cells
7457 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7458 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7459 * @cfg {String} sortable Specifies that the table should be sortable
7460 * @cfg {String} summary Specifies a summary of the content of a table
7461 * @cfg {Number} width Specifies the width of a table
7462 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7464 * @cfg {boolean} striped Should the rows be alternative striped
7465 * @cfg {boolean} bordered Add borders to the table
7466 * @cfg {boolean} hover Add hover highlighting
7467 * @cfg {boolean} condensed Format condensed
7468 * @cfg {boolean} responsive Format condensed
7469 * @cfg {Boolean} loadMask (true|false) default false
7470 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7471 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7472 * @cfg {Boolean} rowSelection (true|false) default false
7473 * @cfg {Boolean} cellSelection (true|false) default false
7474 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7475 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7476 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7477 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7481 * Create a new Table
7482 * @param {Object} config The config object
7485 Roo.bootstrap.Table = function(config){
7486 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7491 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7492 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7493 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7494 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7496 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7498 this.sm.grid = this;
7499 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7500 this.sm = this.selModel;
7501 this.sm.xmodule = this.xmodule || false;
7504 if (this.cm && typeof(this.cm.config) == 'undefined') {
7505 this.colModel = new Roo.grid.ColumnModel(this.cm);
7506 this.cm = this.colModel;
7507 this.cm.xmodule = this.xmodule || false;
7510 this.store= Roo.factory(this.store, Roo.data);
7511 this.ds = this.store;
7512 this.ds.xmodule = this.xmodule || false;
7515 if (this.footer && this.store) {
7516 this.footer.dataSource = this.ds;
7517 this.footer = Roo.factory(this.footer);
7524 * Fires when a cell is clicked
7525 * @param {Roo.bootstrap.Table} this
7526 * @param {Roo.Element} el
7527 * @param {Number} rowIndex
7528 * @param {Number} columnIndex
7529 * @param {Roo.EventObject} e
7533 * @event celldblclick
7534 * Fires when a cell is double clicked
7535 * @param {Roo.bootstrap.Table} this
7536 * @param {Roo.Element} el
7537 * @param {Number} rowIndex
7538 * @param {Number} columnIndex
7539 * @param {Roo.EventObject} e
7541 "celldblclick" : true,
7544 * Fires when a row is clicked
7545 * @param {Roo.bootstrap.Table} this
7546 * @param {Roo.Element} el
7547 * @param {Number} rowIndex
7548 * @param {Roo.EventObject} e
7552 * @event rowdblclick
7553 * Fires when a row is double clicked
7554 * @param {Roo.bootstrap.Table} this
7555 * @param {Roo.Element} el
7556 * @param {Number} rowIndex
7557 * @param {Roo.EventObject} e
7559 "rowdblclick" : true,
7562 * Fires when a mouseover occur
7563 * @param {Roo.bootstrap.Table} this
7564 * @param {Roo.Element} el
7565 * @param {Number} rowIndex
7566 * @param {Number} columnIndex
7567 * @param {Roo.EventObject} e
7572 * Fires when a mouseout occur
7573 * @param {Roo.bootstrap.Table} this
7574 * @param {Roo.Element} el
7575 * @param {Number} rowIndex
7576 * @param {Number} columnIndex
7577 * @param {Roo.EventObject} e
7582 * Fires when a row is rendered, so you can change add a style to it.
7583 * @param {Roo.bootstrap.Table} this
7584 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7588 * @event rowsrendered
7589 * Fires when all the rows have been rendered
7590 * @param {Roo.bootstrap.Table} this
7592 'rowsrendered' : true,
7594 * @event contextmenu
7595 * The raw contextmenu event for the entire grid.
7596 * @param {Roo.EventObject} e
7598 "contextmenu" : true,
7600 * @event rowcontextmenu
7601 * Fires when a row is right clicked
7602 * @param {Roo.bootstrap.Table} this
7603 * @param {Number} rowIndex
7604 * @param {Roo.EventObject} e
7606 "rowcontextmenu" : true,
7608 * @event cellcontextmenu
7609 * Fires when a cell is right clicked
7610 * @param {Roo.bootstrap.Table} this
7611 * @param {Number} rowIndex
7612 * @param {Number} cellIndex
7613 * @param {Roo.EventObject} e
7615 "cellcontextmenu" : true,
7617 * @event headercontextmenu
7618 * Fires when a header is right clicked
7619 * @param {Roo.bootstrap.Table} this
7620 * @param {Number} columnIndex
7621 * @param {Roo.EventObject} e
7623 "headercontextmenu" : true
7627 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7653 rowSelection : false,
7654 cellSelection : false,
7657 // Roo.Element - the tbody
7659 // Roo.Element - thead element
7662 container: false, // used by gridpanel...
7668 auto_hide_footer : false,
7670 getAutoCreate : function()
7672 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7679 if (this.scrollBody) {
7680 cfg.cls += ' table-body-fixed';
7683 cfg.cls += ' table-striped';
7687 cfg.cls += ' table-hover';
7689 if (this.bordered) {
7690 cfg.cls += ' table-bordered';
7692 if (this.condensed) {
7693 cfg.cls += ' table-condensed';
7695 if (this.responsive) {
7696 cfg.cls += ' table-responsive';
7700 cfg.cls+= ' ' +this.cls;
7703 // this lot should be simplifed...
7716 ].forEach(function(k) {
7724 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7727 if(this.store || this.cm){
7728 if(this.headerShow){
7729 cfg.cn.push(this.renderHeader());
7732 cfg.cn.push(this.renderBody());
7734 if(this.footerShow){
7735 cfg.cn.push(this.renderFooter());
7737 // where does this come from?
7738 //cfg.cls+= ' TableGrid';
7741 return { cn : [ cfg ] };
7744 initEvents : function()
7746 if(!this.store || !this.cm){
7749 if (this.selModel) {
7750 this.selModel.initEvents();
7754 //Roo.log('initEvents with ds!!!!');
7756 this.mainBody = this.el.select('tbody', true).first();
7757 this.mainHead = this.el.select('thead', true).first();
7758 this.mainFoot = this.el.select('tfoot', true).first();
7764 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7765 e.on('click', _this.sort, _this);
7768 this.mainBody.on("click", this.onClick, this);
7769 this.mainBody.on("dblclick", this.onDblClick, this);
7771 // why is this done????? = it breaks dialogs??
7772 //this.parent().el.setStyle('position', 'relative');
7776 this.footer.parentId = this.id;
7777 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7780 this.el.select('tfoot tr td').first().addClass('hide');
7785 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7788 this.store.on('load', this.onLoad, this);
7789 this.store.on('beforeload', this.onBeforeLoad, this);
7790 this.store.on('update', this.onUpdate, this);
7791 this.store.on('add', this.onAdd, this);
7792 this.store.on("clear", this.clear, this);
7794 this.el.on("contextmenu", this.onContextMenu, this);
7796 this.mainBody.on('scroll', this.onBodyScroll, this);
7798 this.cm.on("headerchange", this.onHeaderChange, this);
7800 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7804 onContextMenu : function(e, t)
7806 this.processEvent("contextmenu", e);
7809 processEvent : function(name, e)
7811 if (name != 'touchstart' ) {
7812 this.fireEvent(name, e);
7815 var t = e.getTarget();
7817 var cell = Roo.get(t);
7823 if(cell.findParent('tfoot', false, true)){
7827 if(cell.findParent('thead', false, true)){
7829 if(e.getTarget().nodeName.toLowerCase() != 'th'){
7830 cell = Roo.get(t).findParent('th', false, true);
7832 Roo.log("failed to find th in thead?");
7833 Roo.log(e.getTarget());
7838 var cellIndex = cell.dom.cellIndex;
7840 var ename = name == 'touchstart' ? 'click' : name;
7841 this.fireEvent("header" + ename, this, cellIndex, e);
7846 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7847 cell = Roo.get(t).findParent('td', false, true);
7849 Roo.log("failed to find th in tbody?");
7850 Roo.log(e.getTarget());
7855 var row = cell.findParent('tr', false, true);
7856 var cellIndex = cell.dom.cellIndex;
7857 var rowIndex = row.dom.rowIndex - 1;
7861 this.fireEvent("row" + name, this, rowIndex, e);
7865 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7871 onMouseover : function(e, el)
7873 var cell = Roo.get(el);
7879 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7880 cell = cell.findParent('td', false, true);
7883 var row = cell.findParent('tr', false, true);
7884 var cellIndex = cell.dom.cellIndex;
7885 var rowIndex = row.dom.rowIndex - 1; // start from 0
7887 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7891 onMouseout : function(e, el)
7893 var cell = Roo.get(el);
7899 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7900 cell = cell.findParent('td', false, true);
7903 var row = cell.findParent('tr', false, true);
7904 var cellIndex = cell.dom.cellIndex;
7905 var rowIndex = row.dom.rowIndex - 1; // start from 0
7907 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7911 onClick : function(e, el)
7913 var cell = Roo.get(el);
7915 if(!cell || (!this.cellSelection && !this.rowSelection)){
7919 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7920 cell = cell.findParent('td', false, true);
7923 if(!cell || typeof(cell) == 'undefined'){
7927 var row = cell.findParent('tr', false, true);
7929 if(!row || typeof(row) == 'undefined'){
7933 var cellIndex = cell.dom.cellIndex;
7934 var rowIndex = this.getRowIndex(row);
7936 // why??? - should these not be based on SelectionModel?
7937 if(this.cellSelection){
7938 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7941 if(this.rowSelection){
7942 this.fireEvent('rowclick', this, row, rowIndex, e);
7948 onDblClick : function(e,el)
7950 var cell = Roo.get(el);
7952 if(!cell || (!this.cellSelection && !this.rowSelection)){
7956 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7957 cell = cell.findParent('td', false, true);
7960 if(!cell || typeof(cell) == 'undefined'){
7964 var row = cell.findParent('tr', false, true);
7966 if(!row || typeof(row) == 'undefined'){
7970 var cellIndex = cell.dom.cellIndex;
7971 var rowIndex = this.getRowIndex(row);
7973 if(this.cellSelection){
7974 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7977 if(this.rowSelection){
7978 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7982 sort : function(e,el)
7984 var col = Roo.get(el);
7986 if(!col.hasClass('sortable')){
7990 var sort = col.attr('sort');
7993 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7997 this.store.sortInfo = {field : sort, direction : dir};
8000 Roo.log("calling footer first");
8001 this.footer.onClick('first');
8004 this.store.load({ params : { start : 0 } });
8008 renderHeader : function()
8016 this.totalWidth = 0;
8018 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8020 var config = cm.config[i];
8024 cls : 'x-hcol-' + i,
8026 html: cm.getColumnHeader(i)
8031 if(typeof(config.sortable) != 'undefined' && config.sortable){
8033 c.html = '<i class="glyphicon"></i>' + c.html;
8036 // could use BS4 hidden-..-down
8038 if(typeof(config.lgHeader) != 'undefined'){
8039 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8042 if(typeof(config.mdHeader) != 'undefined'){
8043 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8046 if(typeof(config.smHeader) != 'undefined'){
8047 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8050 if(typeof(config.xsHeader) != 'undefined'){
8051 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8058 if(typeof(config.tooltip) != 'undefined'){
8059 c.tooltip = config.tooltip;
8062 if(typeof(config.colspan) != 'undefined'){
8063 c.colspan = config.colspan;
8066 if(typeof(config.hidden) != 'undefined' && config.hidden){
8067 c.style += ' display:none;';
8070 if(typeof(config.dataIndex) != 'undefined'){
8071 c.sort = config.dataIndex;
8076 if(typeof(config.align) != 'undefined' && config.align.length){
8077 c.style += ' text-align:' + config.align + ';';
8080 if(typeof(config.width) != 'undefined'){
8081 c.style += ' width:' + config.width + 'px;';
8082 this.totalWidth += config.width;
8084 this.totalWidth += 100; // assume minimum of 100 per column?
8087 if(typeof(config.cls) != 'undefined'){
8088 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8091 ['xs','sm','md','lg'].map(function(size){
8093 if(typeof(config[size]) == 'undefined'){
8097 if (!config[size]) { // 0 = hidden
8098 // BS 4 '0' is treated as hide that column and below.
8099 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8103 c.cls += ' col-' + size + '-' + config[size] + (
8104 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8116 renderBody : function()
8126 colspan : this.cm.getColumnCount()
8136 renderFooter : function()
8146 colspan : this.cm.getColumnCount()
8160 // Roo.log('ds onload');
8165 var ds = this.store;
8167 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8168 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8169 if (_this.store.sortInfo) {
8171 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8172 e.select('i', true).addClass(['glyphicon-arrow-up']);
8175 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8176 e.select('i', true).addClass(['glyphicon-arrow-down']);
8181 var tbody = this.mainBody;
8183 if(ds.getCount() > 0){
8184 ds.data.each(function(d,rowIndex){
8185 var row = this.renderRow(cm, ds, rowIndex);
8187 tbody.createChild(row);
8191 if(row.cellObjects.length){
8192 Roo.each(row.cellObjects, function(r){
8193 _this.renderCellObject(r);
8200 var tfoot = this.el.select('tfoot', true).first();
8202 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8204 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8206 var total = this.ds.getTotalCount();
8208 if(this.footer.pageSize < total){
8209 this.mainFoot.show();
8213 Roo.each(this.el.select('tbody td', true).elements, function(e){
8214 e.on('mouseover', _this.onMouseover, _this);
8217 Roo.each(this.el.select('tbody td', true).elements, function(e){
8218 e.on('mouseout', _this.onMouseout, _this);
8220 this.fireEvent('rowsrendered', this);
8226 onUpdate : function(ds,record)
8228 this.refreshRow(record);
8232 onRemove : function(ds, record, index, isUpdate){
8233 if(isUpdate !== true){
8234 this.fireEvent("beforerowremoved", this, index, record);
8236 var bt = this.mainBody.dom;
8238 var rows = this.el.select('tbody > tr', true).elements;
8240 if(typeof(rows[index]) != 'undefined'){
8241 bt.removeChild(rows[index].dom);
8244 // if(bt.rows[index]){
8245 // bt.removeChild(bt.rows[index]);
8248 if(isUpdate !== true){
8249 //this.stripeRows(index);
8250 //this.syncRowHeights(index, index);
8252 this.fireEvent("rowremoved", this, index, record);
8256 onAdd : function(ds, records, rowIndex)
8258 //Roo.log('on Add called');
8259 // - note this does not handle multiple adding very well..
8260 var bt = this.mainBody.dom;
8261 for (var i =0 ; i < records.length;i++) {
8262 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8263 //Roo.log(records[i]);
8264 //Roo.log(this.store.getAt(rowIndex+i));
8265 this.insertRow(this.store, rowIndex + i, false);
8272 refreshRow : function(record){
8273 var ds = this.store, index;
8274 if(typeof record == 'number'){
8276 record = ds.getAt(index);
8278 index = ds.indexOf(record);
8280 return; // should not happen - but seems to
8283 this.insertRow(ds, index, true);
8285 this.onRemove(ds, record, index+1, true);
8287 //this.syncRowHeights(index, index);
8289 this.fireEvent("rowupdated", this, index, record);
8292 insertRow : function(dm, rowIndex, isUpdate){
8295 this.fireEvent("beforerowsinserted", this, rowIndex);
8297 //var s = this.getScrollState();
8298 var row = this.renderRow(this.cm, this.store, rowIndex);
8299 // insert before rowIndex..
8300 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8304 if(row.cellObjects.length){
8305 Roo.each(row.cellObjects, function(r){
8306 _this.renderCellObject(r);
8311 this.fireEvent("rowsinserted", this, rowIndex);
8312 //this.syncRowHeights(firstRow, lastRow);
8313 //this.stripeRows(firstRow);
8320 getRowDom : function(rowIndex)
8322 var rows = this.el.select('tbody > tr', true).elements;
8324 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8327 // returns the object tree for a tr..
8330 renderRow : function(cm, ds, rowIndex)
8332 var d = ds.getAt(rowIndex);
8336 cls : 'x-row-' + rowIndex,
8340 var cellObjects = [];
8342 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8343 var config = cm.config[i];
8345 var renderer = cm.getRenderer(i);
8349 if(typeof(renderer) !== 'undefined'){
8350 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8352 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8353 // and are rendered into the cells after the row is rendered - using the id for the element.
8355 if(typeof(value) === 'object'){
8365 rowIndex : rowIndex,
8370 this.fireEvent('rowclass', this, rowcfg);
8374 cls : rowcfg.rowClass + ' x-col-' + i,
8376 html: (typeof(value) === 'object') ? '' : value
8383 if(typeof(config.colspan) != 'undefined'){
8384 td.colspan = config.colspan;
8387 if(typeof(config.hidden) != 'undefined' && config.hidden){
8388 td.style += ' display:none;';
8391 if(typeof(config.align) != 'undefined' && config.align.length){
8392 td.style += ' text-align:' + config.align + ';';
8394 if(typeof(config.valign) != 'undefined' && config.valign.length){
8395 td.style += ' vertical-align:' + config.valign + ';';
8398 if(typeof(config.width) != 'undefined'){
8399 td.style += ' width:' + config.width + 'px;';
8402 if(typeof(config.cursor) != 'undefined'){
8403 td.style += ' cursor:' + config.cursor + ';';
8406 if(typeof(config.cls) != 'undefined'){
8407 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8410 ['xs','sm','md','lg'].map(function(size){
8412 if(typeof(config[size]) == 'undefined'){
8418 if (!config[size]) { // 0 = hidden
8419 // BS 4 '0' is treated as hide that column and below.
8420 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8424 td.cls += ' col-' + size + '-' + config[size] + (
8425 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8435 row.cellObjects = cellObjects;
8443 onBeforeLoad : function()
8452 this.el.select('tbody', true).first().dom.innerHTML = '';
8455 * Show or hide a row.
8456 * @param {Number} rowIndex to show or hide
8457 * @param {Boolean} state hide
8459 setRowVisibility : function(rowIndex, state)
8461 var bt = this.mainBody.dom;
8463 var rows = this.el.select('tbody > tr', true).elements;
8465 if(typeof(rows[rowIndex]) == 'undefined'){
8468 rows[rowIndex].dom.style.display = state ? '' : 'none';
8472 getSelectionModel : function(){
8474 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8476 return this.selModel;
8479 * Render the Roo.bootstrap object from renderder
8481 renderCellObject : function(r)
8485 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8487 var t = r.cfg.render(r.container);
8490 Roo.each(r.cfg.cn, function(c){
8492 container: t.getChildContainer(),
8495 _this.renderCellObject(child);
8500 getRowIndex : function(row)
8504 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8515 * Returns the grid's underlying element = used by panel.Grid
8516 * @return {Element} The element
8518 getGridEl : function(){
8522 * Forces a resize - used by panel.Grid
8523 * @return {Element} The element
8525 autoSize : function()
8527 //var ctr = Roo.get(this.container.dom.parentElement);
8528 var ctr = Roo.get(this.el.dom);
8530 var thd = this.getGridEl().select('thead',true).first();
8531 var tbd = this.getGridEl().select('tbody', true).first();
8532 var tfd = this.getGridEl().select('tfoot', true).first();
8534 var cw = ctr.getWidth();
8538 tbd.setWidth(ctr.getWidth());
8539 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8540 // this needs fixing for various usage - currently only hydra job advers I think..
8542 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8544 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8547 cw = Math.max(cw, this.totalWidth);
8548 this.getGridEl().select('tr',true).setWidth(cw);
8549 // resize 'expandable coloumn?
8551 return; // we doe not have a view in this design..
8554 onBodyScroll: function()
8556 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8558 this.mainHead.setStyle({
8559 'position' : 'relative',
8560 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8566 var scrollHeight = this.mainBody.dom.scrollHeight;
8568 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8570 var height = this.mainBody.getHeight();
8572 if(scrollHeight - height == scrollTop) {
8574 var total = this.ds.getTotalCount();
8576 if(this.footer.cursor + this.footer.pageSize < total){
8578 this.footer.ds.load({
8580 start : this.footer.cursor + this.footer.pageSize,
8581 limit : this.footer.pageSize
8591 onHeaderChange : function()
8593 var header = this.renderHeader();
8594 var table = this.el.select('table', true).first();
8596 this.mainHead.remove();
8597 this.mainHead = table.createChild(header, this.mainBody, false);
8600 onHiddenChange : function(colModel, colIndex, hidden)
8602 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8603 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8605 this.CSS.updateRule(thSelector, "display", "");
8606 this.CSS.updateRule(tdSelector, "display", "");
8609 this.CSS.updateRule(thSelector, "display", "none");
8610 this.CSS.updateRule(tdSelector, "display", "none");
8613 this.onHeaderChange();
8617 setColumnWidth: function(col_index, width)
8619 // width = "md-2 xs-2..."
8620 if(!this.colModel.config[col_index]) {
8624 var w = width.split(" ");
8626 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8628 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8631 for(var j = 0; j < w.length; j++) {
8637 var size_cls = w[j].split("-");
8639 if(!Number.isInteger(size_cls[1] * 1)) {
8643 if(!this.colModel.config[col_index][size_cls[0]]) {
8647 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8651 h_row[0].classList.replace(
8652 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8653 "col-"+size_cls[0]+"-"+size_cls[1]
8656 for(var i = 0; i < rows.length; i++) {
8658 var size_cls = w[j].split("-");
8660 if(!Number.isInteger(size_cls[1] * 1)) {
8664 if(!this.colModel.config[col_index][size_cls[0]]) {
8668 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8672 rows[i].classList.replace(
8673 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8674 "col-"+size_cls[0]+"-"+size_cls[1]
8678 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8693 * @class Roo.bootstrap.TableCell
8694 * @extends Roo.bootstrap.Component
8695 * Bootstrap TableCell class
8696 * @cfg {String} html cell contain text
8697 * @cfg {String} cls cell class
8698 * @cfg {String} tag cell tag (td|th) default td
8699 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8700 * @cfg {String} align Aligns the content in a cell
8701 * @cfg {String} axis Categorizes cells
8702 * @cfg {String} bgcolor Specifies the background color of a cell
8703 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8704 * @cfg {Number} colspan Specifies the number of columns a cell should span
8705 * @cfg {String} headers Specifies one or more header cells a cell is related to
8706 * @cfg {Number} height Sets the height of a cell
8707 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8708 * @cfg {Number} rowspan Sets the number of rows a cell should span
8709 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8710 * @cfg {String} valign Vertical aligns the content in a cell
8711 * @cfg {Number} width Specifies the width of a cell
8714 * Create a new TableCell
8715 * @param {Object} config The config object
8718 Roo.bootstrap.TableCell = function(config){
8719 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8722 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8742 getAutoCreate : function(){
8743 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8763 cfg.align=this.align
8769 cfg.bgcolor=this.bgcolor
8772 cfg.charoff=this.charoff
8775 cfg.colspan=this.colspan
8778 cfg.headers=this.headers
8781 cfg.height=this.height
8784 cfg.nowrap=this.nowrap
8787 cfg.rowspan=this.rowspan
8790 cfg.scope=this.scope
8793 cfg.valign=this.valign
8796 cfg.width=this.width
8815 * @class Roo.bootstrap.TableRow
8816 * @extends Roo.bootstrap.Component
8817 * Bootstrap TableRow class
8818 * @cfg {String} cls row class
8819 * @cfg {String} align Aligns the content in a table row
8820 * @cfg {String} bgcolor Specifies a background color for a table row
8821 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8822 * @cfg {String} valign Vertical aligns the content in a table row
8825 * Create a new TableRow
8826 * @param {Object} config The config object
8829 Roo.bootstrap.TableRow = function(config){
8830 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8833 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
8841 getAutoCreate : function(){
8842 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8852 cfg.align = this.align;
8855 cfg.bgcolor = this.bgcolor;
8858 cfg.charoff = this.charoff;
8861 cfg.valign = this.valign;
8879 * @class Roo.bootstrap.TableBody
8880 * @extends Roo.bootstrap.Component
8881 * Bootstrap TableBody class
8882 * @cfg {String} cls element class
8883 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8884 * @cfg {String} align Aligns the content inside the element
8885 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8886 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8889 * Create a new TableBody
8890 * @param {Object} config The config object
8893 Roo.bootstrap.TableBody = function(config){
8894 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8897 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8905 getAutoCreate : function(){
8906 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8920 cfg.align = this.align;
8923 cfg.charoff = this.charoff;
8926 cfg.valign = this.valign;
8933 // initEvents : function()
8940 // this.store = Roo.factory(this.store, Roo.data);
8941 // this.store.on('load', this.onLoad, this);
8943 // this.store.load();
8947 // onLoad: function ()
8949 // this.fireEvent('load', this);
8959 * Ext JS Library 1.1.1
8960 * Copyright(c) 2006-2007, Ext JS, LLC.
8962 * Originally Released Under LGPL - original licence link has changed is not relivant.
8965 * <script type="text/javascript">
8968 // as we use this in bootstrap.
8969 Roo.namespace('Roo.form');
8971 * @class Roo.form.Action
8972 * Internal Class used to handle form actions
8974 * @param {Roo.form.BasicForm} el The form element or its id
8975 * @param {Object} config Configuration options
8980 // define the action interface
8981 Roo.form.Action = function(form, options){
8983 this.options = options || {};
8986 * Client Validation Failed
8989 Roo.form.Action.CLIENT_INVALID = 'client';
8991 * Server Validation Failed
8994 Roo.form.Action.SERVER_INVALID = 'server';
8996 * Connect to Server Failed
8999 Roo.form.Action.CONNECT_FAILURE = 'connect';
9001 * Reading Data from Server Failed
9004 Roo.form.Action.LOAD_FAILURE = 'load';
9006 Roo.form.Action.prototype = {
9008 failureType : undefined,
9009 response : undefined,
9013 run : function(options){
9018 success : function(response){
9023 handleResponse : function(response){
9027 // default connection failure
9028 failure : function(response){
9030 this.response = response;
9031 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9032 this.form.afterAction(this, false);
9035 processResponse : function(response){
9036 this.response = response;
9037 if(!response.responseText){
9040 this.result = this.handleResponse(response);
9044 // utility functions used internally
9045 getUrl : function(appendParams){
9046 var url = this.options.url || this.form.url || this.form.el.dom.action;
9048 var p = this.getParams();
9050 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9056 getMethod : function(){
9057 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9060 getParams : function(){
9061 var bp = this.form.baseParams;
9062 var p = this.options.params;
9064 if(typeof p == "object"){
9065 p = Roo.urlEncode(Roo.applyIf(p, bp));
9066 }else if(typeof p == 'string' && bp){
9067 p += '&' + Roo.urlEncode(bp);
9070 p = Roo.urlEncode(bp);
9075 createCallback : function(){
9077 success: this.success,
9078 failure: this.failure,
9080 timeout: (this.form.timeout*1000),
9081 upload: this.form.fileUpload ? this.success : undefined
9086 Roo.form.Action.Submit = function(form, options){
9087 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9090 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9093 haveProgress : false,
9094 uploadComplete : false,
9096 // uploadProgress indicator.
9097 uploadProgress : function()
9099 if (!this.form.progressUrl) {
9103 if (!this.haveProgress) {
9104 Roo.MessageBox.progress("Uploading", "Uploading");
9106 if (this.uploadComplete) {
9107 Roo.MessageBox.hide();
9111 this.haveProgress = true;
9113 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9115 var c = new Roo.data.Connection();
9117 url : this.form.progressUrl,
9122 success : function(req){
9123 //console.log(data);
9127 rdata = Roo.decode(req.responseText)
9129 Roo.log("Invalid data from server..");
9133 if (!rdata || !rdata.success) {
9135 Roo.MessageBox.alert(Roo.encode(rdata));
9138 var data = rdata.data;
9140 if (this.uploadComplete) {
9141 Roo.MessageBox.hide();
9146 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9147 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9150 this.uploadProgress.defer(2000,this);
9153 failure: function(data) {
9154 Roo.log('progress url failed ');
9165 // run get Values on the form, so it syncs any secondary forms.
9166 this.form.getValues();
9168 var o = this.options;
9169 var method = this.getMethod();
9170 var isPost = method == 'POST';
9171 if(o.clientValidation === false || this.form.isValid()){
9173 if (this.form.progressUrl) {
9174 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9175 (new Date() * 1) + '' + Math.random());
9180 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9181 form:this.form.el.dom,
9182 url:this.getUrl(!isPost),
9184 params:isPost ? this.getParams() : null,
9185 isUpload: this.form.fileUpload,
9186 formData : this.form.formData
9189 this.uploadProgress();
9191 }else if (o.clientValidation !== false){ // client validation failed
9192 this.failureType = Roo.form.Action.CLIENT_INVALID;
9193 this.form.afterAction(this, false);
9197 success : function(response)
9199 this.uploadComplete= true;
9200 if (this.haveProgress) {
9201 Roo.MessageBox.hide();
9205 var result = this.processResponse(response);
9206 if(result === true || result.success){
9207 this.form.afterAction(this, true);
9211 this.form.markInvalid(result.errors);
9212 this.failureType = Roo.form.Action.SERVER_INVALID;
9214 this.form.afterAction(this, false);
9216 failure : function(response)
9218 this.uploadComplete= true;
9219 if (this.haveProgress) {
9220 Roo.MessageBox.hide();
9223 this.response = response;
9224 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9225 this.form.afterAction(this, false);
9228 handleResponse : function(response){
9229 if(this.form.errorReader){
9230 var rs = this.form.errorReader.read(response);
9233 for(var i = 0, len = rs.records.length; i < len; i++) {
9234 var r = rs.records[i];
9238 if(errors.length < 1){
9242 success : rs.success,
9248 ret = Roo.decode(response.responseText);
9252 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9262 Roo.form.Action.Load = function(form, options){
9263 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9264 this.reader = this.form.reader;
9267 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9272 Roo.Ajax.request(Roo.apply(
9273 this.createCallback(), {
9274 method:this.getMethod(),
9275 url:this.getUrl(false),
9276 params:this.getParams()
9280 success : function(response){
9282 var result = this.processResponse(response);
9283 if(result === true || !result.success || !result.data){
9284 this.failureType = Roo.form.Action.LOAD_FAILURE;
9285 this.form.afterAction(this, false);
9288 this.form.clearInvalid();
9289 this.form.setValues(result.data);
9290 this.form.afterAction(this, true);
9293 handleResponse : function(response){
9294 if(this.form.reader){
9295 var rs = this.form.reader.read(response);
9296 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9298 success : rs.success,
9302 return Roo.decode(response.responseText);
9306 Roo.form.Action.ACTION_TYPES = {
9307 'load' : Roo.form.Action.Load,
9308 'submit' : Roo.form.Action.Submit
9317 * @class Roo.bootstrap.Form
9318 * @extends Roo.bootstrap.Component
9319 * Bootstrap Form class
9320 * @cfg {String} method GET | POST (default POST)
9321 * @cfg {String} labelAlign top | left (default top)
9322 * @cfg {String} align left | right - for navbars
9323 * @cfg {Boolean} loadMask load mask when submit (default true)
9328 * @param {Object} config The config object
9332 Roo.bootstrap.Form = function(config){
9334 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9336 Roo.bootstrap.Form.popover.apply();
9340 * @event clientvalidation
9341 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9342 * @param {Form} this
9343 * @param {Boolean} valid true if the form has passed client-side validation
9345 clientvalidation: true,
9347 * @event beforeaction
9348 * Fires before any action is performed. Return false to cancel the action.
9349 * @param {Form} this
9350 * @param {Action} action The action to be performed
9354 * @event actionfailed
9355 * Fires when an action fails.
9356 * @param {Form} this
9357 * @param {Action} action The action that failed
9359 actionfailed : true,
9361 * @event actioncomplete
9362 * Fires when an action is completed.
9363 * @param {Form} this
9364 * @param {Action} action The action that completed
9366 actioncomplete : true
9370 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9373 * @cfg {String} method
9374 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9379 * The URL to use for form actions if one isn't supplied in the action options.
9382 * @cfg {Boolean} fileUpload
9383 * Set to true if this form is a file upload.
9387 * @cfg {Object} baseParams
9388 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9392 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9396 * @cfg {Sting} align (left|right) for navbar forms
9401 activeAction : null,
9404 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9405 * element by passing it or its id or mask the form itself by passing in true.
9408 waitMsgTarget : false,
9413 * @cfg {Boolean} errorMask (true|false) default false
9418 * @cfg {Number} maskOffset Default 100
9423 * @cfg {Boolean} maskBody
9427 getAutoCreate : function(){
9431 method : this.method || 'POST',
9432 id : this.id || Roo.id(),
9435 if (this.parent().xtype.match(/^Nav/)) {
9436 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9440 if (this.labelAlign == 'left' ) {
9441 cfg.cls += ' form-horizontal';
9447 initEvents : function()
9449 this.el.on('submit', this.onSubmit, this);
9450 // this was added as random key presses on the form where triggering form submit.
9451 this.el.on('keypress', function(e) {
9452 if (e.getCharCode() != 13) {
9455 // we might need to allow it for textareas.. and some other items.
9456 // check e.getTarget().
9458 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9462 Roo.log("keypress blocked");
9470 onSubmit : function(e){
9475 * Returns true if client-side validation on the form is successful.
9478 isValid : function(){
9479 var items = this.getItems();
9483 items.each(function(f){
9489 Roo.log('invalid field: ' + f.name);
9493 if(!target && f.el.isVisible(true)){
9499 if(this.errorMask && !valid){
9500 Roo.bootstrap.Form.popover.mask(this, target);
9507 * Returns true if any fields in this form have changed since their original load.
9510 isDirty : function(){
9512 var items = this.getItems();
9513 items.each(function(f){
9523 * Performs a predefined action (submit or load) or custom actions you define on this form.
9524 * @param {String} actionName The name of the action type
9525 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9526 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9527 * accept other config options):
9529 Property Type Description
9530 ---------------- --------------- ----------------------------------------------------------------------------------
9531 url String The url for the action (defaults to the form's url)
9532 method String The form method to use (defaults to the form's method, or POST if not defined)
9533 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9534 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9535 validate the form on the client (defaults to false)
9537 * @return {BasicForm} this
9539 doAction : function(action, options){
9540 if(typeof action == 'string'){
9541 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9543 if(this.fireEvent('beforeaction', this, action) !== false){
9544 this.beforeAction(action);
9545 action.run.defer(100, action);
9551 beforeAction : function(action){
9552 var o = action.options;
9557 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9559 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9562 // not really supported yet.. ??
9564 //if(this.waitMsgTarget === true){
9565 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9566 //}else if(this.waitMsgTarget){
9567 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9568 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9570 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9576 afterAction : function(action, success){
9577 this.activeAction = null;
9578 var o = action.options;
9583 Roo.get(document.body).unmask();
9589 //if(this.waitMsgTarget === true){
9590 // this.el.unmask();
9591 //}else if(this.waitMsgTarget){
9592 // this.waitMsgTarget.unmask();
9594 // Roo.MessageBox.updateProgress(1);
9595 // Roo.MessageBox.hide();
9602 Roo.callback(o.success, o.scope, [this, action]);
9603 this.fireEvent('actioncomplete', this, action);
9607 // failure condition..
9608 // we have a scenario where updates need confirming.
9609 // eg. if a locking scenario exists..
9610 // we look for { errors : { needs_confirm : true }} in the response.
9612 (typeof(action.result) != 'undefined') &&
9613 (typeof(action.result.errors) != 'undefined') &&
9614 (typeof(action.result.errors.needs_confirm) != 'undefined')
9617 Roo.log("not supported yet");
9620 Roo.MessageBox.confirm(
9621 "Change requires confirmation",
9622 action.result.errorMsg,
9627 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9637 Roo.callback(o.failure, o.scope, [this, action]);
9638 // show an error message if no failed handler is set..
9639 if (!this.hasListener('actionfailed')) {
9640 Roo.log("need to add dialog support");
9642 Roo.MessageBox.alert("Error",
9643 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9644 action.result.errorMsg :
9645 "Saving Failed, please check your entries or try again"
9650 this.fireEvent('actionfailed', this, action);
9655 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9656 * @param {String} id The value to search for
9659 findField : function(id){
9660 var items = this.getItems();
9661 var field = items.get(id);
9663 items.each(function(f){
9664 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9671 return field || null;
9674 * Mark fields in this form invalid in bulk.
9675 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9676 * @return {BasicForm} this
9678 markInvalid : function(errors){
9679 if(errors instanceof Array){
9680 for(var i = 0, len = errors.length; i < len; i++){
9681 var fieldError = errors[i];
9682 var f = this.findField(fieldError.id);
9684 f.markInvalid(fieldError.msg);
9690 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9691 field.markInvalid(errors[id]);
9695 //Roo.each(this.childForms || [], function (f) {
9696 // f.markInvalid(errors);
9703 * Set values for fields in this form in bulk.
9704 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9705 * @return {BasicForm} this
9707 setValues : function(values){
9708 if(values instanceof Array){ // array of objects
9709 for(var i = 0, len = values.length; i < len; i++){
9711 var f = this.findField(v.id);
9713 f.setValue(v.value);
9714 if(this.trackResetOnLoad){
9715 f.originalValue = f.getValue();
9719 }else{ // object hash
9722 if(typeof values[id] != 'function' && (field = this.findField(id))){
9724 if (field.setFromData &&
9726 field.displayField &&
9727 // combos' with local stores can
9728 // be queried via setValue()
9729 // to set their value..
9730 (field.store && !field.store.isLocal)
9734 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9735 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9736 field.setFromData(sd);
9738 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9740 field.setFromData(values);
9743 field.setValue(values[id]);
9747 if(this.trackResetOnLoad){
9748 field.originalValue = field.getValue();
9754 //Roo.each(this.childForms || [], function (f) {
9755 // f.setValues(values);
9762 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9763 * they are returned as an array.
9764 * @param {Boolean} asString
9767 getValues : function(asString){
9768 //if (this.childForms) {
9769 // copy values from the child forms
9770 // Roo.each(this.childForms, function (f) {
9771 // this.setValues(f.getValues());
9777 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9778 if(asString === true){
9781 return Roo.urlDecode(fs);
9785 * Returns the fields in this form as an object with key/value pairs.
9786 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9789 getFieldValues : function(with_hidden)
9791 var items = this.getItems();
9793 items.each(function(f){
9799 var v = f.getValue();
9801 if (f.inputType =='radio') {
9802 if (typeof(ret[f.getName()]) == 'undefined') {
9803 ret[f.getName()] = ''; // empty..
9806 if (!f.el.dom.checked) {
9814 if(f.xtype == 'MoneyField'){
9815 ret[f.currencyName] = f.getCurrency();
9818 // not sure if this supported any more..
9819 if ((typeof(v) == 'object') && f.getRawValue) {
9820 v = f.getRawValue() ; // dates..
9822 // combo boxes where name != hiddenName...
9823 if (f.name !== false && f.name != '' && f.name != f.getName()) {
9824 ret[f.name] = f.getRawValue();
9826 ret[f.getName()] = v;
9833 * Clears all invalid messages in this form.
9834 * @return {BasicForm} this
9836 clearInvalid : function(){
9837 var items = this.getItems();
9839 items.each(function(f){
9848 * @return {BasicForm} this
9851 var items = this.getItems();
9852 items.each(function(f){
9856 Roo.each(this.childForms || [], function (f) {
9864 getItems : function()
9866 var r=new Roo.util.MixedCollection(false, function(o){
9867 return o.id || (o.id = Roo.id());
9869 var iter = function(el) {
9876 Roo.each(el.items,function(e) {
9885 hideFields : function(items)
9887 Roo.each(items, function(i){
9889 var f = this.findField(i);
9900 showFields : function(items)
9902 Roo.each(items, function(i){
9904 var f = this.findField(i);
9917 Roo.apply(Roo.bootstrap.Form, {
9944 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9945 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9946 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9947 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9950 this.maskEl.top.enableDisplayMode("block");
9951 this.maskEl.left.enableDisplayMode("block");
9952 this.maskEl.bottom.enableDisplayMode("block");
9953 this.maskEl.right.enableDisplayMode("block");
9955 this.toolTip = new Roo.bootstrap.Tooltip({
9956 cls : 'roo-form-error-popover',
9958 'left' : ['r-l', [-2,0], 'right'],
9959 'right' : ['l-r', [2,0], 'left'],
9960 'bottom' : ['tl-bl', [0,2], 'top'],
9961 'top' : [ 'bl-tl', [0,-2], 'bottom']
9965 this.toolTip.render(Roo.get(document.body));
9967 this.toolTip.el.enableDisplayMode("block");
9969 Roo.get(document.body).on('click', function(){
9973 Roo.get(document.body).on('touchstart', function(){
9977 this.isApplied = true
9980 mask : function(form, target)
9984 this.target = target;
9986 if(!this.form.errorMask || !target.el){
9990 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9992 Roo.log(scrollable);
9994 var ot = this.target.el.calcOffsetsTo(scrollable);
9996 var scrollTo = ot[1] - this.form.maskOffset;
9998 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10000 scrollable.scrollTo('top', scrollTo);
10002 var box = this.target.el.getBox();
10004 var zIndex = Roo.bootstrap.Modal.zIndex++;
10007 this.maskEl.top.setStyle('position', 'absolute');
10008 this.maskEl.top.setStyle('z-index', zIndex);
10009 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10010 this.maskEl.top.setLeft(0);
10011 this.maskEl.top.setTop(0);
10012 this.maskEl.top.show();
10014 this.maskEl.left.setStyle('position', 'absolute');
10015 this.maskEl.left.setStyle('z-index', zIndex);
10016 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10017 this.maskEl.left.setLeft(0);
10018 this.maskEl.left.setTop(box.y - this.padding);
10019 this.maskEl.left.show();
10021 this.maskEl.bottom.setStyle('position', 'absolute');
10022 this.maskEl.bottom.setStyle('z-index', zIndex);
10023 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10024 this.maskEl.bottom.setLeft(0);
10025 this.maskEl.bottom.setTop(box.bottom + this.padding);
10026 this.maskEl.bottom.show();
10028 this.maskEl.right.setStyle('position', 'absolute');
10029 this.maskEl.right.setStyle('z-index', zIndex);
10030 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10031 this.maskEl.right.setLeft(box.right + this.padding);
10032 this.maskEl.right.setTop(box.y - this.padding);
10033 this.maskEl.right.show();
10035 this.toolTip.bindEl = this.target.el;
10037 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10039 var tip = this.target.blankText;
10041 if(this.target.getValue() !== '' ) {
10043 if (this.target.invalidText.length) {
10044 tip = this.target.invalidText;
10045 } else if (this.target.regexText.length){
10046 tip = this.target.regexText;
10050 this.toolTip.show(tip);
10052 this.intervalID = window.setInterval(function() {
10053 Roo.bootstrap.Form.popover.unmask();
10056 window.onwheel = function(){ return false;};
10058 (function(){ this.isMasked = true; }).defer(500, this);
10062 unmask : function()
10064 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10068 this.maskEl.top.setStyle('position', 'absolute');
10069 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10070 this.maskEl.top.hide();
10072 this.maskEl.left.setStyle('position', 'absolute');
10073 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10074 this.maskEl.left.hide();
10076 this.maskEl.bottom.setStyle('position', 'absolute');
10077 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10078 this.maskEl.bottom.hide();
10080 this.maskEl.right.setStyle('position', 'absolute');
10081 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10082 this.maskEl.right.hide();
10084 this.toolTip.hide();
10086 this.toolTip.el.hide();
10088 window.onwheel = function(){ return true;};
10090 if(this.intervalID){
10091 window.clearInterval(this.intervalID);
10092 this.intervalID = false;
10095 this.isMasked = false;
10105 * Ext JS Library 1.1.1
10106 * Copyright(c) 2006-2007, Ext JS, LLC.
10108 * Originally Released Under LGPL - original licence link has changed is not relivant.
10111 * <script type="text/javascript">
10114 * @class Roo.form.VTypes
10115 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10118 Roo.form.VTypes = function(){
10119 // closure these in so they are only created once.
10120 var alpha = /^[a-zA-Z_]+$/;
10121 var alphanum = /^[a-zA-Z0-9_]+$/;
10122 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10123 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10125 // All these messages and functions are configurable
10128 * The function used to validate email addresses
10129 * @param {String} value The email address
10131 'email' : function(v){
10132 return email.test(v);
10135 * The error text to display when the email validation function returns false
10138 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10140 * The keystroke filter mask to be applied on email input
10143 'emailMask' : /[a-z0-9_\.\-@]/i,
10146 * The function used to validate URLs
10147 * @param {String} value The URL
10149 'url' : function(v){
10150 return url.test(v);
10153 * The error text to display when the url validation function returns false
10156 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10159 * The function used to validate alpha values
10160 * @param {String} value The value
10162 'alpha' : function(v){
10163 return alpha.test(v);
10166 * The error text to display when the alpha validation function returns false
10169 'alphaText' : 'This field should only contain letters and _',
10171 * The keystroke filter mask to be applied on alpha input
10174 'alphaMask' : /[a-z_]/i,
10177 * The function used to validate alphanumeric values
10178 * @param {String} value The value
10180 'alphanum' : function(v){
10181 return alphanum.test(v);
10184 * The error text to display when the alphanumeric validation function returns false
10187 'alphanumText' : 'This field should only contain letters, numbers and _',
10189 * The keystroke filter mask to be applied on alphanumeric input
10192 'alphanumMask' : /[a-z0-9_]/i
10202 * @class Roo.bootstrap.Input
10203 * @extends Roo.bootstrap.Component
10204 * Bootstrap Input class
10205 * @cfg {Boolean} disabled is it disabled
10206 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10207 * @cfg {String} name name of the input
10208 * @cfg {string} fieldLabel - the label associated
10209 * @cfg {string} placeholder - placeholder to put in text.
10210 * @cfg {string} before - input group add on before
10211 * @cfg {string} after - input group add on after
10212 * @cfg {string} size - (lg|sm) or leave empty..
10213 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10214 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10215 * @cfg {Number} md colspan out of 12 for computer-sized screens
10216 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10217 * @cfg {string} value default value of the input
10218 * @cfg {Number} labelWidth set the width of label
10219 * @cfg {Number} labellg set the width of label (1-12)
10220 * @cfg {Number} labelmd set the width of label (1-12)
10221 * @cfg {Number} labelsm set the width of label (1-12)
10222 * @cfg {Number} labelxs set the width of label (1-12)
10223 * @cfg {String} labelAlign (top|left)
10224 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10225 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10226 * @cfg {String} indicatorpos (left|right) default left
10227 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10228 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10229 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10231 * @cfg {String} align (left|center|right) Default left
10232 * @cfg {Boolean} forceFeedback (true|false) Default false
10235 * Create a new Input
10236 * @param {Object} config The config object
10239 Roo.bootstrap.Input = function(config){
10241 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10246 * Fires when this field receives input focus.
10247 * @param {Roo.form.Field} this
10252 * Fires when this field loses input focus.
10253 * @param {Roo.form.Field} this
10257 * @event specialkey
10258 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10259 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10260 * @param {Roo.form.Field} this
10261 * @param {Roo.EventObject} e The event object
10266 * Fires just before the field blurs if the field value has changed.
10267 * @param {Roo.form.Field} this
10268 * @param {Mixed} newValue The new value
10269 * @param {Mixed} oldValue The original value
10274 * Fires after the field has been marked as invalid.
10275 * @param {Roo.form.Field} this
10276 * @param {String} msg The validation message
10281 * Fires after the field has been validated with no errors.
10282 * @param {Roo.form.Field} this
10287 * Fires after the key up
10288 * @param {Roo.form.Field} this
10289 * @param {Roo.EventObject} e The event Object
10295 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10297 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10298 automatic validation (defaults to "keyup").
10300 validationEvent : "keyup",
10302 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10304 validateOnBlur : true,
10306 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10308 validationDelay : 250,
10310 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10312 focusClass : "x-form-focus", // not needed???
10316 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10318 invalidClass : "has-warning",
10321 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10323 validClass : "has-success",
10326 * @cfg {Boolean} hasFeedback (true|false) default true
10328 hasFeedback : true,
10331 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10333 invalidFeedbackClass : "glyphicon-warning-sign",
10336 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10338 validFeedbackClass : "glyphicon-ok",
10341 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10343 selectOnFocus : false,
10346 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10350 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10355 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10357 disableKeyFilter : false,
10360 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10364 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10368 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10370 blankText : "Please complete this mandatory field",
10373 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10377 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10379 maxLength : Number.MAX_VALUE,
10381 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10383 minLengthText : "The minimum length for this field is {0}",
10385 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10387 maxLengthText : "The maximum length for this field is {0}",
10391 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10392 * If available, this function will be called only after the basic validators all return true, and will be passed the
10393 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10397 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10398 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10399 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10403 * @cfg {String} regexText -- Depricated - use Invalid Text
10408 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10414 autocomplete: false,
10418 inputType : 'text',
10421 placeholder: false,
10426 preventMark: false,
10427 isFormField : true,
10430 labelAlign : false,
10433 formatedValue : false,
10434 forceFeedback : false,
10436 indicatorpos : 'left',
10446 parentLabelAlign : function()
10449 while (parent.parent()) {
10450 parent = parent.parent();
10451 if (typeof(parent.labelAlign) !='undefined') {
10452 return parent.labelAlign;
10459 getAutoCreate : function()
10461 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10467 if(this.inputType != 'hidden'){
10468 cfg.cls = 'form-group' //input-group
10474 type : this.inputType,
10475 value : this.value,
10476 cls : 'form-control',
10477 placeholder : this.placeholder || '',
10478 autocomplete : this.autocomplete || 'new-password'
10481 if(this.capture.length){
10482 input.capture = this.capture;
10485 if(this.accept.length){
10486 input.accept = this.accept + "/*";
10490 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10493 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10494 input.maxLength = this.maxLength;
10497 if (this.disabled) {
10498 input.disabled=true;
10501 if (this.readOnly) {
10502 input.readonly=true;
10506 input.name = this.name;
10510 input.cls += ' input-' + this.size;
10514 ['xs','sm','md','lg'].map(function(size){
10515 if (settings[size]) {
10516 cfg.cls += ' col-' + size + '-' + settings[size];
10520 var inputblock = input;
10524 cls: 'glyphicon form-control-feedback'
10527 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10530 cls : 'has-feedback',
10538 if (this.before || this.after) {
10541 cls : 'input-group',
10545 if (this.before && typeof(this.before) == 'string') {
10547 inputblock.cn.push({
10549 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10553 if (this.before && typeof(this.before) == 'object') {
10554 this.before = Roo.factory(this.before);
10556 inputblock.cn.push({
10558 cls : 'roo-input-before input-group-prepend input-group-' +
10559 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10563 inputblock.cn.push(input);
10565 if (this.after && typeof(this.after) == 'string') {
10566 inputblock.cn.push({
10568 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10572 if (this.after && typeof(this.after) == 'object') {
10573 this.after = Roo.factory(this.after);
10575 inputblock.cn.push({
10577 cls : 'roo-input-after input-group-append input-group-' +
10578 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10582 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10583 inputblock.cls += ' has-feedback';
10584 inputblock.cn.push(feedback);
10589 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10590 tooltip : 'This field is required'
10592 if (Roo.bootstrap.version == 4) {
10595 style : 'display-none'
10598 if (align ==='left' && this.fieldLabel.length) {
10600 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10607 cls : 'control-label col-form-label',
10608 html : this.fieldLabel
10619 var labelCfg = cfg.cn[1];
10620 var contentCfg = cfg.cn[2];
10622 if(this.indicatorpos == 'right'){
10627 cls : 'control-label col-form-label',
10631 html : this.fieldLabel
10645 labelCfg = cfg.cn[0];
10646 contentCfg = cfg.cn[1];
10650 if(this.labelWidth > 12){
10651 labelCfg.style = "width: " + this.labelWidth + 'px';
10654 if(this.labelWidth < 13 && this.labelmd == 0){
10655 this.labelmd = this.labelWidth;
10658 if(this.labellg > 0){
10659 labelCfg.cls += ' col-lg-' + this.labellg;
10660 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10663 if(this.labelmd > 0){
10664 labelCfg.cls += ' col-md-' + this.labelmd;
10665 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10668 if(this.labelsm > 0){
10669 labelCfg.cls += ' col-sm-' + this.labelsm;
10670 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10673 if(this.labelxs > 0){
10674 labelCfg.cls += ' col-xs-' + this.labelxs;
10675 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10679 } else if ( this.fieldLabel.length) {
10684 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10685 tooltip : 'This field is required'
10689 //cls : 'input-group-addon',
10690 html : this.fieldLabel
10698 if(this.indicatorpos == 'right'){
10703 //cls : 'input-group-addon',
10704 html : this.fieldLabel
10709 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10710 tooltip : 'This field is required'
10730 if (this.parentType === 'Navbar' && this.parent().bar) {
10731 cfg.cls += ' navbar-form';
10734 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10735 // on BS4 we do this only if not form
10736 cfg.cls += ' navbar-form';
10744 * return the real input element.
10746 inputEl: function ()
10748 return this.el.select('input.form-control',true).first();
10751 tooltipEl : function()
10753 return this.inputEl();
10756 indicatorEl : function()
10758 if (Roo.bootstrap.version == 4) {
10759 return false; // not enabled in v4 yet.
10762 var indicator = this.el.select('i.roo-required-indicator',true).first();
10772 setDisabled : function(v)
10774 var i = this.inputEl().dom;
10776 i.removeAttribute('disabled');
10780 i.setAttribute('disabled','true');
10782 initEvents : function()
10785 this.inputEl().on("keydown" , this.fireKey, this);
10786 this.inputEl().on("focus", this.onFocus, this);
10787 this.inputEl().on("blur", this.onBlur, this);
10789 this.inputEl().relayEvent('keyup', this);
10791 this.indicator = this.indicatorEl();
10793 if(this.indicator){
10794 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10797 // reference to original value for reset
10798 this.originalValue = this.getValue();
10799 //Roo.form.TextField.superclass.initEvents.call(this);
10800 if(this.validationEvent == 'keyup'){
10801 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10802 this.inputEl().on('keyup', this.filterValidation, this);
10804 else if(this.validationEvent !== false){
10805 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10808 if(this.selectOnFocus){
10809 this.on("focus", this.preFocus, this);
10812 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10813 this.inputEl().on("keypress", this.filterKeys, this);
10815 this.inputEl().relayEvent('keypress', this);
10818 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
10819 this.el.on("click", this.autoSize, this);
10822 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10823 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10826 if (typeof(this.before) == 'object') {
10827 this.before.render(this.el.select('.roo-input-before',true).first());
10829 if (typeof(this.after) == 'object') {
10830 this.after.render(this.el.select('.roo-input-after',true).first());
10833 this.inputEl().on('change', this.onChange, this);
10836 filterValidation : function(e){
10837 if(!e.isNavKeyPress()){
10838 this.validationTask.delay(this.validationDelay);
10842 * Validates the field value
10843 * @return {Boolean} True if the value is valid, else false
10845 validate : function(){
10846 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10847 if(this.disabled || this.validateValue(this.getRawValue())){
10852 this.markInvalid();
10858 * Validates a value according to the field's validation rules and marks the field as invalid
10859 * if the validation fails
10860 * @param {Mixed} value The value to validate
10861 * @return {Boolean} True if the value is valid, else false
10863 validateValue : function(value)
10865 if(this.getVisibilityEl().hasClass('hidden')){
10869 if(value.length < 1) { // if it's blank
10870 if(this.allowBlank){
10876 if(value.length < this.minLength){
10879 if(value.length > this.maxLength){
10883 var vt = Roo.form.VTypes;
10884 if(!vt[this.vtype](value, this)){
10888 if(typeof this.validator == "function"){
10889 var msg = this.validator(value);
10893 if (typeof(msg) == 'string') {
10894 this.invalidText = msg;
10898 if(this.regex && !this.regex.test(value)){
10906 fireKey : function(e){
10907 //Roo.log('field ' + e.getKey());
10908 if(e.isNavKeyPress()){
10909 this.fireEvent("specialkey", this, e);
10912 focus : function (selectText){
10914 this.inputEl().focus();
10915 if(selectText === true){
10916 this.inputEl().dom.select();
10922 onFocus : function(){
10923 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10924 // this.el.addClass(this.focusClass);
10926 if(!this.hasFocus){
10927 this.hasFocus = true;
10928 this.startValue = this.getValue();
10929 this.fireEvent("focus", this);
10933 beforeBlur : Roo.emptyFn,
10937 onBlur : function(){
10939 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10940 //this.el.removeClass(this.focusClass);
10942 this.hasFocus = false;
10943 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10946 var v = this.getValue();
10947 if(String(v) !== String(this.startValue)){
10948 this.fireEvent('change', this, v, this.startValue);
10950 this.fireEvent("blur", this);
10953 onChange : function(e)
10955 var v = this.getValue();
10956 if(String(v) !== String(this.startValue)){
10957 this.fireEvent('change', this, v, this.startValue);
10963 * Resets the current field value to the originally loaded value and clears any validation messages
10965 reset : function(){
10966 this.setValue(this.originalValue);
10970 * Returns the name of the field
10971 * @return {Mixed} name The name field
10973 getName: function(){
10977 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10978 * @return {Mixed} value The field value
10980 getValue : function(){
10982 var v = this.inputEl().getValue();
10987 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10988 * @return {Mixed} value The field value
10990 getRawValue : function(){
10991 var v = this.inputEl().getValue();
10997 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10998 * @param {Mixed} value The value to set
11000 setRawValue : function(v){
11001 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11004 selectText : function(start, end){
11005 var v = this.getRawValue();
11007 start = start === undefined ? 0 : start;
11008 end = end === undefined ? v.length : end;
11009 var d = this.inputEl().dom;
11010 if(d.setSelectionRange){
11011 d.setSelectionRange(start, end);
11012 }else if(d.createTextRange){
11013 var range = d.createTextRange();
11014 range.moveStart("character", start);
11015 range.moveEnd("character", v.length-end);
11022 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11023 * @param {Mixed} value The value to set
11025 setValue : function(v){
11028 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11034 processValue : function(value){
11035 if(this.stripCharsRe){
11036 var newValue = value.replace(this.stripCharsRe, '');
11037 if(newValue !== value){
11038 this.setRawValue(newValue);
11045 preFocus : function(){
11047 if(this.selectOnFocus){
11048 this.inputEl().dom.select();
11051 filterKeys : function(e){
11052 var k = e.getKey();
11053 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11056 var c = e.getCharCode(), cc = String.fromCharCode(c);
11057 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11060 if(!this.maskRe.test(cc)){
11065 * Clear any invalid styles/messages for this field
11067 clearInvalid : function(){
11069 if(!this.el || this.preventMark){ // not rendered
11074 this.el.removeClass([this.invalidClass, 'is-invalid']);
11076 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11078 var feedback = this.el.select('.form-control-feedback', true).first();
11081 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11086 if(this.indicator){
11087 this.indicator.removeClass('visible');
11088 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11091 this.fireEvent('valid', this);
11095 * Mark this field as valid
11097 markValid : function()
11099 if(!this.el || this.preventMark){ // not rendered...
11103 this.el.removeClass([this.invalidClass, this.validClass]);
11104 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11106 var feedback = this.el.select('.form-control-feedback', true).first();
11109 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11112 if(this.indicator){
11113 this.indicator.removeClass('visible');
11114 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11122 if(this.allowBlank && !this.getRawValue().length){
11125 if (Roo.bootstrap.version == 3) {
11126 this.el.addClass(this.validClass);
11128 this.inputEl().addClass('is-valid');
11131 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11133 var feedback = this.el.select('.form-control-feedback', true).first();
11136 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11137 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11142 this.fireEvent('valid', this);
11146 * Mark this field as invalid
11147 * @param {String} msg The validation message
11149 markInvalid : function(msg)
11151 if(!this.el || this.preventMark){ // not rendered
11155 this.el.removeClass([this.invalidClass, this.validClass]);
11156 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11158 var feedback = this.el.select('.form-control-feedback', true).first();
11161 this.el.select('.form-control-feedback', true).first().removeClass(
11162 [this.invalidFeedbackClass, this.validFeedbackClass]);
11169 if(this.allowBlank && !this.getRawValue().length){
11173 if(this.indicator){
11174 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11175 this.indicator.addClass('visible');
11177 if (Roo.bootstrap.version == 3) {
11178 this.el.addClass(this.invalidClass);
11180 this.inputEl().addClass('is-invalid');
11185 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11187 var feedback = this.el.select('.form-control-feedback', true).first();
11190 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11192 if(this.getValue().length || this.forceFeedback){
11193 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11200 this.fireEvent('invalid', this, msg);
11203 SafariOnKeyDown : function(event)
11205 // this is a workaround for a password hang bug on chrome/ webkit.
11206 if (this.inputEl().dom.type != 'password') {
11210 var isSelectAll = false;
11212 if(this.inputEl().dom.selectionEnd > 0){
11213 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11215 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11216 event.preventDefault();
11221 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11223 event.preventDefault();
11224 // this is very hacky as keydown always get's upper case.
11226 var cc = String.fromCharCode(event.getCharCode());
11227 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11231 adjustWidth : function(tag, w){
11232 tag = tag.toLowerCase();
11233 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11234 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11235 if(tag == 'input'){
11238 if(tag == 'textarea'){
11241 }else if(Roo.isOpera){
11242 if(tag == 'input'){
11245 if(tag == 'textarea'){
11253 setFieldLabel : function(v)
11255 if(!this.rendered){
11259 if(this.indicatorEl()){
11260 var ar = this.el.select('label > span',true);
11262 if (ar.elements.length) {
11263 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11264 this.fieldLabel = v;
11268 var br = this.el.select('label',true);
11270 if(br.elements.length) {
11271 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11272 this.fieldLabel = v;
11276 Roo.log('Cannot Found any of label > span || label in input');
11280 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11281 this.fieldLabel = v;
11296 * @class Roo.bootstrap.TextArea
11297 * @extends Roo.bootstrap.Input
11298 * Bootstrap TextArea class
11299 * @cfg {Number} cols Specifies the visible width of a text area
11300 * @cfg {Number} rows Specifies the visible number of lines in a text area
11301 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11302 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11303 * @cfg {string} html text
11306 * Create a new TextArea
11307 * @param {Object} config The config object
11310 Roo.bootstrap.TextArea = function(config){
11311 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11315 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11325 getAutoCreate : function(){
11327 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11333 if(this.inputType != 'hidden'){
11334 cfg.cls = 'form-group' //input-group
11342 value : this.value || '',
11343 html: this.html || '',
11344 cls : 'form-control',
11345 placeholder : this.placeholder || ''
11349 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11350 input.maxLength = this.maxLength;
11354 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11358 input.cols = this.cols;
11361 if (this.readOnly) {
11362 input.readonly = true;
11366 input.name = this.name;
11370 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11374 ['xs','sm','md','lg'].map(function(size){
11375 if (settings[size]) {
11376 cfg.cls += ' col-' + size + '-' + settings[size];
11380 var inputblock = input;
11382 if(this.hasFeedback && !this.allowBlank){
11386 cls: 'glyphicon form-control-feedback'
11390 cls : 'has-feedback',
11399 if (this.before || this.after) {
11402 cls : 'input-group',
11406 inputblock.cn.push({
11408 cls : 'input-group-addon',
11413 inputblock.cn.push(input);
11415 if(this.hasFeedback && !this.allowBlank){
11416 inputblock.cls += ' has-feedback';
11417 inputblock.cn.push(feedback);
11421 inputblock.cn.push({
11423 cls : 'input-group-addon',
11430 if (align ==='left' && this.fieldLabel.length) {
11435 cls : 'control-label',
11436 html : this.fieldLabel
11447 if(this.labelWidth > 12){
11448 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11451 if(this.labelWidth < 13 && this.labelmd == 0){
11452 this.labelmd = this.labelWidth;
11455 if(this.labellg > 0){
11456 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11457 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11460 if(this.labelmd > 0){
11461 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11462 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11465 if(this.labelsm > 0){
11466 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11467 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11470 if(this.labelxs > 0){
11471 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11472 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11475 } else if ( this.fieldLabel.length) {
11480 //cls : 'input-group-addon',
11481 html : this.fieldLabel
11499 if (this.disabled) {
11500 input.disabled=true;
11507 * return the real textarea element.
11509 inputEl: function ()
11511 return this.el.select('textarea.form-control',true).first();
11515 * Clear any invalid styles/messages for this field
11517 clearInvalid : function()
11520 if(!this.el || this.preventMark){ // not rendered
11524 var label = this.el.select('label', true).first();
11525 var icon = this.el.select('i.fa-star', true).first();
11530 this.el.removeClass( this.validClass);
11531 this.inputEl().removeClass('is-invalid');
11533 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11535 var feedback = this.el.select('.form-control-feedback', true).first();
11538 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11543 this.fireEvent('valid', this);
11547 * Mark this field as valid
11549 markValid : function()
11551 if(!this.el || this.preventMark){ // not rendered
11555 this.el.removeClass([this.invalidClass, this.validClass]);
11556 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11558 var feedback = this.el.select('.form-control-feedback', true).first();
11561 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11564 if(this.disabled || this.allowBlank){
11568 var label = this.el.select('label', true).first();
11569 var icon = this.el.select('i.fa-star', true).first();
11574 if (Roo.bootstrap.version == 3) {
11575 this.el.addClass(this.validClass);
11577 this.inputEl().addClass('is-valid');
11581 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11583 var feedback = this.el.select('.form-control-feedback', true).first();
11586 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11587 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11592 this.fireEvent('valid', this);
11596 * Mark this field as invalid
11597 * @param {String} msg The validation message
11599 markInvalid : function(msg)
11601 if(!this.el || this.preventMark){ // not rendered
11605 this.el.removeClass([this.invalidClass, this.validClass]);
11606 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11608 var feedback = this.el.select('.form-control-feedback', true).first();
11611 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11614 if(this.disabled || this.allowBlank){
11618 var label = this.el.select('label', true).first();
11619 var icon = this.el.select('i.fa-star', true).first();
11621 if(!this.getValue().length && label && !icon){
11622 this.el.createChild({
11624 cls : 'text-danger fa fa-lg fa-star',
11625 tooltip : 'This field is required',
11626 style : 'margin-right:5px;'
11630 if (Roo.bootstrap.version == 3) {
11631 this.el.addClass(this.invalidClass);
11633 this.inputEl().addClass('is-invalid');
11636 // fixme ... this may be depricated need to test..
11637 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11639 var feedback = this.el.select('.form-control-feedback', true).first();
11642 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11644 if(this.getValue().length || this.forceFeedback){
11645 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11652 this.fireEvent('invalid', this, msg);
11660 * trigger field - base class for combo..
11665 * @class Roo.bootstrap.TriggerField
11666 * @extends Roo.bootstrap.Input
11667 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11668 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11669 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11670 * for which you can provide a custom implementation. For example:
11672 var trigger = new Roo.bootstrap.TriggerField();
11673 trigger.onTriggerClick = myTriggerFn;
11674 trigger.applyTo('my-field');
11677 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11678 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11679 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11680 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11681 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11684 * Create a new TriggerField.
11685 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11686 * to the base TextField)
11688 Roo.bootstrap.TriggerField = function(config){
11689 this.mimicing = false;
11690 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11693 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11695 * @cfg {String} triggerClass A CSS class to apply to the trigger
11698 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11703 * @cfg {Boolean} removable (true|false) special filter default false
11707 /** @cfg {Boolean} grow @hide */
11708 /** @cfg {Number} growMin @hide */
11709 /** @cfg {Number} growMax @hide */
11715 autoSize: Roo.emptyFn,
11719 deferHeight : true,
11722 actionMode : 'wrap',
11727 getAutoCreate : function(){
11729 var align = this.labelAlign || this.parentLabelAlign();
11734 cls: 'form-group' //input-group
11741 type : this.inputType,
11742 cls : 'form-control',
11743 autocomplete: 'new-password',
11744 placeholder : this.placeholder || ''
11748 input.name = this.name;
11751 input.cls += ' input-' + this.size;
11754 if (this.disabled) {
11755 input.disabled=true;
11758 var inputblock = input;
11760 if(this.hasFeedback && !this.allowBlank){
11764 cls: 'glyphicon form-control-feedback'
11767 if(this.removable && !this.editable ){
11769 cls : 'has-feedback',
11775 cls : 'roo-combo-removable-btn close'
11782 cls : 'has-feedback',
11791 if(this.removable && !this.editable ){
11793 cls : 'roo-removable',
11799 cls : 'roo-combo-removable-btn close'
11806 if (this.before || this.after) {
11809 cls : 'input-group',
11813 inputblock.cn.push({
11815 cls : 'input-group-addon input-group-prepend input-group-text',
11820 inputblock.cn.push(input);
11822 if(this.hasFeedback && !this.allowBlank){
11823 inputblock.cls += ' has-feedback';
11824 inputblock.cn.push(feedback);
11828 inputblock.cn.push({
11830 cls : 'input-group-addon input-group-append input-group-text',
11839 var ibwrap = inputblock;
11844 cls: 'roo-select2-choices',
11848 cls: 'roo-select2-search-field',
11860 cls: 'roo-select2-container input-group',
11865 cls: 'form-hidden-field'
11871 if(!this.multiple && this.showToggleBtn){
11877 if (this.caret != false) {
11880 cls: 'fa fa-' + this.caret
11887 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11889 Roo.bootstrap.version == 3 ? caret : '',
11892 cls: 'combobox-clear',
11906 combobox.cls += ' roo-select2-container-multi';
11910 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11911 tooltip : 'This field is required'
11913 if (Roo.bootstrap.version == 4) {
11916 style : 'display:none'
11921 if (align ==='left' && this.fieldLabel.length) {
11923 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11930 cls : 'control-label',
11931 html : this.fieldLabel
11943 var labelCfg = cfg.cn[1];
11944 var contentCfg = cfg.cn[2];
11946 if(this.indicatorpos == 'right'){
11951 cls : 'control-label',
11955 html : this.fieldLabel
11969 labelCfg = cfg.cn[0];
11970 contentCfg = cfg.cn[1];
11973 if(this.labelWidth > 12){
11974 labelCfg.style = "width: " + this.labelWidth + 'px';
11977 if(this.labelWidth < 13 && this.labelmd == 0){
11978 this.labelmd = this.labelWidth;
11981 if(this.labellg > 0){
11982 labelCfg.cls += ' col-lg-' + this.labellg;
11983 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11986 if(this.labelmd > 0){
11987 labelCfg.cls += ' col-md-' + this.labelmd;
11988 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11991 if(this.labelsm > 0){
11992 labelCfg.cls += ' col-sm-' + this.labelsm;
11993 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11996 if(this.labelxs > 0){
11997 labelCfg.cls += ' col-xs-' + this.labelxs;
11998 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12001 } else if ( this.fieldLabel.length) {
12002 // Roo.log(" label");
12007 //cls : 'input-group-addon',
12008 html : this.fieldLabel
12016 if(this.indicatorpos == 'right'){
12024 html : this.fieldLabel
12038 // Roo.log(" no label && no align");
12045 ['xs','sm','md','lg'].map(function(size){
12046 if (settings[size]) {
12047 cfg.cls += ' col-' + size + '-' + settings[size];
12058 onResize : function(w, h){
12059 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12060 // if(typeof w == 'number'){
12061 // var x = w - this.trigger.getWidth();
12062 // this.inputEl().setWidth(this.adjustWidth('input', x));
12063 // this.trigger.setStyle('left', x+'px');
12068 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12071 getResizeEl : function(){
12072 return this.inputEl();
12076 getPositionEl : function(){
12077 return this.inputEl();
12081 alignErrorIcon : function(){
12082 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12086 initEvents : function(){
12090 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12091 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12092 if(!this.multiple && this.showToggleBtn){
12093 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12094 if(this.hideTrigger){
12095 this.trigger.setDisplayed(false);
12097 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12101 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12104 if(this.removable && !this.editable && !this.tickable){
12105 var close = this.closeTriggerEl();
12108 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12109 close.on('click', this.removeBtnClick, this, close);
12113 //this.trigger.addClassOnOver('x-form-trigger-over');
12114 //this.trigger.addClassOnClick('x-form-trigger-click');
12117 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12121 closeTriggerEl : function()
12123 var close = this.el.select('.roo-combo-removable-btn', true).first();
12124 return close ? close : false;
12127 removeBtnClick : function(e, h, el)
12129 e.preventDefault();
12131 if(this.fireEvent("remove", this) !== false){
12133 this.fireEvent("afterremove", this)
12137 createList : function()
12139 this.list = Roo.get(document.body).createChild({
12140 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12141 cls: 'typeahead typeahead-long dropdown-menu',
12142 style: 'display:none'
12145 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12150 initTrigger : function(){
12155 onDestroy : function(){
12157 this.trigger.removeAllListeners();
12158 // this.trigger.remove();
12161 // this.wrap.remove();
12163 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12167 onFocus : function(){
12168 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12170 if(!this.mimicing){
12171 this.wrap.addClass('x-trigger-wrap-focus');
12172 this.mimicing = true;
12173 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12174 if(this.monitorTab){
12175 this.el.on("keydown", this.checkTab, this);
12182 checkTab : function(e){
12183 if(e.getKey() == e.TAB){
12184 this.triggerBlur();
12189 onBlur : function(){
12194 mimicBlur : function(e, t){
12196 if(!this.wrap.contains(t) && this.validateBlur()){
12197 this.triggerBlur();
12203 triggerBlur : function(){
12204 this.mimicing = false;
12205 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12206 if(this.monitorTab){
12207 this.el.un("keydown", this.checkTab, this);
12209 //this.wrap.removeClass('x-trigger-wrap-focus');
12210 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12214 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12215 validateBlur : function(e, t){
12220 onDisable : function(){
12221 this.inputEl().dom.disabled = true;
12222 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12224 // this.wrap.addClass('x-item-disabled');
12229 onEnable : function(){
12230 this.inputEl().dom.disabled = false;
12231 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12233 // this.el.removeClass('x-item-disabled');
12238 onShow : function(){
12239 var ae = this.getActionEl();
12242 ae.dom.style.display = '';
12243 ae.dom.style.visibility = 'visible';
12249 onHide : function(){
12250 var ae = this.getActionEl();
12251 ae.dom.style.display = 'none';
12255 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12256 * by an implementing function.
12258 * @param {EventObject} e
12260 onTriggerClick : Roo.emptyFn
12268 * @class Roo.bootstrap.CardUploader
12269 * @extends Roo.bootstrap.Button
12270 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12271 * @cfg {Number} errorTimeout default 3000
12272 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12273 * @cfg {Array} html The button text.
12277 * Create a new CardUploader
12278 * @param {Object} config The config object
12281 Roo.bootstrap.CardUploader = function(config){
12285 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12288 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12295 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12298 errorTimeout : 3000,
12302 fileCollection : false,
12305 getAutoCreate : function()
12309 cls :'form-group' ,
12314 //cls : 'input-group-addon',
12315 html : this.fieldLabel
12322 value : this.value,
12323 cls : 'd-none form-control'
12328 multiple : 'multiple',
12330 cls : 'd-none roo-card-upload-selector'
12334 cls : 'roo-card-uploader-button-container w-100 mb-2'
12337 cls : 'card-columns roo-card-uploader-container'
12347 getChildContainer : function() /// what children are added to.
12349 return this.containerEl;
12352 getButtonContainer : function() /// what children are added to.
12354 return this.el.select(".roo-card-uploader-button-container").first();
12357 initEvents : function()
12360 Roo.bootstrap.Input.prototype.initEvents.call(this);
12364 xns: Roo.bootstrap,
12367 container_method : 'getButtonContainer' ,
12368 html : this.html, // fix changable?
12371 'click' : function(btn, e) {
12380 this.urlAPI = (window.createObjectURL && window) ||
12381 (window.URL && URL.revokeObjectURL && URL) ||
12382 (window.webkitURL && webkitURL);
12387 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12389 this.selectorEl.on('change', this.onFileSelected, this);
12392 this.images.forEach(function(img) {
12395 this.images = false;
12397 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12403 onClick : function(e)
12405 e.preventDefault();
12407 this.selectorEl.dom.click();
12411 onFileSelected : function(e)
12413 e.preventDefault();
12415 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12419 Roo.each(this.selectorEl.dom.files, function(file){
12420 this.addFile(file);
12429 addFile : function(file)
12432 if(typeof(file) === 'string'){
12433 throw "Add file by name?"; // should not happen
12437 if(!file || !this.urlAPI){
12447 var url = _this.urlAPI.createObjectURL( file);
12450 id : Roo.bootstrap.CardUploader.ID--,
12451 is_uploaded : false,
12454 mimetype : file.type,
12461 addCard : function (data)
12463 // hidden input element?
12464 // if the file is not an image...
12465 //then we need to use something other that and header_image
12470 xns : Roo.bootstrap,
12471 xtype : 'CardFooter',
12474 xns : Roo.bootstrap,
12480 xns : Roo.bootstrap,
12482 html : String.format("<small>{0}</small>", data.title),
12483 cls : 'col-11 text-left',
12488 click : function() {
12489 this.downloadCard(data.id)
12495 xns : Roo.bootstrap,
12503 click : function() {
12504 t.removeCard(data.id)
12516 var cn = this.addxtype(
12519 xns : Roo.bootstrap,
12522 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12523 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12524 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12529 initEvents : function() {
12530 Roo.bootstrap.Card.prototype.initEvents.call(this);
12531 this.imgEl = this.el.select('.card-img-top').first();
12533 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12534 this.imgEl.set({ 'pointer' : 'cursor' });
12543 // dont' really need ot update items.
12544 // this.items.push(cn);
12545 this.fileCollection.add(cn);
12546 this.updateInput();
12549 removeCard : function(id)
12552 var card = this.fileCollection.get(id);
12553 card.data.is_deleted = 1;
12554 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12555 this.fileCollection.remove(card);
12556 //this.items = this.items.filter(function(e) { return e != card });
12557 // dont' really need ot update items.
12558 card.el.dom.parentNode.removeChild(card.el.dom);
12563 this.fileCollection.each(function(card) {
12564 card.el.dom.parentNode.removeChild(card.el.dom);
12566 this.fileCollection.clear();
12567 this.updateInput();
12570 updateInput : function()
12573 this.fileCollection.each(function(e) {
12577 this.inputEl().dom.value = JSON.stringify(data);
12584 Roo.bootstrap.CardUploader.ID = -1;/*
12586 * Ext JS Library 1.1.1
12587 * Copyright(c) 2006-2007, Ext JS, LLC.
12589 * Originally Released Under LGPL - original licence link has changed is not relivant.
12592 * <script type="text/javascript">
12597 * @class Roo.data.SortTypes
12599 * Defines the default sorting (casting?) comparison functions used when sorting data.
12601 Roo.data.SortTypes = {
12603 * Default sort that does nothing
12604 * @param {Mixed} s The value being converted
12605 * @return {Mixed} The comparison value
12607 none : function(s){
12612 * The regular expression used to strip tags
12616 stripTagsRE : /<\/?[^>]+>/gi,
12619 * Strips all HTML tags to sort on text only
12620 * @param {Mixed} s The value being converted
12621 * @return {String} The comparison value
12623 asText : function(s){
12624 return String(s).replace(this.stripTagsRE, "");
12628 * Strips all HTML tags to sort on text only - Case insensitive
12629 * @param {Mixed} s The value being converted
12630 * @return {String} The comparison value
12632 asUCText : function(s){
12633 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12637 * Case insensitive string
12638 * @param {Mixed} s The value being converted
12639 * @return {String} The comparison value
12641 asUCString : function(s) {
12642 return String(s).toUpperCase();
12647 * @param {Mixed} s The value being converted
12648 * @return {Number} The comparison value
12650 asDate : function(s) {
12654 if(s instanceof Date){
12655 return s.getTime();
12657 return Date.parse(String(s));
12662 * @param {Mixed} s The value being converted
12663 * @return {Float} The comparison value
12665 asFloat : function(s) {
12666 var val = parseFloat(String(s).replace(/,/g, ""));
12675 * @param {Mixed} s The value being converted
12676 * @return {Number} The comparison value
12678 asInt : function(s) {
12679 var val = parseInt(String(s).replace(/,/g, ""));
12687 * Ext JS Library 1.1.1
12688 * Copyright(c) 2006-2007, Ext JS, LLC.
12690 * Originally Released Under LGPL - original licence link has changed is not relivant.
12693 * <script type="text/javascript">
12697 * @class Roo.data.Record
12698 * Instances of this class encapsulate both record <em>definition</em> information, and record
12699 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12700 * to access Records cached in an {@link Roo.data.Store} object.<br>
12702 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12703 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12706 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12708 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12709 * {@link #create}. The parameters are the same.
12710 * @param {Array} data An associative Array of data values keyed by the field name.
12711 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12712 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12713 * not specified an integer id is generated.
12715 Roo.data.Record = function(data, id){
12716 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12721 * Generate a constructor for a specific record layout.
12722 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12723 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12724 * Each field definition object may contain the following properties: <ul>
12725 * <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,
12726 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12727 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12728 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12729 * is being used, then this is a string containing the javascript expression to reference the data relative to
12730 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12731 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12732 * this may be omitted.</p></li>
12733 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12734 * <ul><li>auto (Default, implies no conversion)</li>
12739 * <li>date</li></ul></p></li>
12740 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12741 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12742 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12743 * by the Reader into an object that will be stored in the Record. It is passed the
12744 * following parameters:<ul>
12745 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12747 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12749 * <br>usage:<br><pre><code>
12750 var TopicRecord = Roo.data.Record.create(
12751 {name: 'title', mapping: 'topic_title'},
12752 {name: 'author', mapping: 'username'},
12753 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12754 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12755 {name: 'lastPoster', mapping: 'user2'},
12756 {name: 'excerpt', mapping: 'post_text'}
12759 var myNewRecord = new TopicRecord({
12760 title: 'Do my job please',
12763 lastPost: new Date(),
12764 lastPoster: 'Animal',
12765 excerpt: 'No way dude!'
12767 myStore.add(myNewRecord);
12772 Roo.data.Record.create = function(o){
12773 var f = function(){
12774 f.superclass.constructor.apply(this, arguments);
12776 Roo.extend(f, Roo.data.Record);
12777 var p = f.prototype;
12778 p.fields = new Roo.util.MixedCollection(false, function(field){
12781 for(var i = 0, len = o.length; i < len; i++){
12782 p.fields.add(new Roo.data.Field(o[i]));
12784 f.getField = function(name){
12785 return p.fields.get(name);
12790 Roo.data.Record.AUTO_ID = 1000;
12791 Roo.data.Record.EDIT = 'edit';
12792 Roo.data.Record.REJECT = 'reject';
12793 Roo.data.Record.COMMIT = 'commit';
12795 Roo.data.Record.prototype = {
12797 * Readonly flag - true if this record has been modified.
12806 join : function(store){
12807 this.store = store;
12811 * Set the named field to the specified value.
12812 * @param {String} name The name of the field to set.
12813 * @param {Object} value The value to set the field to.
12815 set : function(name, value){
12816 if(this.data[name] == value){
12820 if(!this.modified){
12821 this.modified = {};
12823 if(typeof this.modified[name] == 'undefined'){
12824 this.modified[name] = this.data[name];
12826 this.data[name] = value;
12827 if(!this.editing && this.store){
12828 this.store.afterEdit(this);
12833 * Get the value of the named field.
12834 * @param {String} name The name of the field to get the value of.
12835 * @return {Object} The value of the field.
12837 get : function(name){
12838 return this.data[name];
12842 beginEdit : function(){
12843 this.editing = true;
12844 this.modified = {};
12848 cancelEdit : function(){
12849 this.editing = false;
12850 delete this.modified;
12854 endEdit : function(){
12855 this.editing = false;
12856 if(this.dirty && this.store){
12857 this.store.afterEdit(this);
12862 * Usually called by the {@link Roo.data.Store} which owns the Record.
12863 * Rejects all changes made to the Record since either creation, or the last commit operation.
12864 * Modified fields are reverted to their original values.
12866 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12867 * of reject operations.
12869 reject : function(){
12870 var m = this.modified;
12872 if(typeof m[n] != "function"){
12873 this.data[n] = m[n];
12876 this.dirty = false;
12877 delete this.modified;
12878 this.editing = false;
12880 this.store.afterReject(this);
12885 * Usually called by the {@link Roo.data.Store} which owns the Record.
12886 * Commits all changes made to the Record since either creation, or the last commit operation.
12888 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12889 * of commit operations.
12891 commit : function(){
12892 this.dirty = false;
12893 delete this.modified;
12894 this.editing = false;
12896 this.store.afterCommit(this);
12901 hasError : function(){
12902 return this.error != null;
12906 clearError : function(){
12911 * Creates a copy of this record.
12912 * @param {String} id (optional) A new record id if you don't want to use this record's id
12915 copy : function(newId) {
12916 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12920 * Ext JS Library 1.1.1
12921 * Copyright(c) 2006-2007, Ext JS, LLC.
12923 * Originally Released Under LGPL - original licence link has changed is not relivant.
12926 * <script type="text/javascript">
12932 * @class Roo.data.Store
12933 * @extends Roo.util.Observable
12934 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12935 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12937 * 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
12938 * has no knowledge of the format of the data returned by the Proxy.<br>
12940 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12941 * instances from the data object. These records are cached and made available through accessor functions.
12943 * Creates a new Store.
12944 * @param {Object} config A config object containing the objects needed for the Store to access data,
12945 * and read the data into Records.
12947 Roo.data.Store = function(config){
12948 this.data = new Roo.util.MixedCollection(false);
12949 this.data.getKey = function(o){
12952 this.baseParams = {};
12954 this.paramNames = {
12959 "multisort" : "_multisort"
12962 if(config && config.data){
12963 this.inlineData = config.data;
12964 delete config.data;
12967 Roo.apply(this, config);
12969 if(this.reader){ // reader passed
12970 this.reader = Roo.factory(this.reader, Roo.data);
12971 this.reader.xmodule = this.xmodule || false;
12972 if(!this.recordType){
12973 this.recordType = this.reader.recordType;
12975 if(this.reader.onMetaChange){
12976 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12980 if(this.recordType){
12981 this.fields = this.recordType.prototype.fields;
12983 this.modified = [];
12987 * @event datachanged
12988 * Fires when the data cache has changed, and a widget which is using this Store
12989 * as a Record cache should refresh its view.
12990 * @param {Store} this
12992 datachanged : true,
12994 * @event metachange
12995 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12996 * @param {Store} this
12997 * @param {Object} meta The JSON metadata
13002 * Fires when Records have been added to the Store
13003 * @param {Store} this
13004 * @param {Roo.data.Record[]} records The array of Records added
13005 * @param {Number} index The index at which the record(s) were added
13010 * Fires when a Record has been removed from the Store
13011 * @param {Store} this
13012 * @param {Roo.data.Record} record The Record that was removed
13013 * @param {Number} index The index at which the record was removed
13018 * Fires when a Record has been updated
13019 * @param {Store} this
13020 * @param {Roo.data.Record} record The Record that was updated
13021 * @param {String} operation The update operation being performed. Value may be one of:
13023 Roo.data.Record.EDIT
13024 Roo.data.Record.REJECT
13025 Roo.data.Record.COMMIT
13031 * Fires when the data cache has been cleared.
13032 * @param {Store} this
13036 * @event beforeload
13037 * Fires before a request is made for a new data object. If the beforeload handler returns false
13038 * the load action will be canceled.
13039 * @param {Store} this
13040 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13044 * @event beforeloadadd
13045 * Fires after a new set of Records has been loaded.
13046 * @param {Store} this
13047 * @param {Roo.data.Record[]} records The Records that were loaded
13048 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13050 beforeloadadd : true,
13053 * Fires after a new set of Records has been loaded, before they are added to the store.
13054 * @param {Store} this
13055 * @param {Roo.data.Record[]} records The Records that were loaded
13056 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13057 * @params {Object} return from reader
13061 * @event loadexception
13062 * Fires if an exception occurs in the Proxy during loading.
13063 * Called with the signature of the Proxy's "loadexception" event.
13064 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13067 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13068 * @param {Object} load options
13069 * @param {Object} jsonData from your request (normally this contains the Exception)
13071 loadexception : true
13075 this.proxy = Roo.factory(this.proxy, Roo.data);
13076 this.proxy.xmodule = this.xmodule || false;
13077 this.relayEvents(this.proxy, ["loadexception"]);
13079 this.sortToggle = {};
13080 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13082 Roo.data.Store.superclass.constructor.call(this);
13084 if(this.inlineData){
13085 this.loadData(this.inlineData);
13086 delete this.inlineData;
13090 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13092 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13093 * without a remote query - used by combo/forms at present.
13097 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13100 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13103 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13104 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13107 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13108 * on any HTTP request
13111 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13114 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13118 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13119 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13121 remoteSort : false,
13124 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13125 * loaded or when a record is removed. (defaults to false).
13127 pruneModifiedRecords : false,
13130 lastOptions : null,
13133 * Add Records to the Store and fires the add event.
13134 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13136 add : function(records){
13137 records = [].concat(records);
13138 for(var i = 0, len = records.length; i < len; i++){
13139 records[i].join(this);
13141 var index = this.data.length;
13142 this.data.addAll(records);
13143 this.fireEvent("add", this, records, index);
13147 * Remove a Record from the Store and fires the remove event.
13148 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13150 remove : function(record){
13151 var index = this.data.indexOf(record);
13152 this.data.removeAt(index);
13154 if(this.pruneModifiedRecords){
13155 this.modified.remove(record);
13157 this.fireEvent("remove", this, record, index);
13161 * Remove all Records from the Store and fires the clear event.
13163 removeAll : function(){
13165 if(this.pruneModifiedRecords){
13166 this.modified = [];
13168 this.fireEvent("clear", this);
13172 * Inserts Records to the Store at the given index and fires the add event.
13173 * @param {Number} index The start index at which to insert the passed Records.
13174 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13176 insert : function(index, records){
13177 records = [].concat(records);
13178 for(var i = 0, len = records.length; i < len; i++){
13179 this.data.insert(index, records[i]);
13180 records[i].join(this);
13182 this.fireEvent("add", this, records, index);
13186 * Get the index within the cache of the passed Record.
13187 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13188 * @return {Number} The index of the passed Record. Returns -1 if not found.
13190 indexOf : function(record){
13191 return this.data.indexOf(record);
13195 * Get the index within the cache of the Record with the passed id.
13196 * @param {String} id The id of the Record to find.
13197 * @return {Number} The index of the Record. Returns -1 if not found.
13199 indexOfId : function(id){
13200 return this.data.indexOfKey(id);
13204 * Get the Record with the specified id.
13205 * @param {String} id The id of the Record to find.
13206 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13208 getById : function(id){
13209 return this.data.key(id);
13213 * Get the Record at the specified index.
13214 * @param {Number} index The index of the Record to find.
13215 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13217 getAt : function(index){
13218 return this.data.itemAt(index);
13222 * Returns a range of Records between specified indices.
13223 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13224 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13225 * @return {Roo.data.Record[]} An array of Records
13227 getRange : function(start, end){
13228 return this.data.getRange(start, end);
13232 storeOptions : function(o){
13233 o = Roo.apply({}, o);
13236 this.lastOptions = o;
13240 * Loads the Record cache from the configured Proxy using the configured Reader.
13242 * If using remote paging, then the first load call must specify the <em>start</em>
13243 * and <em>limit</em> properties in the options.params property to establish the initial
13244 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13246 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13247 * and this call will return before the new data has been loaded. Perform any post-processing
13248 * in a callback function, or in a "load" event handler.</strong>
13250 * @param {Object} options An object containing properties which control loading options:<ul>
13251 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13252 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13253 * passed the following arguments:<ul>
13254 * <li>r : Roo.data.Record[]</li>
13255 * <li>options: Options object from the load call</li>
13256 * <li>success: Boolean success indicator</li></ul></li>
13257 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13258 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13261 load : function(options){
13262 options = options || {};
13263 if(this.fireEvent("beforeload", this, options) !== false){
13264 this.storeOptions(options);
13265 var p = Roo.apply(options.params || {}, this.baseParams);
13266 // if meta was not loaded from remote source.. try requesting it.
13267 if (!this.reader.metaFromRemote) {
13268 p._requestMeta = 1;
13270 if(this.sortInfo && this.remoteSort){
13271 var pn = this.paramNames;
13272 p[pn["sort"]] = this.sortInfo.field;
13273 p[pn["dir"]] = this.sortInfo.direction;
13275 if (this.multiSort) {
13276 var pn = this.paramNames;
13277 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13280 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13285 * Reloads the Record cache from the configured Proxy using the configured Reader and
13286 * the options from the last load operation performed.
13287 * @param {Object} options (optional) An object containing properties which may override the options
13288 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13289 * the most recently used options are reused).
13291 reload : function(options){
13292 this.load(Roo.applyIf(options||{}, this.lastOptions));
13296 // Called as a callback by the Reader during a load operation.
13297 loadRecords : function(o, options, success){
13298 if(!o || success === false){
13299 if(success !== false){
13300 this.fireEvent("load", this, [], options, o);
13302 if(options.callback){
13303 options.callback.call(options.scope || this, [], options, false);
13307 // if data returned failure - throw an exception.
13308 if (o.success === false) {
13309 // show a message if no listener is registered.
13310 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13311 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13313 // loadmask wil be hooked into this..
13314 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13317 var r = o.records, t = o.totalRecords || r.length;
13319 this.fireEvent("beforeloadadd", this, r, options, o);
13321 if(!options || options.add !== true){
13322 if(this.pruneModifiedRecords){
13323 this.modified = [];
13325 for(var i = 0, len = r.length; i < len; i++){
13329 this.data = this.snapshot;
13330 delete this.snapshot;
13333 this.data.addAll(r);
13334 this.totalLength = t;
13336 this.fireEvent("datachanged", this);
13338 this.totalLength = Math.max(t, this.data.length+r.length);
13342 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13344 var e = new Roo.data.Record({});
13346 e.set(this.parent.displayField, this.parent.emptyTitle);
13347 e.set(this.parent.valueField, '');
13352 this.fireEvent("load", this, r, options, o);
13353 if(options.callback){
13354 options.callback.call(options.scope || this, r, options, true);
13360 * Loads data from a passed data block. A Reader which understands the format of the data
13361 * must have been configured in the constructor.
13362 * @param {Object} data The data block from which to read the Records. The format of the data expected
13363 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13364 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13366 loadData : function(o, append){
13367 var r = this.reader.readRecords(o);
13368 this.loadRecords(r, {add: append}, true);
13372 * using 'cn' the nested child reader read the child array into it's child stores.
13373 * @param {Object} rec The record with a 'children array
13375 loadDataFromChildren : function(rec)
13377 this.loadData(this.reader.toLoadData(rec));
13382 * Gets the number of cached records.
13384 * <em>If using paging, this may not be the total size of the dataset. If the data object
13385 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13386 * the data set size</em>
13388 getCount : function(){
13389 return this.data.length || 0;
13393 * Gets the total number of records in the dataset as returned by the server.
13395 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13396 * the dataset size</em>
13398 getTotalCount : function(){
13399 return this.totalLength || 0;
13403 * Returns the sort state of the Store as an object with two properties:
13405 field {String} The name of the field by which the Records are sorted
13406 direction {String} The sort order, "ASC" or "DESC"
13409 getSortState : function(){
13410 return this.sortInfo;
13414 applySort : function(){
13415 if(this.sortInfo && !this.remoteSort){
13416 var s = this.sortInfo, f = s.field;
13417 var st = this.fields.get(f).sortType;
13418 var fn = function(r1, r2){
13419 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13420 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13422 this.data.sort(s.direction, fn);
13423 if(this.snapshot && this.snapshot != this.data){
13424 this.snapshot.sort(s.direction, fn);
13430 * Sets the default sort column and order to be used by the next load operation.
13431 * @param {String} fieldName The name of the field to sort by.
13432 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13434 setDefaultSort : function(field, dir){
13435 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13439 * Sort the Records.
13440 * If remote sorting is used, the sort is performed on the server, and the cache is
13441 * reloaded. If local sorting is used, the cache is sorted internally.
13442 * @param {String} fieldName The name of the field to sort by.
13443 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13445 sort : function(fieldName, dir){
13446 var f = this.fields.get(fieldName);
13448 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13450 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13451 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13456 this.sortToggle[f.name] = dir;
13457 this.sortInfo = {field: f.name, direction: dir};
13458 if(!this.remoteSort){
13460 this.fireEvent("datachanged", this);
13462 this.load(this.lastOptions);
13467 * Calls the specified function for each of the Records in the cache.
13468 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13469 * Returning <em>false</em> aborts and exits the iteration.
13470 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13472 each : function(fn, scope){
13473 this.data.each(fn, scope);
13477 * Gets all records modified since the last commit. Modified records are persisted across load operations
13478 * (e.g., during paging).
13479 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13481 getModifiedRecords : function(){
13482 return this.modified;
13486 createFilterFn : function(property, value, anyMatch){
13487 if(!value.exec){ // not a regex
13488 value = String(value);
13489 if(value.length == 0){
13492 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13494 return function(r){
13495 return value.test(r.data[property]);
13500 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13501 * @param {String} property A field on your records
13502 * @param {Number} start The record index to start at (defaults to 0)
13503 * @param {Number} end The last record index to include (defaults to length - 1)
13504 * @return {Number} The sum
13506 sum : function(property, start, end){
13507 var rs = this.data.items, v = 0;
13508 start = start || 0;
13509 end = (end || end === 0) ? end : rs.length-1;
13511 for(var i = start; i <= end; i++){
13512 v += (rs[i].data[property] || 0);
13518 * Filter the records by a specified property.
13519 * @param {String} field A field on your records
13520 * @param {String/RegExp} value Either a string that the field
13521 * should start with or a RegExp to test against the field
13522 * @param {Boolean} anyMatch True to match any part not just the beginning
13524 filter : function(property, value, anyMatch){
13525 var fn = this.createFilterFn(property, value, anyMatch);
13526 return fn ? this.filterBy(fn) : this.clearFilter();
13530 * Filter by a function. The specified function will be called with each
13531 * record in this data source. If the function returns true the record is included,
13532 * otherwise it is filtered.
13533 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13534 * @param {Object} scope (optional) The scope of the function (defaults to this)
13536 filterBy : function(fn, scope){
13537 this.snapshot = this.snapshot || this.data;
13538 this.data = this.queryBy(fn, scope||this);
13539 this.fireEvent("datachanged", this);
13543 * Query the records by a specified property.
13544 * @param {String} field A field on your records
13545 * @param {String/RegExp} value Either a string that the field
13546 * should start with or a RegExp to test against the field
13547 * @param {Boolean} anyMatch True to match any part not just the beginning
13548 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13550 query : function(property, value, anyMatch){
13551 var fn = this.createFilterFn(property, value, anyMatch);
13552 return fn ? this.queryBy(fn) : this.data.clone();
13556 * Query by a function. The specified function will be called with each
13557 * record in this data source. If the function returns true the record is included
13559 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13560 * @param {Object} scope (optional) The scope of the function (defaults to this)
13561 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13563 queryBy : function(fn, scope){
13564 var data = this.snapshot || this.data;
13565 return data.filterBy(fn, scope||this);
13569 * Collects unique values for a particular dataIndex from this store.
13570 * @param {String} dataIndex The property to collect
13571 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13572 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13573 * @return {Array} An array of the unique values
13575 collect : function(dataIndex, allowNull, bypassFilter){
13576 var d = (bypassFilter === true && this.snapshot) ?
13577 this.snapshot.items : this.data.items;
13578 var v, sv, r = [], l = {};
13579 for(var i = 0, len = d.length; i < len; i++){
13580 v = d[i].data[dataIndex];
13582 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13591 * Revert to a view of the Record cache with no filtering applied.
13592 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13594 clearFilter : function(suppressEvent){
13595 if(this.snapshot && this.snapshot != this.data){
13596 this.data = this.snapshot;
13597 delete this.snapshot;
13598 if(suppressEvent !== true){
13599 this.fireEvent("datachanged", this);
13605 afterEdit : function(record){
13606 if(this.modified.indexOf(record) == -1){
13607 this.modified.push(record);
13609 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13613 afterReject : function(record){
13614 this.modified.remove(record);
13615 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13619 afterCommit : function(record){
13620 this.modified.remove(record);
13621 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13625 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13626 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13628 commitChanges : function(){
13629 var m = this.modified.slice(0);
13630 this.modified = [];
13631 for(var i = 0, len = m.length; i < len; i++){
13637 * Cancel outstanding changes on all changed records.
13639 rejectChanges : function(){
13640 var m = this.modified.slice(0);
13641 this.modified = [];
13642 for(var i = 0, len = m.length; i < len; i++){
13647 onMetaChange : function(meta, rtype, o){
13648 this.recordType = rtype;
13649 this.fields = rtype.prototype.fields;
13650 delete this.snapshot;
13651 this.sortInfo = meta.sortInfo || this.sortInfo;
13652 this.modified = [];
13653 this.fireEvent('metachange', this, this.reader.meta);
13656 moveIndex : function(data, type)
13658 var index = this.indexOf(data);
13660 var newIndex = index + type;
13664 this.insert(newIndex, data);
13669 * Ext JS Library 1.1.1
13670 * Copyright(c) 2006-2007, Ext JS, LLC.
13672 * Originally Released Under LGPL - original licence link has changed is not relivant.
13675 * <script type="text/javascript">
13679 * @class Roo.data.SimpleStore
13680 * @extends Roo.data.Store
13681 * Small helper class to make creating Stores from Array data easier.
13682 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13683 * @cfg {Array} fields An array of field definition objects, or field name strings.
13684 * @cfg {Object} an existing reader (eg. copied from another store)
13685 * @cfg {Array} data The multi-dimensional array of data
13687 * @param {Object} config
13689 Roo.data.SimpleStore = function(config)
13691 Roo.data.SimpleStore.superclass.constructor.call(this, {
13693 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13696 Roo.data.Record.create(config.fields)
13698 proxy : new Roo.data.MemoryProxy(config.data)
13702 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13704 * Ext JS Library 1.1.1
13705 * Copyright(c) 2006-2007, Ext JS, LLC.
13707 * Originally Released Under LGPL - original licence link has changed is not relivant.
13710 * <script type="text/javascript">
13715 * @extends Roo.data.Store
13716 * @class Roo.data.JsonStore
13717 * Small helper class to make creating Stores for JSON data easier. <br/>
13719 var store = new Roo.data.JsonStore({
13720 url: 'get-images.php',
13722 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13725 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13726 * JsonReader and HttpProxy (unless inline data is provided).</b>
13727 * @cfg {Array} fields An array of field definition objects, or field name strings.
13729 * @param {Object} config
13731 Roo.data.JsonStore = function(c){
13732 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13733 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13734 reader: new Roo.data.JsonReader(c, c.fields)
13737 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13739 * Ext JS Library 1.1.1
13740 * Copyright(c) 2006-2007, Ext JS, LLC.
13742 * Originally Released Under LGPL - original licence link has changed is not relivant.
13745 * <script type="text/javascript">
13749 Roo.data.Field = function(config){
13750 if(typeof config == "string"){
13751 config = {name: config};
13753 Roo.apply(this, config);
13756 this.type = "auto";
13759 var st = Roo.data.SortTypes;
13760 // named sortTypes are supported, here we look them up
13761 if(typeof this.sortType == "string"){
13762 this.sortType = st[this.sortType];
13765 // set default sortType for strings and dates
13766 if(!this.sortType){
13769 this.sortType = st.asUCString;
13772 this.sortType = st.asDate;
13775 this.sortType = st.none;
13780 var stripRe = /[\$,%]/g;
13782 // prebuilt conversion function for this field, instead of
13783 // switching every time we're reading a value
13785 var cv, dateFormat = this.dateFormat;
13790 cv = function(v){ return v; };
13793 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13797 return v !== undefined && v !== null && v !== '' ?
13798 parseInt(String(v).replace(stripRe, ""), 10) : '';
13803 return v !== undefined && v !== null && v !== '' ?
13804 parseFloat(String(v).replace(stripRe, ""), 10) : '';
13809 cv = function(v){ return v === true || v === "true" || v == 1; };
13816 if(v instanceof Date){
13820 if(dateFormat == "timestamp"){
13821 return new Date(v*1000);
13823 return Date.parseDate(v, dateFormat);
13825 var parsed = Date.parse(v);
13826 return parsed ? new Date(parsed) : null;
13835 Roo.data.Field.prototype = {
13843 * Ext JS Library 1.1.1
13844 * Copyright(c) 2006-2007, Ext JS, LLC.
13846 * Originally Released Under LGPL - original licence link has changed is not relivant.
13849 * <script type="text/javascript">
13852 // Base class for reading structured data from a data source. This class is intended to be
13853 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13856 * @class Roo.data.DataReader
13857 * Base class for reading structured data from a data source. This class is intended to be
13858 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13861 Roo.data.DataReader = function(meta, recordType){
13865 this.recordType = recordType instanceof Array ?
13866 Roo.data.Record.create(recordType) : recordType;
13869 Roo.data.DataReader.prototype = {
13872 readerType : 'Data',
13874 * Create an empty record
13875 * @param {Object} data (optional) - overlay some values
13876 * @return {Roo.data.Record} record created.
13878 newRow : function(d) {
13880 this.recordType.prototype.fields.each(function(c) {
13882 case 'int' : da[c.name] = 0; break;
13883 case 'date' : da[c.name] = new Date(); break;
13884 case 'float' : da[c.name] = 0.0; break;
13885 case 'boolean' : da[c.name] = false; break;
13886 default : da[c.name] = ""; break;
13890 return new this.recordType(Roo.apply(da, d));
13896 * Ext JS Library 1.1.1
13897 * Copyright(c) 2006-2007, Ext JS, LLC.
13899 * Originally Released Under LGPL - original licence link has changed is not relivant.
13902 * <script type="text/javascript">
13906 * @class Roo.data.DataProxy
13907 * @extends Roo.data.Observable
13908 * This class is an abstract base class for implementations which provide retrieval of
13909 * unformatted data objects.<br>
13911 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13912 * (of the appropriate type which knows how to parse the data object) to provide a block of
13913 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13915 * Custom implementations must implement the load method as described in
13916 * {@link Roo.data.HttpProxy#load}.
13918 Roo.data.DataProxy = function(){
13921 * @event beforeload
13922 * Fires before a network request is made to retrieve a data object.
13923 * @param {Object} This DataProxy object.
13924 * @param {Object} params The params parameter to the load function.
13929 * Fires before the load method's callback is called.
13930 * @param {Object} This DataProxy object.
13931 * @param {Object} o The data object.
13932 * @param {Object} arg The callback argument object passed to the load function.
13936 * @event loadexception
13937 * Fires if an Exception occurs during data retrieval.
13938 * @param {Object} This DataProxy object.
13939 * @param {Object} o The data object.
13940 * @param {Object} arg The callback argument object passed to the load function.
13941 * @param {Object} e The Exception.
13943 loadexception : true
13945 Roo.data.DataProxy.superclass.constructor.call(this);
13948 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13951 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13955 * Ext JS Library 1.1.1
13956 * Copyright(c) 2006-2007, Ext JS, LLC.
13958 * Originally Released Under LGPL - original licence link has changed is not relivant.
13961 * <script type="text/javascript">
13964 * @class Roo.data.MemoryProxy
13965 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13966 * to the Reader when its load method is called.
13968 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13970 Roo.data.MemoryProxy = function(data){
13974 Roo.data.MemoryProxy.superclass.constructor.call(this);
13978 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13981 * Load data from the requested source (in this case an in-memory
13982 * data object passed to the constructor), read the data object into
13983 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13984 * process that block using the passed callback.
13985 * @param {Object} params This parameter is not used by the MemoryProxy class.
13986 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13987 * object into a block of Roo.data.Records.
13988 * @param {Function} callback The function into which to pass the block of Roo.data.records.
13989 * The function must be passed <ul>
13990 * <li>The Record block object</li>
13991 * <li>The "arg" argument from the load function</li>
13992 * <li>A boolean success indicator</li>
13994 * @param {Object} scope The scope in which to call the callback
13995 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13997 load : function(params, reader, callback, scope, arg){
13998 params = params || {};
14001 result = reader.readRecords(params.data ? params.data :this.data);
14003 this.fireEvent("loadexception", this, arg, null, e);
14004 callback.call(scope, null, arg, false);
14007 callback.call(scope, result, arg, true);
14011 update : function(params, records){
14016 * Ext JS Library 1.1.1
14017 * Copyright(c) 2006-2007, Ext JS, LLC.
14019 * Originally Released Under LGPL - original licence link has changed is not relivant.
14022 * <script type="text/javascript">
14025 * @class Roo.data.HttpProxy
14026 * @extends Roo.data.DataProxy
14027 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14028 * configured to reference a certain URL.<br><br>
14030 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14031 * from which the running page was served.<br><br>
14033 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14035 * Be aware that to enable the browser to parse an XML document, the server must set
14036 * the Content-Type header in the HTTP response to "text/xml".
14038 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14039 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14040 * will be used to make the request.
14042 Roo.data.HttpProxy = function(conn){
14043 Roo.data.HttpProxy.superclass.constructor.call(this);
14044 // is conn a conn config or a real conn?
14046 this.useAjax = !conn || !conn.events;
14050 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14051 // thse are take from connection...
14054 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14057 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14058 * extra parameters to each request made by this object. (defaults to undefined)
14061 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14062 * to each request made by this object. (defaults to undefined)
14065 * @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)
14068 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14071 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14077 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14081 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14082 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14083 * a finer-grained basis than the DataProxy events.
14085 getConnection : function(){
14086 return this.useAjax ? Roo.Ajax : this.conn;
14090 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14091 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14092 * process that block using the passed callback.
14093 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14094 * for the request to the remote server.
14095 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14096 * object into a block of Roo.data.Records.
14097 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14098 * The function must be passed <ul>
14099 * <li>The Record block object</li>
14100 * <li>The "arg" argument from the load function</li>
14101 * <li>A boolean success indicator</li>
14103 * @param {Object} scope The scope in which to call the callback
14104 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14106 load : function(params, reader, callback, scope, arg){
14107 if(this.fireEvent("beforeload", this, params) !== false){
14109 params : params || {},
14111 callback : callback,
14116 callback : this.loadResponse,
14120 Roo.applyIf(o, this.conn);
14121 if(this.activeRequest){
14122 Roo.Ajax.abort(this.activeRequest);
14124 this.activeRequest = Roo.Ajax.request(o);
14126 this.conn.request(o);
14129 callback.call(scope||this, null, arg, false);
14134 loadResponse : function(o, success, response){
14135 delete this.activeRequest;
14137 this.fireEvent("loadexception", this, o, response);
14138 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14143 result = o.reader.read(response);
14145 this.fireEvent("loadexception", this, o, response, e);
14146 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14150 this.fireEvent("load", this, o, o.request.arg);
14151 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14155 update : function(dataSet){
14160 updateResponse : function(dataSet){
14165 * Ext JS Library 1.1.1
14166 * Copyright(c) 2006-2007, Ext JS, LLC.
14168 * Originally Released Under LGPL - original licence link has changed is not relivant.
14171 * <script type="text/javascript">
14175 * @class Roo.data.ScriptTagProxy
14176 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14177 * other than the originating domain of the running page.<br><br>
14179 * <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
14180 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14182 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14183 * source code that is used as the source inside a <script> tag.<br><br>
14185 * In order for the browser to process the returned data, the server must wrap the data object
14186 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14187 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14188 * depending on whether the callback name was passed:
14191 boolean scriptTag = false;
14192 String cb = request.getParameter("callback");
14195 response.setContentType("text/javascript");
14197 response.setContentType("application/x-json");
14199 Writer out = response.getWriter();
14201 out.write(cb + "(");
14203 out.print(dataBlock.toJsonString());
14210 * @param {Object} config A configuration object.
14212 Roo.data.ScriptTagProxy = function(config){
14213 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14214 Roo.apply(this, config);
14215 this.head = document.getElementsByTagName("head")[0];
14218 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14220 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14222 * @cfg {String} url The URL from which to request the data object.
14225 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14229 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14230 * the server the name of the callback function set up by the load call to process the returned data object.
14231 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14232 * javascript output which calls this named function passing the data object as its only parameter.
14234 callbackParam : "callback",
14236 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14237 * name to the request.
14242 * Load data from the configured URL, read the data object into
14243 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14244 * process that block using the passed callback.
14245 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14246 * for the request to the remote server.
14247 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14248 * object into a block of Roo.data.Records.
14249 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14250 * The function must be passed <ul>
14251 * <li>The Record block object</li>
14252 * <li>The "arg" argument from the load function</li>
14253 * <li>A boolean success indicator</li>
14255 * @param {Object} scope The scope in which to call the callback
14256 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14258 load : function(params, reader, callback, scope, arg){
14259 if(this.fireEvent("beforeload", this, params) !== false){
14261 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14263 var url = this.url;
14264 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14266 url += "&_dc=" + (new Date().getTime());
14268 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14271 cb : "stcCallback"+transId,
14272 scriptId : "stcScript"+transId,
14276 callback : callback,
14282 window[trans.cb] = function(o){
14283 conn.handleResponse(o, trans);
14286 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14288 if(this.autoAbort !== false){
14292 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14294 var script = document.createElement("script");
14295 script.setAttribute("src", url);
14296 script.setAttribute("type", "text/javascript");
14297 script.setAttribute("id", trans.scriptId);
14298 this.head.appendChild(script);
14300 this.trans = trans;
14302 callback.call(scope||this, null, arg, false);
14307 isLoading : function(){
14308 return this.trans ? true : false;
14312 * Abort the current server request.
14314 abort : function(){
14315 if(this.isLoading()){
14316 this.destroyTrans(this.trans);
14321 destroyTrans : function(trans, isLoaded){
14322 this.head.removeChild(document.getElementById(trans.scriptId));
14323 clearTimeout(trans.timeoutId);
14325 window[trans.cb] = undefined;
14327 delete window[trans.cb];
14330 // if hasn't been loaded, wait for load to remove it to prevent script error
14331 window[trans.cb] = function(){
14332 window[trans.cb] = undefined;
14334 delete window[trans.cb];
14341 handleResponse : function(o, trans){
14342 this.trans = false;
14343 this.destroyTrans(trans, true);
14346 result = trans.reader.readRecords(o);
14348 this.fireEvent("loadexception", this, o, trans.arg, e);
14349 trans.callback.call(trans.scope||window, null, trans.arg, false);
14352 this.fireEvent("load", this, o, trans.arg);
14353 trans.callback.call(trans.scope||window, result, trans.arg, true);
14357 handleFailure : function(trans){
14358 this.trans = false;
14359 this.destroyTrans(trans, false);
14360 this.fireEvent("loadexception", this, null, trans.arg);
14361 trans.callback.call(trans.scope||window, null, trans.arg, false);
14365 * Ext JS Library 1.1.1
14366 * Copyright(c) 2006-2007, Ext JS, LLC.
14368 * Originally Released Under LGPL - original licence link has changed is not relivant.
14371 * <script type="text/javascript">
14375 * @class Roo.data.JsonReader
14376 * @extends Roo.data.DataReader
14377 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14378 * based on mappings in a provided Roo.data.Record constructor.
14380 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14381 * in the reply previously.
14386 var RecordDef = Roo.data.Record.create([
14387 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14388 {name: 'occupation'} // This field will use "occupation" as the mapping.
14390 var myReader = new Roo.data.JsonReader({
14391 totalProperty: "results", // The property which contains the total dataset size (optional)
14392 root: "rows", // The property which contains an Array of row objects
14393 id: "id" // The property within each row object that provides an ID for the record (optional)
14397 * This would consume a JSON file like this:
14399 { 'results': 2, 'rows': [
14400 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14401 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14404 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14405 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14406 * paged from the remote server.
14407 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14408 * @cfg {String} root name of the property which contains the Array of row objects.
14409 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14410 * @cfg {Array} fields Array of field definition objects
14412 * Create a new JsonReader
14413 * @param {Object} meta Metadata configuration options
14414 * @param {Object} recordType Either an Array of field definition objects,
14415 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14417 Roo.data.JsonReader = function(meta, recordType){
14420 // set some defaults:
14421 Roo.applyIf(meta, {
14422 totalProperty: 'total',
14423 successProperty : 'success',
14428 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14430 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14432 readerType : 'Json',
14435 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14436 * Used by Store query builder to append _requestMeta to params.
14439 metaFromRemote : false,
14441 * This method is only used by a DataProxy which has retrieved data from a remote server.
14442 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14443 * @return {Object} data A data block which is used by an Roo.data.Store object as
14444 * a cache of Roo.data.Records.
14446 read : function(response){
14447 var json = response.responseText;
14449 var o = /* eval:var:o */ eval("("+json+")");
14451 throw {message: "JsonReader.read: Json object not found"};
14457 this.metaFromRemote = true;
14458 this.meta = o.metaData;
14459 this.recordType = Roo.data.Record.create(o.metaData.fields);
14460 this.onMetaChange(this.meta, this.recordType, o);
14462 return this.readRecords(o);
14465 // private function a store will implement
14466 onMetaChange : function(meta, recordType, o){
14473 simpleAccess: function(obj, subsc) {
14480 getJsonAccessor: function(){
14482 return function(expr) {
14484 return(re.test(expr))
14485 ? new Function("obj", "return obj." + expr)
14490 return Roo.emptyFn;
14495 * Create a data block containing Roo.data.Records from an XML document.
14496 * @param {Object} o An object which contains an Array of row objects in the property specified
14497 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14498 * which contains the total size of the dataset.
14499 * @return {Object} data A data block which is used by an Roo.data.Store object as
14500 * a cache of Roo.data.Records.
14502 readRecords : function(o){
14504 * After any data loads, the raw JSON data is available for further custom processing.
14508 var s = this.meta, Record = this.recordType,
14509 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14511 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14513 if(s.totalProperty) {
14514 this.getTotal = this.getJsonAccessor(s.totalProperty);
14516 if(s.successProperty) {
14517 this.getSuccess = this.getJsonAccessor(s.successProperty);
14519 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14521 var g = this.getJsonAccessor(s.id);
14522 this.getId = function(rec) {
14524 return (r === undefined || r === "") ? null : r;
14527 this.getId = function(){return null;};
14530 for(var jj = 0; jj < fl; jj++){
14532 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14533 this.ef[jj] = this.getJsonAccessor(map);
14537 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14538 if(s.totalProperty){
14539 var vt = parseInt(this.getTotal(o), 10);
14544 if(s.successProperty){
14545 var vs = this.getSuccess(o);
14546 if(vs === false || vs === 'false'){
14551 for(var i = 0; i < c; i++){
14554 var id = this.getId(n);
14555 for(var j = 0; j < fl; j++){
14557 var v = this.ef[j](n);
14559 Roo.log('missing convert for ' + f.name);
14563 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14565 var record = new Record(values, id);
14567 records[i] = record;
14573 totalRecords : totalRecords
14576 // used when loading children.. @see loadDataFromChildren
14577 toLoadData: function(rec)
14579 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14580 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14581 return { data : data, total : data.length };
14586 * Ext JS Library 1.1.1
14587 * Copyright(c) 2006-2007, Ext JS, LLC.
14589 * Originally Released Under LGPL - original licence link has changed is not relivant.
14592 * <script type="text/javascript">
14596 * @class Roo.data.ArrayReader
14597 * @extends Roo.data.DataReader
14598 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14599 * Each element of that Array represents a row of data fields. The
14600 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14601 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14605 var RecordDef = Roo.data.Record.create([
14606 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14607 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14609 var myReader = new Roo.data.ArrayReader({
14610 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14614 * This would consume an Array like this:
14616 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14620 * Create a new JsonReader
14621 * @param {Object} meta Metadata configuration options.
14622 * @param {Object|Array} recordType Either an Array of field definition objects
14624 * @cfg {Array} fields Array of field definition objects
14625 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14626 * as specified to {@link Roo.data.Record#create},
14627 * or an {@link Roo.data.Record} object
14630 * created using {@link Roo.data.Record#create}.
14632 Roo.data.ArrayReader = function(meta, recordType)
14634 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14637 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14640 * Create a data block containing Roo.data.Records from an XML document.
14641 * @param {Object} o An Array of row objects which represents the dataset.
14642 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14643 * a cache of Roo.data.Records.
14645 readRecords : function(o)
14647 var sid = this.meta ? this.meta.id : null;
14648 var recordType = this.recordType, fields = recordType.prototype.fields;
14651 for(var i = 0; i < root.length; i++){
14654 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14655 for(var j = 0, jlen = fields.length; j < jlen; j++){
14656 var f = fields.items[j];
14657 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14658 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14660 values[f.name] = v;
14662 var record = new recordType(values, id);
14664 records[records.length] = record;
14668 totalRecords : records.length
14671 // used when loading children.. @see loadDataFromChildren
14672 toLoadData: function(rec)
14674 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14675 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14686 * @class Roo.bootstrap.ComboBox
14687 * @extends Roo.bootstrap.TriggerField
14688 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14689 * @cfg {Boolean} append (true|false) default false
14690 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14691 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14692 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14693 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14694 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14695 * @cfg {Boolean} animate default true
14696 * @cfg {Boolean} emptyResultText only for touch device
14697 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14698 * @cfg {String} emptyTitle default ''
14700 * Create a new ComboBox.
14701 * @param {Object} config Configuration options
14703 Roo.bootstrap.ComboBox = function(config){
14704 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14708 * Fires when the dropdown list is expanded
14709 * @param {Roo.bootstrap.ComboBox} combo This combo box
14714 * Fires when the dropdown list is collapsed
14715 * @param {Roo.bootstrap.ComboBox} combo This combo box
14719 * @event beforeselect
14720 * Fires before a list item is selected. Return false to cancel the selection.
14721 * @param {Roo.bootstrap.ComboBox} combo This combo box
14722 * @param {Roo.data.Record} record The data record returned from the underlying store
14723 * @param {Number} index The index of the selected item in the dropdown list
14725 'beforeselect' : true,
14728 * Fires when a list item is selected
14729 * @param {Roo.bootstrap.ComboBox} combo This combo box
14730 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14731 * @param {Number} index The index of the selected item in the dropdown list
14735 * @event beforequery
14736 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14737 * The event object passed has these properties:
14738 * @param {Roo.bootstrap.ComboBox} combo This combo box
14739 * @param {String} query The query
14740 * @param {Boolean} forceAll true to force "all" query
14741 * @param {Boolean} cancel true to cancel the query
14742 * @param {Object} e The query event object
14744 'beforequery': true,
14747 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14748 * @param {Roo.bootstrap.ComboBox} combo This combo box
14753 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14754 * @param {Roo.bootstrap.ComboBox} combo This combo box
14755 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14760 * Fires when the remove value from the combobox array
14761 * @param {Roo.bootstrap.ComboBox} combo This combo box
14765 * @event afterremove
14766 * Fires when the remove value from the combobox array
14767 * @param {Roo.bootstrap.ComboBox} combo This combo box
14769 'afterremove' : true,
14771 * @event specialfilter
14772 * Fires when specialfilter
14773 * @param {Roo.bootstrap.ComboBox} combo This combo box
14775 'specialfilter' : true,
14778 * Fires when tick the element
14779 * @param {Roo.bootstrap.ComboBox} combo This combo box
14783 * @event touchviewdisplay
14784 * Fires when touch view require special display (default is using displayField)
14785 * @param {Roo.bootstrap.ComboBox} combo This combo box
14786 * @param {Object} cfg set html .
14788 'touchviewdisplay' : true
14793 this.tickItems = [];
14795 this.selectedIndex = -1;
14796 if(this.mode == 'local'){
14797 if(config.queryDelay === undefined){
14798 this.queryDelay = 10;
14800 if(config.minChars === undefined){
14806 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14809 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14810 * rendering into an Roo.Editor, defaults to false)
14813 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14814 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14817 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14820 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14821 * the dropdown list (defaults to undefined, with no header element)
14825 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
14829 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14831 listWidth: undefined,
14833 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14834 * mode = 'remote' or 'text' if mode = 'local')
14836 displayField: undefined,
14839 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14840 * mode = 'remote' or 'value' if mode = 'local').
14841 * Note: use of a valueField requires the user make a selection
14842 * in order for a value to be mapped.
14844 valueField: undefined,
14846 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14851 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14852 * field's data value (defaults to the underlying DOM element's name)
14854 hiddenName: undefined,
14856 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14860 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14862 selectedClass: 'active',
14865 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14869 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14870 * anchor positions (defaults to 'tl-bl')
14872 listAlign: 'tl-bl?',
14874 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14878 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
14879 * query specified by the allQuery config option (defaults to 'query')
14881 triggerAction: 'query',
14883 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14884 * (defaults to 4, does not apply if editable = false)
14888 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14889 * delay (typeAheadDelay) if it matches a known value (defaults to false)
14893 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14894 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14898 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14899 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
14903 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
14904 * when editable = true (defaults to false)
14906 selectOnFocus:false,
14908 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14910 queryParam: 'query',
14912 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
14913 * when mode = 'remote' (defaults to 'Loading...')
14915 loadingText: 'Loading...',
14917 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14921 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14925 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14926 * traditional select (defaults to true)
14930 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14934 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14938 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14939 * listWidth has a higher value)
14943 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14944 * allow the user to set arbitrary text into the field (defaults to false)
14946 forceSelection:false,
14948 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14949 * if typeAhead = true (defaults to 250)
14951 typeAheadDelay : 250,
14953 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14954 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14956 valueNotFoundText : undefined,
14958 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14960 blockFocus : false,
14963 * @cfg {Boolean} disableClear Disable showing of clear button.
14965 disableClear : false,
14967 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
14969 alwaysQuery : false,
14972 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
14977 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14979 invalidClass : "has-warning",
14982 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14984 validClass : "has-success",
14987 * @cfg {Boolean} specialFilter (true|false) special filter default false
14989 specialFilter : false,
14992 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14994 mobileTouchView : true,
14997 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14999 useNativeIOS : false,
15002 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15004 mobile_restrict_height : false,
15006 ios_options : false,
15018 btnPosition : 'right',
15019 triggerList : true,
15020 showToggleBtn : true,
15022 emptyResultText: 'Empty',
15023 triggerText : 'Select',
15026 // element that contains real text value.. (when hidden is used..)
15028 getAutoCreate : function()
15033 * Render classic select for iso
15036 if(Roo.isIOS && this.useNativeIOS){
15037 cfg = this.getAutoCreateNativeIOS();
15045 if(Roo.isTouch && this.mobileTouchView){
15046 cfg = this.getAutoCreateTouchView();
15053 if(!this.tickable){
15054 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15059 * ComboBox with tickable selections
15062 var align = this.labelAlign || this.parentLabelAlign();
15065 cls : 'form-group roo-combobox-tickable' //input-group
15068 var btn_text_select = '';
15069 var btn_text_done = '';
15070 var btn_text_cancel = '';
15072 if (this.btn_text_show) {
15073 btn_text_select = 'Select';
15074 btn_text_done = 'Done';
15075 btn_text_cancel = 'Cancel';
15080 cls : 'tickable-buttons',
15085 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15086 //html : this.triggerText
15087 html: btn_text_select
15093 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15095 html: btn_text_done
15101 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15103 html: btn_text_cancel
15109 buttons.cn.unshift({
15111 cls: 'roo-select2-search-field-input'
15117 Roo.each(buttons.cn, function(c){
15119 c.cls += ' btn-' + _this.size;
15122 if (_this.disabled) {
15129 style : 'display: contents',
15134 cls: 'form-hidden-field'
15138 cls: 'roo-select2-choices',
15142 cls: 'roo-select2-search-field',
15153 cls: 'roo-select2-container input-group roo-select2-container-multi',
15159 // cls: 'typeahead typeahead-long dropdown-menu',
15160 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15165 if(this.hasFeedback && !this.allowBlank){
15169 cls: 'glyphicon form-control-feedback'
15172 combobox.cn.push(feedback);
15179 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15180 tooltip : 'This field is required'
15182 if (Roo.bootstrap.version == 4) {
15185 style : 'display:none'
15188 if (align ==='left' && this.fieldLabel.length) {
15190 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15197 cls : 'control-label col-form-label',
15198 html : this.fieldLabel
15210 var labelCfg = cfg.cn[1];
15211 var contentCfg = cfg.cn[2];
15214 if(this.indicatorpos == 'right'){
15220 cls : 'control-label col-form-label',
15224 html : this.fieldLabel
15240 labelCfg = cfg.cn[0];
15241 contentCfg = cfg.cn[1];
15245 if(this.labelWidth > 12){
15246 labelCfg.style = "width: " + this.labelWidth + 'px';
15249 if(this.labelWidth < 13 && this.labelmd == 0){
15250 this.labelmd = this.labelWidth;
15253 if(this.labellg > 0){
15254 labelCfg.cls += ' col-lg-' + this.labellg;
15255 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15258 if(this.labelmd > 0){
15259 labelCfg.cls += ' col-md-' + this.labelmd;
15260 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15263 if(this.labelsm > 0){
15264 labelCfg.cls += ' col-sm-' + this.labelsm;
15265 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15268 if(this.labelxs > 0){
15269 labelCfg.cls += ' col-xs-' + this.labelxs;
15270 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15274 } else if ( this.fieldLabel.length) {
15275 // Roo.log(" label");
15280 //cls : 'input-group-addon',
15281 html : this.fieldLabel
15286 if(this.indicatorpos == 'right'){
15290 //cls : 'input-group-addon',
15291 html : this.fieldLabel
15301 // Roo.log(" no label && no align");
15308 ['xs','sm','md','lg'].map(function(size){
15309 if (settings[size]) {
15310 cfg.cls += ' col-' + size + '-' + settings[size];
15318 _initEventsCalled : false,
15321 initEvents: function()
15323 if (this._initEventsCalled) { // as we call render... prevent looping...
15326 this._initEventsCalled = true;
15329 throw "can not find store for combo";
15332 this.indicator = this.indicatorEl();
15334 this.store = Roo.factory(this.store, Roo.data);
15335 this.store.parent = this;
15337 // if we are building from html. then this element is so complex, that we can not really
15338 // use the rendered HTML.
15339 // so we have to trash and replace the previous code.
15340 if (Roo.XComponent.build_from_html) {
15341 // remove this element....
15342 var e = this.el.dom, k=0;
15343 while (e ) { e = e.previousSibling; ++k;}
15348 this.rendered = false;
15350 this.render(this.parent().getChildContainer(true), k);
15353 if(Roo.isIOS && this.useNativeIOS){
15354 this.initIOSView();
15362 if(Roo.isTouch && this.mobileTouchView){
15363 this.initTouchView();
15368 this.initTickableEvents();
15372 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15374 if(this.hiddenName){
15376 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15378 this.hiddenField.dom.value =
15379 this.hiddenValue !== undefined ? this.hiddenValue :
15380 this.value !== undefined ? this.value : '';
15382 // prevent input submission
15383 this.el.dom.removeAttribute('name');
15384 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15389 // this.el.dom.setAttribute('autocomplete', 'off');
15392 var cls = 'x-combo-list';
15394 //this.list = new Roo.Layer({
15395 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15401 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15402 _this.list.setWidth(lw);
15405 this.list.on('mouseover', this.onViewOver, this);
15406 this.list.on('mousemove', this.onViewMove, this);
15407 this.list.on('scroll', this.onViewScroll, this);
15410 this.list.swallowEvent('mousewheel');
15411 this.assetHeight = 0;
15414 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15415 this.assetHeight += this.header.getHeight();
15418 this.innerList = this.list.createChild({cls:cls+'-inner'});
15419 this.innerList.on('mouseover', this.onViewOver, this);
15420 this.innerList.on('mousemove', this.onViewMove, this);
15421 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15423 if(this.allowBlank && !this.pageSize && !this.disableClear){
15424 this.footer = this.list.createChild({cls:cls+'-ft'});
15425 this.pageTb = new Roo.Toolbar(this.footer);
15429 this.footer = this.list.createChild({cls:cls+'-ft'});
15430 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15431 {pageSize: this.pageSize});
15435 if (this.pageTb && this.allowBlank && !this.disableClear) {
15437 this.pageTb.add(new Roo.Toolbar.Fill(), {
15438 cls: 'x-btn-icon x-btn-clear',
15440 handler: function()
15443 _this.clearValue();
15444 _this.onSelect(false, -1);
15449 this.assetHeight += this.footer.getHeight();
15454 this.tpl = Roo.bootstrap.version == 4 ?
15455 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15456 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15459 this.view = new Roo.View(this.list, this.tpl, {
15460 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15462 //this.view.wrapEl.setDisplayed(false);
15463 this.view.on('click', this.onViewClick, this);
15466 this.store.on('beforeload', this.onBeforeLoad, this);
15467 this.store.on('load', this.onLoad, this);
15468 this.store.on('loadexception', this.onLoadException, this);
15470 if(this.resizable){
15471 this.resizer = new Roo.Resizable(this.list, {
15472 pinned:true, handles:'se'
15474 this.resizer.on('resize', function(r, w, h){
15475 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15476 this.listWidth = w;
15477 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15478 this.restrictHeight();
15480 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15483 if(!this.editable){
15484 this.editable = true;
15485 this.setEditable(false);
15490 if (typeof(this.events.add.listeners) != 'undefined') {
15492 this.addicon = this.wrap.createChild(
15493 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15495 this.addicon.on('click', function(e) {
15496 this.fireEvent('add', this);
15499 if (typeof(this.events.edit.listeners) != 'undefined') {
15501 this.editicon = this.wrap.createChild(
15502 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15503 if (this.addicon) {
15504 this.editicon.setStyle('margin-left', '40px');
15506 this.editicon.on('click', function(e) {
15508 // we fire even if inothing is selected..
15509 this.fireEvent('edit', this, this.lastData );
15515 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15516 "up" : function(e){
15517 this.inKeyMode = true;
15521 "down" : function(e){
15522 if(!this.isExpanded()){
15523 this.onTriggerClick();
15525 this.inKeyMode = true;
15530 "enter" : function(e){
15531 // this.onViewClick();
15535 if(this.fireEvent("specialkey", this, e)){
15536 this.onViewClick(false);
15542 "esc" : function(e){
15546 "tab" : function(e){
15549 if(this.fireEvent("specialkey", this, e)){
15550 this.onViewClick(false);
15558 doRelay : function(foo, bar, hname){
15559 if(hname == 'down' || this.scope.isExpanded()){
15560 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15569 this.queryDelay = Math.max(this.queryDelay || 10,
15570 this.mode == 'local' ? 10 : 250);
15573 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15575 if(this.typeAhead){
15576 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15578 if(this.editable !== false){
15579 this.inputEl().on("keyup", this.onKeyUp, this);
15581 if(this.forceSelection){
15582 this.inputEl().on('blur', this.doForce, this);
15586 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15587 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15591 initTickableEvents: function()
15595 if(this.hiddenName){
15597 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15599 this.hiddenField.dom.value =
15600 this.hiddenValue !== undefined ? this.hiddenValue :
15601 this.value !== undefined ? this.value : '';
15603 // prevent input submission
15604 this.el.dom.removeAttribute('name');
15605 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15610 // this.list = this.el.select('ul.dropdown-menu',true).first();
15612 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15613 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15614 if(this.triggerList){
15615 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15618 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15619 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15621 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15622 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15624 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15625 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15627 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15628 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15629 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15632 this.cancelBtn.hide();
15637 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15638 _this.list.setWidth(lw);
15641 this.list.on('mouseover', this.onViewOver, this);
15642 this.list.on('mousemove', this.onViewMove, this);
15644 this.list.on('scroll', this.onViewScroll, this);
15647 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15648 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15651 this.view = new Roo.View(this.list, this.tpl, {
15656 selectedClass: this.selectedClass
15659 //this.view.wrapEl.setDisplayed(false);
15660 this.view.on('click', this.onViewClick, this);
15664 this.store.on('beforeload', this.onBeforeLoad, this);
15665 this.store.on('load', this.onLoad, this);
15666 this.store.on('loadexception', this.onLoadException, this);
15669 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15670 "up" : function(e){
15671 this.inKeyMode = true;
15675 "down" : function(e){
15676 this.inKeyMode = true;
15680 "enter" : function(e){
15681 if(this.fireEvent("specialkey", this, e)){
15682 this.onViewClick(false);
15688 "esc" : function(e){
15689 this.onTickableFooterButtonClick(e, false, false);
15692 "tab" : function(e){
15693 this.fireEvent("specialkey", this, e);
15695 this.onTickableFooterButtonClick(e, false, false);
15702 doRelay : function(e, fn, key){
15703 if(this.scope.isExpanded()){
15704 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15713 this.queryDelay = Math.max(this.queryDelay || 10,
15714 this.mode == 'local' ? 10 : 250);
15717 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15719 if(this.typeAhead){
15720 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15723 if(this.editable !== false){
15724 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15727 this.indicator = this.indicatorEl();
15729 if(this.indicator){
15730 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15731 this.indicator.hide();
15736 onDestroy : function(){
15738 this.view.setStore(null);
15739 this.view.el.removeAllListeners();
15740 this.view.el.remove();
15741 this.view.purgeListeners();
15744 this.list.dom.innerHTML = '';
15748 this.store.un('beforeload', this.onBeforeLoad, this);
15749 this.store.un('load', this.onLoad, this);
15750 this.store.un('loadexception', this.onLoadException, this);
15752 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15756 fireKey : function(e){
15757 if(e.isNavKeyPress() && !this.list.isVisible()){
15758 this.fireEvent("specialkey", this, e);
15763 onResize: function(w, h){
15764 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15766 // if(typeof w != 'number'){
15767 // // we do not handle it!?!?
15770 // var tw = this.trigger.getWidth();
15771 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15772 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15774 // this.inputEl().setWidth( this.adjustWidth('input', x));
15776 // //this.trigger.setStyle('left', x+'px');
15778 // if(this.list && this.listWidth === undefined){
15779 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15780 // this.list.setWidth(lw);
15781 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15789 * Allow or prevent the user from directly editing the field text. If false is passed,
15790 * the user will only be able to select from the items defined in the dropdown list. This method
15791 * is the runtime equivalent of setting the 'editable' config option at config time.
15792 * @param {Boolean} value True to allow the user to directly edit the field text
15794 setEditable : function(value){
15795 if(value == this.editable){
15798 this.editable = value;
15800 this.inputEl().dom.setAttribute('readOnly', true);
15801 this.inputEl().on('mousedown', this.onTriggerClick, this);
15802 this.inputEl().addClass('x-combo-noedit');
15804 this.inputEl().dom.setAttribute('readOnly', false);
15805 this.inputEl().un('mousedown', this.onTriggerClick, this);
15806 this.inputEl().removeClass('x-combo-noedit');
15812 onBeforeLoad : function(combo,opts){
15813 if(!this.hasFocus){
15817 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15819 this.restrictHeight();
15820 this.selectedIndex = -1;
15824 onLoad : function(){
15826 this.hasQuery = false;
15828 if(!this.hasFocus){
15832 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15833 this.loading.hide();
15836 if(this.store.getCount() > 0){
15839 this.restrictHeight();
15840 if(this.lastQuery == this.allQuery){
15841 if(this.editable && !this.tickable){
15842 this.inputEl().dom.select();
15846 !this.selectByValue(this.value, true) &&
15849 !this.store.lastOptions ||
15850 typeof(this.store.lastOptions.add) == 'undefined' ||
15851 this.store.lastOptions.add != true
15854 this.select(0, true);
15857 if(this.autoFocus){
15860 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15861 this.taTask.delay(this.typeAheadDelay);
15865 this.onEmptyResults();
15871 onLoadException : function()
15873 this.hasQuery = false;
15875 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15876 this.loading.hide();
15879 if(this.tickable && this.editable){
15884 // only causes errors at present
15885 //Roo.log(this.store.reader.jsonData);
15886 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15888 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15894 onTypeAhead : function(){
15895 if(this.store.getCount() > 0){
15896 var r = this.store.getAt(0);
15897 var newValue = r.data[this.displayField];
15898 var len = newValue.length;
15899 var selStart = this.getRawValue().length;
15901 if(selStart != len){
15902 this.setRawValue(newValue);
15903 this.selectText(selStart, newValue.length);
15909 onSelect : function(record, index){
15911 if(this.fireEvent('beforeselect', this, record, index) !== false){
15913 this.setFromData(index > -1 ? record.data : false);
15916 this.fireEvent('select', this, record, index);
15921 * Returns the currently selected field value or empty string if no value is set.
15922 * @return {String} value The selected value
15924 getValue : function()
15926 if(Roo.isIOS && this.useNativeIOS){
15927 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15931 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15934 if(this.valueField){
15935 return typeof this.value != 'undefined' ? this.value : '';
15937 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15941 getRawValue : function()
15943 if(Roo.isIOS && this.useNativeIOS){
15944 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15947 var v = this.inputEl().getValue();
15953 * Clears any text/value currently set in the field
15955 clearValue : function(){
15957 if(this.hiddenField){
15958 this.hiddenField.dom.value = '';
15961 this.setRawValue('');
15962 this.lastSelectionText = '';
15963 this.lastData = false;
15965 var close = this.closeTriggerEl();
15976 * Sets the specified value into the field. If the value finds a match, the corresponding record text
15977 * will be displayed in the field. If the value does not match the data value of an existing item,
15978 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15979 * Otherwise the field will be blank (although the value will still be set).
15980 * @param {String} value The value to match
15982 setValue : function(v)
15984 if(Roo.isIOS && this.useNativeIOS){
15985 this.setIOSValue(v);
15995 if(this.valueField){
15996 var r = this.findRecord(this.valueField, v);
15998 text = r.data[this.displayField];
15999 }else if(this.valueNotFoundText !== undefined){
16000 text = this.valueNotFoundText;
16003 this.lastSelectionText = text;
16004 if(this.hiddenField){
16005 this.hiddenField.dom.value = v;
16007 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16010 var close = this.closeTriggerEl();
16013 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16019 * @property {Object} the last set data for the element
16024 * Sets the value of the field based on a object which is related to the record format for the store.
16025 * @param {Object} value the value to set as. or false on reset?
16027 setFromData : function(o){
16034 var dv = ''; // display value
16035 var vv = ''; // value value..
16037 if (this.displayField) {
16038 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16040 // this is an error condition!!!
16041 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16044 if(this.valueField){
16045 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16048 var close = this.closeTriggerEl();
16051 if(dv.length || vv * 1 > 0){
16053 this.blockFocus=true;
16059 if(this.hiddenField){
16060 this.hiddenField.dom.value = vv;
16062 this.lastSelectionText = dv;
16063 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16067 // no hidden field.. - we store the value in 'value', but still display
16068 // display field!!!!
16069 this.lastSelectionText = dv;
16070 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16077 reset : function(){
16078 // overridden so that last data is reset..
16085 this.setValue(this.originalValue);
16086 //this.clearInvalid();
16087 this.lastData = false;
16089 this.view.clearSelections();
16095 findRecord : function(prop, value){
16097 if(this.store.getCount() > 0){
16098 this.store.each(function(r){
16099 if(r.data[prop] == value){
16109 getName: function()
16111 // returns hidden if it's set..
16112 if (!this.rendered) {return ''};
16113 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16117 onViewMove : function(e, t){
16118 this.inKeyMode = false;
16122 onViewOver : function(e, t){
16123 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16126 var item = this.view.findItemFromChild(t);
16129 var index = this.view.indexOf(item);
16130 this.select(index, false);
16135 onViewClick : function(view, doFocus, el, e)
16137 var index = this.view.getSelectedIndexes()[0];
16139 var r = this.store.getAt(index);
16143 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16150 Roo.each(this.tickItems, function(v,k){
16152 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16154 _this.tickItems.splice(k, 1);
16156 if(typeof(e) == 'undefined' && view == false){
16157 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16169 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16170 this.tickItems.push(r.data);
16173 if(typeof(e) == 'undefined' && view == false){
16174 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16181 this.onSelect(r, index);
16183 if(doFocus !== false && !this.blockFocus){
16184 this.inputEl().focus();
16189 restrictHeight : function(){
16190 //this.innerList.dom.style.height = '';
16191 //var inner = this.innerList.dom;
16192 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16193 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16194 //this.list.beginUpdate();
16195 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16196 this.list.alignTo(this.inputEl(), this.listAlign);
16197 this.list.alignTo(this.inputEl(), this.listAlign);
16198 //this.list.endUpdate();
16202 onEmptyResults : function(){
16204 if(this.tickable && this.editable){
16205 this.hasFocus = false;
16206 this.restrictHeight();
16214 * Returns true if the dropdown list is expanded, else false.
16216 isExpanded : function(){
16217 return this.list.isVisible();
16221 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16222 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16223 * @param {String} value The data value of the item to select
16224 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16225 * selected item if it is not currently in view (defaults to true)
16226 * @return {Boolean} True if the value matched an item in the list, else false
16228 selectByValue : function(v, scrollIntoView){
16229 if(v !== undefined && v !== null){
16230 var r = this.findRecord(this.valueField || this.displayField, v);
16232 this.select(this.store.indexOf(r), scrollIntoView);
16240 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16241 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16242 * @param {Number} index The zero-based index of the list item to select
16243 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16244 * selected item if it is not currently in view (defaults to true)
16246 select : function(index, scrollIntoView){
16247 this.selectedIndex = index;
16248 this.view.select(index);
16249 if(scrollIntoView !== false){
16250 var el = this.view.getNode(index);
16252 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16255 this.list.scrollChildIntoView(el, false);
16261 selectNext : function(){
16262 var ct = this.store.getCount();
16264 if(this.selectedIndex == -1){
16266 }else if(this.selectedIndex < ct-1){
16267 this.select(this.selectedIndex+1);
16273 selectPrev : function(){
16274 var ct = this.store.getCount();
16276 if(this.selectedIndex == -1){
16278 }else if(this.selectedIndex != 0){
16279 this.select(this.selectedIndex-1);
16285 onKeyUp : function(e){
16286 if(this.editable !== false && !e.isSpecialKey()){
16287 this.lastKey = e.getKey();
16288 this.dqTask.delay(this.queryDelay);
16293 validateBlur : function(){
16294 return !this.list || !this.list.isVisible();
16298 initQuery : function(){
16300 var v = this.getRawValue();
16302 if(this.tickable && this.editable){
16303 v = this.tickableInputEl().getValue();
16310 doForce : function(){
16311 if(this.inputEl().dom.value.length > 0){
16312 this.inputEl().dom.value =
16313 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16319 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16320 * query allowing the query action to be canceled if needed.
16321 * @param {String} query The SQL query to execute
16322 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16323 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16324 * saved in the current store (defaults to false)
16326 doQuery : function(q, forceAll){
16328 if(q === undefined || q === null){
16333 forceAll: forceAll,
16337 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16342 forceAll = qe.forceAll;
16343 if(forceAll === true || (q.length >= this.minChars)){
16345 this.hasQuery = true;
16347 if(this.lastQuery != q || this.alwaysQuery){
16348 this.lastQuery = q;
16349 if(this.mode == 'local'){
16350 this.selectedIndex = -1;
16352 this.store.clearFilter();
16355 if(this.specialFilter){
16356 this.fireEvent('specialfilter', this);
16361 this.store.filter(this.displayField, q);
16364 this.store.fireEvent("datachanged", this.store);
16371 this.store.baseParams[this.queryParam] = q;
16373 var options = {params : this.getParams(q)};
16376 options.add = true;
16377 options.params.start = this.page * this.pageSize;
16380 this.store.load(options);
16383 * this code will make the page width larger, at the beginning, the list not align correctly,
16384 * we should expand the list on onLoad
16385 * so command out it
16390 this.selectedIndex = -1;
16395 this.loadNext = false;
16399 getParams : function(q){
16401 //p[this.queryParam] = q;
16405 p.limit = this.pageSize;
16411 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16413 collapse : function(){
16414 if(!this.isExpanded()){
16420 this.hasFocus = false;
16424 this.cancelBtn.hide();
16425 this.trigger.show();
16428 this.tickableInputEl().dom.value = '';
16429 this.tickableInputEl().blur();
16434 Roo.get(document).un('mousedown', this.collapseIf, this);
16435 Roo.get(document).un('mousewheel', this.collapseIf, this);
16436 if (!this.editable) {
16437 Roo.get(document).un('keydown', this.listKeyPress, this);
16439 this.fireEvent('collapse', this);
16445 collapseIf : function(e){
16446 var in_combo = e.within(this.el);
16447 var in_list = e.within(this.list);
16448 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16450 if (in_combo || in_list || is_list) {
16451 //e.stopPropagation();
16456 this.onTickableFooterButtonClick(e, false, false);
16464 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16466 expand : function(){
16468 if(this.isExpanded() || !this.hasFocus){
16472 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16473 this.list.setWidth(lw);
16479 this.restrictHeight();
16483 this.tickItems = Roo.apply([], this.item);
16486 this.cancelBtn.show();
16487 this.trigger.hide();
16490 this.tickableInputEl().focus();
16495 Roo.get(document).on('mousedown', this.collapseIf, this);
16496 Roo.get(document).on('mousewheel', this.collapseIf, this);
16497 if (!this.editable) {
16498 Roo.get(document).on('keydown', this.listKeyPress, this);
16501 this.fireEvent('expand', this);
16505 // Implements the default empty TriggerField.onTriggerClick function
16506 onTriggerClick : function(e)
16508 Roo.log('trigger click');
16510 if(this.disabled || !this.triggerList){
16515 this.loadNext = false;
16517 if(this.isExpanded()){
16519 if (!this.blockFocus) {
16520 this.inputEl().focus();
16524 this.hasFocus = true;
16525 if(this.triggerAction == 'all') {
16526 this.doQuery(this.allQuery, true);
16528 this.doQuery(this.getRawValue());
16530 if (!this.blockFocus) {
16531 this.inputEl().focus();
16536 onTickableTriggerClick : function(e)
16543 this.loadNext = false;
16544 this.hasFocus = true;
16546 if(this.triggerAction == 'all') {
16547 this.doQuery(this.allQuery, true);
16549 this.doQuery(this.getRawValue());
16553 onSearchFieldClick : function(e)
16555 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16556 this.onTickableFooterButtonClick(e, false, false);
16560 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16565 this.loadNext = false;
16566 this.hasFocus = true;
16568 if(this.triggerAction == 'all') {
16569 this.doQuery(this.allQuery, true);
16571 this.doQuery(this.getRawValue());
16575 listKeyPress : function(e)
16577 //Roo.log('listkeypress');
16578 // scroll to first matching element based on key pres..
16579 if (e.isSpecialKey()) {
16582 var k = String.fromCharCode(e.getKey()).toUpperCase();
16585 var csel = this.view.getSelectedNodes();
16586 var cselitem = false;
16588 var ix = this.view.indexOf(csel[0]);
16589 cselitem = this.store.getAt(ix);
16590 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16596 this.store.each(function(v) {
16598 // start at existing selection.
16599 if (cselitem.id == v.id) {
16605 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16606 match = this.store.indexOf(v);
16612 if (match === false) {
16613 return true; // no more action?
16616 this.view.select(match);
16617 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16618 sn.scrollIntoView(sn.dom.parentNode, false);
16621 onViewScroll : function(e, t){
16623 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){
16627 this.hasQuery = true;
16629 this.loading = this.list.select('.loading', true).first();
16631 if(this.loading === null){
16632 this.list.createChild({
16634 cls: 'loading roo-select2-more-results roo-select2-active',
16635 html: 'Loading more results...'
16638 this.loading = this.list.select('.loading', true).first();
16640 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16642 this.loading.hide();
16645 this.loading.show();
16650 this.loadNext = true;
16652 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16657 addItem : function(o)
16659 var dv = ''; // display value
16661 if (this.displayField) {
16662 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16664 // this is an error condition!!!
16665 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16672 var choice = this.choices.createChild({
16674 cls: 'roo-select2-search-choice',
16683 cls: 'roo-select2-search-choice-close fa fa-times',
16688 }, this.searchField);
16690 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16692 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16700 this.inputEl().dom.value = '';
16705 onRemoveItem : function(e, _self, o)
16707 e.preventDefault();
16709 this.lastItem = Roo.apply([], this.item);
16711 var index = this.item.indexOf(o.data) * 1;
16714 Roo.log('not this item?!');
16718 this.item.splice(index, 1);
16723 this.fireEvent('remove', this, e);
16729 syncValue : function()
16731 if(!this.item.length){
16738 Roo.each(this.item, function(i){
16739 if(_this.valueField){
16740 value.push(i[_this.valueField]);
16747 this.value = value.join(',');
16749 if(this.hiddenField){
16750 this.hiddenField.dom.value = this.value;
16753 this.store.fireEvent("datachanged", this.store);
16758 clearItem : function()
16760 if(!this.multiple){
16766 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16774 if(this.tickable && !Roo.isTouch){
16775 this.view.refresh();
16779 inputEl: function ()
16781 if(Roo.isIOS && this.useNativeIOS){
16782 return this.el.select('select.roo-ios-select', true).first();
16785 if(Roo.isTouch && this.mobileTouchView){
16786 return this.el.select('input.form-control',true).first();
16790 return this.searchField;
16793 return this.el.select('input.form-control',true).first();
16796 onTickableFooterButtonClick : function(e, btn, el)
16798 e.preventDefault();
16800 this.lastItem = Roo.apply([], this.item);
16802 if(btn && btn.name == 'cancel'){
16803 this.tickItems = Roo.apply([], this.item);
16812 Roo.each(this.tickItems, function(o){
16820 validate : function()
16822 if(this.getVisibilityEl().hasClass('hidden')){
16826 var v = this.getRawValue();
16829 v = this.getValue();
16832 if(this.disabled || this.allowBlank || v.length){
16837 this.markInvalid();
16841 tickableInputEl : function()
16843 if(!this.tickable || !this.editable){
16844 return this.inputEl();
16847 return this.inputEl().select('.roo-select2-search-field-input', true).first();
16851 getAutoCreateTouchView : function()
16856 cls: 'form-group' //input-group
16862 type : this.inputType,
16863 cls : 'form-control x-combo-noedit',
16864 autocomplete: 'new-password',
16865 placeholder : this.placeholder || '',
16870 input.name = this.name;
16874 input.cls += ' input-' + this.size;
16877 if (this.disabled) {
16878 input.disabled = true;
16889 inputblock.cls += ' input-group';
16891 inputblock.cn.unshift({
16893 cls : 'input-group-addon input-group-prepend input-group-text',
16898 if(this.removable && !this.multiple){
16899 inputblock.cls += ' roo-removable';
16901 inputblock.cn.push({
16904 cls : 'roo-combo-removable-btn close'
16908 if(this.hasFeedback && !this.allowBlank){
16910 inputblock.cls += ' has-feedback';
16912 inputblock.cn.push({
16914 cls: 'glyphicon form-control-feedback'
16921 inputblock.cls += (this.before) ? '' : ' input-group';
16923 inputblock.cn.push({
16925 cls : 'input-group-addon input-group-append input-group-text',
16931 var ibwrap = inputblock;
16936 cls: 'roo-select2-choices',
16940 cls: 'roo-select2-search-field',
16953 cls: 'roo-select2-container input-group roo-touchview-combobox ',
16958 cls: 'form-hidden-field'
16964 if(!this.multiple && this.showToggleBtn){
16970 if (this.caret != false) {
16973 cls: 'fa fa-' + this.caret
16980 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16982 Roo.bootstrap.version == 3 ? caret : '',
16985 cls: 'combobox-clear',
16999 combobox.cls += ' roo-select2-container-multi';
17002 var align = this.labelAlign || this.parentLabelAlign();
17004 if (align ==='left' && this.fieldLabel.length) {
17009 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17010 tooltip : 'This field is required'
17014 cls : 'control-label col-form-label',
17015 html : this.fieldLabel
17026 var labelCfg = cfg.cn[1];
17027 var contentCfg = cfg.cn[2];
17030 if(this.indicatorpos == 'right'){
17035 cls : 'control-label col-form-label',
17039 html : this.fieldLabel
17043 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17044 tooltip : 'This field is required'
17057 labelCfg = cfg.cn[0];
17058 contentCfg = cfg.cn[1];
17063 if(this.labelWidth > 12){
17064 labelCfg.style = "width: " + this.labelWidth + 'px';
17067 if(this.labelWidth < 13 && this.labelmd == 0){
17068 this.labelmd = this.labelWidth;
17071 if(this.labellg > 0){
17072 labelCfg.cls += ' col-lg-' + this.labellg;
17073 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17076 if(this.labelmd > 0){
17077 labelCfg.cls += ' col-md-' + this.labelmd;
17078 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17081 if(this.labelsm > 0){
17082 labelCfg.cls += ' col-sm-' + this.labelsm;
17083 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17086 if(this.labelxs > 0){
17087 labelCfg.cls += ' col-xs-' + this.labelxs;
17088 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17092 } else if ( this.fieldLabel.length) {
17096 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17097 tooltip : 'This field is required'
17101 cls : 'control-label',
17102 html : this.fieldLabel
17113 if(this.indicatorpos == 'right'){
17117 cls : 'control-label',
17118 html : this.fieldLabel,
17122 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17123 tooltip : 'This field is required'
17140 var settings = this;
17142 ['xs','sm','md','lg'].map(function(size){
17143 if (settings[size]) {
17144 cfg.cls += ' col-' + size + '-' + settings[size];
17151 initTouchView : function()
17153 this.renderTouchView();
17155 this.touchViewEl.on('scroll', function(){
17156 this.el.dom.scrollTop = 0;
17159 this.originalValue = this.getValue();
17161 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17163 this.inputEl().on("click", this.showTouchView, this);
17164 if (this.triggerEl) {
17165 this.triggerEl.on("click", this.showTouchView, this);
17169 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17170 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17172 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17174 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17175 this.store.on('load', this.onTouchViewLoad, this);
17176 this.store.on('loadexception', this.onTouchViewLoadException, this);
17178 if(this.hiddenName){
17180 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17182 this.hiddenField.dom.value =
17183 this.hiddenValue !== undefined ? this.hiddenValue :
17184 this.value !== undefined ? this.value : '';
17186 this.el.dom.removeAttribute('name');
17187 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17191 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17192 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17195 if(this.removable && !this.multiple){
17196 var close = this.closeTriggerEl();
17198 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17199 close.on('click', this.removeBtnClick, this, close);
17203 * fix the bug in Safari iOS8
17205 this.inputEl().on("focus", function(e){
17206 document.activeElement.blur();
17209 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17216 renderTouchView : function()
17218 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17219 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17221 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17222 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17224 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17225 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17226 this.touchViewBodyEl.setStyle('overflow', 'auto');
17228 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17229 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17231 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17232 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17236 showTouchView : function()
17242 this.touchViewHeaderEl.hide();
17244 if(this.modalTitle.length){
17245 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17246 this.touchViewHeaderEl.show();
17249 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17250 this.touchViewEl.show();
17252 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17254 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17255 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17257 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17259 if(this.modalTitle.length){
17260 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17263 this.touchViewBodyEl.setHeight(bodyHeight);
17267 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17269 this.touchViewEl.addClass('in');
17272 if(this._touchViewMask){
17273 Roo.get(document.body).addClass("x-body-masked");
17274 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17275 this._touchViewMask.setStyle('z-index', 10000);
17276 this._touchViewMask.addClass('show');
17279 this.doTouchViewQuery();
17283 hideTouchView : function()
17285 this.touchViewEl.removeClass('in');
17289 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17291 this.touchViewEl.setStyle('display', 'none');
17294 if(this._touchViewMask){
17295 this._touchViewMask.removeClass('show');
17296 Roo.get(document.body).removeClass("x-body-masked");
17300 setTouchViewValue : function()
17307 Roo.each(this.tickItems, function(o){
17312 this.hideTouchView();
17315 doTouchViewQuery : function()
17324 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17328 if(!this.alwaysQuery || this.mode == 'local'){
17329 this.onTouchViewLoad();
17336 onTouchViewBeforeLoad : function(combo,opts)
17342 onTouchViewLoad : function()
17344 if(this.store.getCount() < 1){
17345 this.onTouchViewEmptyResults();
17349 this.clearTouchView();
17351 var rawValue = this.getRawValue();
17353 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17355 this.tickItems = [];
17357 this.store.data.each(function(d, rowIndex){
17358 var row = this.touchViewListGroup.createChild(template);
17360 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17361 row.addClass(d.data.cls);
17364 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17367 html : d.data[this.displayField]
17370 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17371 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17374 row.removeClass('selected');
17375 if(!this.multiple && this.valueField &&
17376 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17379 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17380 row.addClass('selected');
17383 if(this.multiple && this.valueField &&
17384 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17388 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17389 this.tickItems.push(d.data);
17392 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17396 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17398 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17400 if(this.modalTitle.length){
17401 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17404 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17406 if(this.mobile_restrict_height && listHeight < bodyHeight){
17407 this.touchViewBodyEl.setHeight(listHeight);
17412 if(firstChecked && listHeight > bodyHeight){
17413 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17418 onTouchViewLoadException : function()
17420 this.hideTouchView();
17423 onTouchViewEmptyResults : function()
17425 this.clearTouchView();
17427 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17429 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17433 clearTouchView : function()
17435 this.touchViewListGroup.dom.innerHTML = '';
17438 onTouchViewClick : function(e, el, o)
17440 e.preventDefault();
17443 var rowIndex = o.rowIndex;
17445 var r = this.store.getAt(rowIndex);
17447 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17449 if(!this.multiple){
17450 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17451 c.dom.removeAttribute('checked');
17454 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17456 this.setFromData(r.data);
17458 var close = this.closeTriggerEl();
17464 this.hideTouchView();
17466 this.fireEvent('select', this, r, rowIndex);
17471 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17472 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17473 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17477 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17478 this.addItem(r.data);
17479 this.tickItems.push(r.data);
17483 getAutoCreateNativeIOS : function()
17486 cls: 'form-group' //input-group,
17491 cls : 'roo-ios-select'
17495 combobox.name = this.name;
17498 if (this.disabled) {
17499 combobox.disabled = true;
17502 var settings = this;
17504 ['xs','sm','md','lg'].map(function(size){
17505 if (settings[size]) {
17506 cfg.cls += ' col-' + size + '-' + settings[size];
17516 initIOSView : function()
17518 this.store.on('load', this.onIOSViewLoad, this);
17523 onIOSViewLoad : function()
17525 if(this.store.getCount() < 1){
17529 this.clearIOSView();
17531 if(this.allowBlank) {
17533 var default_text = '-- SELECT --';
17535 if(this.placeholder.length){
17536 default_text = this.placeholder;
17539 if(this.emptyTitle.length){
17540 default_text += ' - ' + this.emptyTitle + ' -';
17543 var opt = this.inputEl().createChild({
17546 html : default_text
17550 o[this.valueField] = 0;
17551 o[this.displayField] = default_text;
17553 this.ios_options.push({
17560 this.store.data.each(function(d, rowIndex){
17564 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17565 html = d.data[this.displayField];
17570 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17571 value = d.data[this.valueField];
17580 if(this.value == d.data[this.valueField]){
17581 option['selected'] = true;
17584 var opt = this.inputEl().createChild(option);
17586 this.ios_options.push({
17593 this.inputEl().on('change', function(){
17594 this.fireEvent('select', this);
17599 clearIOSView: function()
17601 this.inputEl().dom.innerHTML = '';
17603 this.ios_options = [];
17606 setIOSValue: function(v)
17610 if(!this.ios_options){
17614 Roo.each(this.ios_options, function(opts){
17616 opts.el.dom.removeAttribute('selected');
17618 if(opts.data[this.valueField] != v){
17622 opts.el.dom.setAttribute('selected', true);
17628 * @cfg {Boolean} grow
17632 * @cfg {Number} growMin
17636 * @cfg {Number} growMax
17645 Roo.apply(Roo.bootstrap.ComboBox, {
17649 cls: 'modal-header',
17671 cls: 'list-group-item',
17675 cls: 'roo-combobox-list-group-item-value'
17679 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17693 listItemCheckbox : {
17695 cls: 'list-group-item',
17699 cls: 'roo-combobox-list-group-item-value'
17703 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17719 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17724 cls: 'modal-footer',
17732 cls: 'col-xs-6 text-left',
17735 cls: 'btn btn-danger roo-touch-view-cancel',
17741 cls: 'col-xs-6 text-right',
17744 cls: 'btn btn-success roo-touch-view-ok',
17755 Roo.apply(Roo.bootstrap.ComboBox, {
17757 touchViewTemplate : {
17759 cls: 'modal fade roo-combobox-touch-view',
17763 cls: 'modal-dialog',
17764 style : 'position:fixed', // we have to fix position....
17768 cls: 'modal-content',
17770 Roo.bootstrap.ComboBox.header,
17771 Roo.bootstrap.ComboBox.body,
17772 Roo.bootstrap.ComboBox.footer
17781 * Ext JS Library 1.1.1
17782 * Copyright(c) 2006-2007, Ext JS, LLC.
17784 * Originally Released Under LGPL - original licence link has changed is not relivant.
17787 * <script type="text/javascript">
17792 * @extends Roo.util.Observable
17793 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17794 * This class also supports single and multi selection modes. <br>
17795 * Create a data model bound view:
17797 var store = new Roo.data.Store(...);
17799 var view = new Roo.View({
17801 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
17803 singleSelect: true,
17804 selectedClass: "ydataview-selected",
17808 // listen for node click?
17809 view.on("click", function(vw, index, node, e){
17810 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17814 dataModel.load("foobar.xml");
17816 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17818 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17819 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17821 * Note: old style constructor is still suported (container, template, config)
17824 * Create a new View
17825 * @param {Object} config The config object
17828 Roo.View = function(config, depreciated_tpl, depreciated_config){
17830 this.parent = false;
17832 if (typeof(depreciated_tpl) == 'undefined') {
17833 // new way.. - universal constructor.
17834 Roo.apply(this, config);
17835 this.el = Roo.get(this.el);
17838 this.el = Roo.get(config);
17839 this.tpl = depreciated_tpl;
17840 Roo.apply(this, depreciated_config);
17842 this.wrapEl = this.el.wrap().wrap();
17843 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17846 if(typeof(this.tpl) == "string"){
17847 this.tpl = new Roo.Template(this.tpl);
17849 // support xtype ctors..
17850 this.tpl = new Roo.factory(this.tpl, Roo);
17854 this.tpl.compile();
17859 * @event beforeclick
17860 * Fires before a click is processed. Returns false to cancel the default action.
17861 * @param {Roo.View} this
17862 * @param {Number} index The index of the target node
17863 * @param {HTMLElement} node The target node
17864 * @param {Roo.EventObject} e The raw event object
17866 "beforeclick" : true,
17869 * Fires when a template node is clicked.
17870 * @param {Roo.View} this
17871 * @param {Number} index The index of the target node
17872 * @param {HTMLElement} node The target node
17873 * @param {Roo.EventObject} e The raw event object
17878 * Fires when a template node is double clicked.
17879 * @param {Roo.View} this
17880 * @param {Number} index The index of the target node
17881 * @param {HTMLElement} node The target node
17882 * @param {Roo.EventObject} e The raw event object
17886 * @event contextmenu
17887 * Fires when a template node is right clicked.
17888 * @param {Roo.View} this
17889 * @param {Number} index The index of the target node
17890 * @param {HTMLElement} node The target node
17891 * @param {Roo.EventObject} e The raw event object
17893 "contextmenu" : true,
17895 * @event selectionchange
17896 * Fires when the selected nodes change.
17897 * @param {Roo.View} this
17898 * @param {Array} selections Array of the selected nodes
17900 "selectionchange" : true,
17903 * @event beforeselect
17904 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17905 * @param {Roo.View} this
17906 * @param {HTMLElement} node The node to be selected
17907 * @param {Array} selections Array of currently selected nodes
17909 "beforeselect" : true,
17911 * @event preparedata
17912 * Fires on every row to render, to allow you to change the data.
17913 * @param {Roo.View} this
17914 * @param {Object} data to be rendered (change this)
17916 "preparedata" : true
17924 "click": this.onClick,
17925 "dblclick": this.onDblClick,
17926 "contextmenu": this.onContextMenu,
17930 this.selections = [];
17932 this.cmp = new Roo.CompositeElementLite([]);
17934 this.store = Roo.factory(this.store, Roo.data);
17935 this.setStore(this.store, true);
17938 if ( this.footer && this.footer.xtype) {
17940 var fctr = this.wrapEl.appendChild(document.createElement("div"));
17942 this.footer.dataSource = this.store;
17943 this.footer.container = fctr;
17944 this.footer = Roo.factory(this.footer, Roo);
17945 fctr.insertFirst(this.el);
17947 // this is a bit insane - as the paging toolbar seems to detach the el..
17948 // dom.parentNode.parentNode.parentNode
17949 // they get detached?
17953 Roo.View.superclass.constructor.call(this);
17958 Roo.extend(Roo.View, Roo.util.Observable, {
17961 * @cfg {Roo.data.Store} store Data store to load data from.
17966 * @cfg {String|Roo.Element} el The container element.
17971 * @cfg {String|Roo.Template} tpl The template used by this View
17975 * @cfg {String} dataName the named area of the template to use as the data area
17976 * Works with domtemplates roo-name="name"
17980 * @cfg {String} selectedClass The css class to add to selected nodes
17982 selectedClass : "x-view-selected",
17984 * @cfg {String} emptyText The empty text to show when nothing is loaded.
17989 * @cfg {String} text to display on mask (default Loading)
17993 * @cfg {Boolean} multiSelect Allow multiple selection
17995 multiSelect : false,
17997 * @cfg {Boolean} singleSelect Allow single selection
17999 singleSelect: false,
18002 * @cfg {Boolean} toggleSelect - selecting
18004 toggleSelect : false,
18007 * @cfg {Boolean} tickable - selecting
18012 * Returns the element this view is bound to.
18013 * @return {Roo.Element}
18015 getEl : function(){
18016 return this.wrapEl;
18022 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18024 refresh : function(){
18025 //Roo.log('refresh');
18028 // if we are using something like 'domtemplate', then
18029 // the what gets used is:
18030 // t.applySubtemplate(NAME, data, wrapping data..)
18031 // the outer template then get' applied with
18032 // the store 'extra data'
18033 // and the body get's added to the
18034 // roo-name="data" node?
18035 // <span class='roo-tpl-{name}'></span> ?????
18039 this.clearSelections();
18040 this.el.update("");
18042 var records = this.store.getRange();
18043 if(records.length < 1) {
18045 // is this valid?? = should it render a template??
18047 this.el.update(this.emptyText);
18051 if (this.dataName) {
18052 this.el.update(t.apply(this.store.meta)); //????
18053 el = this.el.child('.roo-tpl-' + this.dataName);
18056 for(var i = 0, len = records.length; i < len; i++){
18057 var data = this.prepareData(records[i].data, i, records[i]);
18058 this.fireEvent("preparedata", this, data, i, records[i]);
18060 var d = Roo.apply({}, data);
18063 Roo.apply(d, {'roo-id' : Roo.id()});
18067 Roo.each(this.parent.item, function(item){
18068 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18071 Roo.apply(d, {'roo-data-checked' : 'checked'});
18075 html[html.length] = Roo.util.Format.trim(
18077 t.applySubtemplate(this.dataName, d, this.store.meta) :
18084 el.update(html.join(""));
18085 this.nodes = el.dom.childNodes;
18086 this.updateIndexes(0);
18091 * Function to override to reformat the data that is sent to
18092 * the template for each node.
18093 * DEPRICATED - use the preparedata event handler.
18094 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18095 * a JSON object for an UpdateManager bound view).
18097 prepareData : function(data, index, record)
18099 this.fireEvent("preparedata", this, data, index, record);
18103 onUpdate : function(ds, record){
18104 // Roo.log('on update');
18105 this.clearSelections();
18106 var index = this.store.indexOf(record);
18107 var n = this.nodes[index];
18108 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18109 n.parentNode.removeChild(n);
18110 this.updateIndexes(index, index);
18116 onAdd : function(ds, records, index)
18118 //Roo.log(['on Add', ds, records, index] );
18119 this.clearSelections();
18120 if(this.nodes.length == 0){
18124 var n = this.nodes[index];
18125 for(var i = 0, len = records.length; i < len; i++){
18126 var d = this.prepareData(records[i].data, i, records[i]);
18128 this.tpl.insertBefore(n, d);
18131 this.tpl.append(this.el, d);
18134 this.updateIndexes(index);
18137 onRemove : function(ds, record, index){
18138 // Roo.log('onRemove');
18139 this.clearSelections();
18140 var el = this.dataName ?
18141 this.el.child('.roo-tpl-' + this.dataName) :
18144 el.dom.removeChild(this.nodes[index]);
18145 this.updateIndexes(index);
18149 * Refresh an individual node.
18150 * @param {Number} index
18152 refreshNode : function(index){
18153 this.onUpdate(this.store, this.store.getAt(index));
18156 updateIndexes : function(startIndex, endIndex){
18157 var ns = this.nodes;
18158 startIndex = startIndex || 0;
18159 endIndex = endIndex || ns.length - 1;
18160 for(var i = startIndex; i <= endIndex; i++){
18161 ns[i].nodeIndex = i;
18166 * Changes the data store this view uses and refresh the view.
18167 * @param {Store} store
18169 setStore : function(store, initial){
18170 if(!initial && this.store){
18171 this.store.un("datachanged", this.refresh);
18172 this.store.un("add", this.onAdd);
18173 this.store.un("remove", this.onRemove);
18174 this.store.un("update", this.onUpdate);
18175 this.store.un("clear", this.refresh);
18176 this.store.un("beforeload", this.onBeforeLoad);
18177 this.store.un("load", this.onLoad);
18178 this.store.un("loadexception", this.onLoad);
18182 store.on("datachanged", this.refresh, this);
18183 store.on("add", this.onAdd, this);
18184 store.on("remove", this.onRemove, this);
18185 store.on("update", this.onUpdate, this);
18186 store.on("clear", this.refresh, this);
18187 store.on("beforeload", this.onBeforeLoad, this);
18188 store.on("load", this.onLoad, this);
18189 store.on("loadexception", this.onLoad, this);
18197 * onbeforeLoad - masks the loading area.
18200 onBeforeLoad : function(store,opts)
18202 //Roo.log('onBeforeLoad');
18204 this.el.update("");
18206 this.el.mask(this.mask ? this.mask : "Loading" );
18208 onLoad : function ()
18215 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18216 * @param {HTMLElement} node
18217 * @return {HTMLElement} The template node
18219 findItemFromChild : function(node){
18220 var el = this.dataName ?
18221 this.el.child('.roo-tpl-' + this.dataName,true) :
18224 if(!node || node.parentNode == el){
18227 var p = node.parentNode;
18228 while(p && p != el){
18229 if(p.parentNode == el){
18238 onClick : function(e){
18239 var item = this.findItemFromChild(e.getTarget());
18241 var index = this.indexOf(item);
18242 if(this.onItemClick(item, index, e) !== false){
18243 this.fireEvent("click", this, index, item, e);
18246 this.clearSelections();
18251 onContextMenu : function(e){
18252 var item = this.findItemFromChild(e.getTarget());
18254 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18259 onDblClick : function(e){
18260 var item = this.findItemFromChild(e.getTarget());
18262 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18266 onItemClick : function(item, index, e)
18268 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18271 if (this.toggleSelect) {
18272 var m = this.isSelected(item) ? 'unselect' : 'select';
18275 _t[m](item, true, false);
18278 if(this.multiSelect || this.singleSelect){
18279 if(this.multiSelect && e.shiftKey && this.lastSelection){
18280 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18282 this.select(item, this.multiSelect && e.ctrlKey);
18283 this.lastSelection = item;
18286 if(!this.tickable){
18287 e.preventDefault();
18295 * Get the number of selected nodes.
18298 getSelectionCount : function(){
18299 return this.selections.length;
18303 * Get the currently selected nodes.
18304 * @return {Array} An array of HTMLElements
18306 getSelectedNodes : function(){
18307 return this.selections;
18311 * Get the indexes of the selected nodes.
18314 getSelectedIndexes : function(){
18315 var indexes = [], s = this.selections;
18316 for(var i = 0, len = s.length; i < len; i++){
18317 indexes.push(s[i].nodeIndex);
18323 * Clear all selections
18324 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18326 clearSelections : function(suppressEvent){
18327 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18328 this.cmp.elements = this.selections;
18329 this.cmp.removeClass(this.selectedClass);
18330 this.selections = [];
18331 if(!suppressEvent){
18332 this.fireEvent("selectionchange", this, this.selections);
18338 * Returns true if the passed node is selected
18339 * @param {HTMLElement/Number} node The node or node index
18340 * @return {Boolean}
18342 isSelected : function(node){
18343 var s = this.selections;
18347 node = this.getNode(node);
18348 return s.indexOf(node) !== -1;
18353 * @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
18354 * @param {Boolean} keepExisting (optional) true to keep existing selections
18355 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18357 select : function(nodeInfo, keepExisting, suppressEvent){
18358 if(nodeInfo instanceof Array){
18360 this.clearSelections(true);
18362 for(var i = 0, len = nodeInfo.length; i < len; i++){
18363 this.select(nodeInfo[i], true, true);
18367 var node = this.getNode(nodeInfo);
18368 if(!node || this.isSelected(node)){
18369 return; // already selected.
18372 this.clearSelections(true);
18375 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18376 Roo.fly(node).addClass(this.selectedClass);
18377 this.selections.push(node);
18378 if(!suppressEvent){
18379 this.fireEvent("selectionchange", this, this.selections);
18387 * @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
18388 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18389 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18391 unselect : function(nodeInfo, keepExisting, suppressEvent)
18393 if(nodeInfo instanceof Array){
18394 Roo.each(this.selections, function(s) {
18395 this.unselect(s, nodeInfo);
18399 var node = this.getNode(nodeInfo);
18400 if(!node || !this.isSelected(node)){
18401 //Roo.log("not selected");
18402 return; // not selected.
18406 Roo.each(this.selections, function(s) {
18408 Roo.fly(node).removeClass(this.selectedClass);
18415 this.selections= ns;
18416 this.fireEvent("selectionchange", this, this.selections);
18420 * Gets a template node.
18421 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18422 * @return {HTMLElement} The node or null if it wasn't found
18424 getNode : function(nodeInfo){
18425 if(typeof nodeInfo == "string"){
18426 return document.getElementById(nodeInfo);
18427 }else if(typeof nodeInfo == "number"){
18428 return this.nodes[nodeInfo];
18434 * Gets a range template nodes.
18435 * @param {Number} startIndex
18436 * @param {Number} endIndex
18437 * @return {Array} An array of nodes
18439 getNodes : function(start, end){
18440 var ns = this.nodes;
18441 start = start || 0;
18442 end = typeof end == "undefined" ? ns.length - 1 : end;
18445 for(var i = start; i <= end; i++){
18449 for(var i = start; i >= end; i--){
18457 * Finds the index of the passed node
18458 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18459 * @return {Number} The index of the node or -1
18461 indexOf : function(node){
18462 node = this.getNode(node);
18463 if(typeof node.nodeIndex == "number"){
18464 return node.nodeIndex;
18466 var ns = this.nodes;
18467 for(var i = 0, len = ns.length; i < len; i++){
18478 * based on jquery fullcalendar
18482 Roo.bootstrap = Roo.bootstrap || {};
18484 * @class Roo.bootstrap.Calendar
18485 * @extends Roo.bootstrap.Component
18486 * Bootstrap Calendar class
18487 * @cfg {Boolean} loadMask (true|false) default false
18488 * @cfg {Object} header generate the user specific header of the calendar, default false
18491 * Create a new Container
18492 * @param {Object} config The config object
18497 Roo.bootstrap.Calendar = function(config){
18498 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18502 * Fires when a date is selected
18503 * @param {DatePicker} this
18504 * @param {Date} date The selected date
18508 * @event monthchange
18509 * Fires when the displayed month changes
18510 * @param {DatePicker} this
18511 * @param {Date} date The selected month
18513 'monthchange': true,
18515 * @event evententer
18516 * Fires when mouse over an event
18517 * @param {Calendar} this
18518 * @param {event} Event
18520 'evententer': true,
18522 * @event eventleave
18523 * Fires when the mouse leaves an
18524 * @param {Calendar} this
18527 'eventleave': true,
18529 * @event eventclick
18530 * Fires when the mouse click an
18531 * @param {Calendar} this
18540 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18543 * @cfg {Number} startDay
18544 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18552 getAutoCreate : function(){
18555 var fc_button = function(name, corner, style, content ) {
18556 return Roo.apply({},{
18558 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18560 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18563 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18574 style : 'width:100%',
18581 cls : 'fc-header-left',
18583 fc_button('prev', 'left', 'arrow', '‹' ),
18584 fc_button('next', 'right', 'arrow', '›' ),
18585 { tag: 'span', cls: 'fc-header-space' },
18586 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18594 cls : 'fc-header-center',
18598 cls: 'fc-header-title',
18601 html : 'month / year'
18609 cls : 'fc-header-right',
18611 /* fc_button('month', 'left', '', 'month' ),
18612 fc_button('week', '', '', 'week' ),
18613 fc_button('day', 'right', '', 'day' )
18625 header = this.header;
18628 var cal_heads = function() {
18630 // fixme - handle this.
18632 for (var i =0; i < Date.dayNames.length; i++) {
18633 var d = Date.dayNames[i];
18636 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18637 html : d.substring(0,3)
18641 ret[0].cls += ' fc-first';
18642 ret[6].cls += ' fc-last';
18645 var cal_cell = function(n) {
18648 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18653 cls: 'fc-day-number',
18657 cls: 'fc-day-content',
18661 style: 'position: relative;' // height: 17px;
18673 var cal_rows = function() {
18676 for (var r = 0; r < 6; r++) {
18683 for (var i =0; i < Date.dayNames.length; i++) {
18684 var d = Date.dayNames[i];
18685 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18688 row.cn[0].cls+=' fc-first';
18689 row.cn[0].cn[0].style = 'min-height:90px';
18690 row.cn[6].cls+=' fc-last';
18694 ret[0].cls += ' fc-first';
18695 ret[4].cls += ' fc-prev-last';
18696 ret[5].cls += ' fc-last';
18703 cls: 'fc-border-separate',
18704 style : 'width:100%',
18712 cls : 'fc-first fc-last',
18730 cls : 'fc-content',
18731 style : "position: relative;",
18734 cls : 'fc-view fc-view-month fc-grid',
18735 style : 'position: relative',
18736 unselectable : 'on',
18739 cls : 'fc-event-container',
18740 style : 'position:absolute;z-index:8;top:0;left:0;'
18758 initEvents : function()
18761 throw "can not find store for calendar";
18767 style: "text-align:center",
18771 style: "background-color:white;width:50%;margin:250 auto",
18775 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18786 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18788 var size = this.el.select('.fc-content', true).first().getSize();
18789 this.maskEl.setSize(size.width, size.height);
18790 this.maskEl.enableDisplayMode("block");
18791 if(!this.loadMask){
18792 this.maskEl.hide();
18795 this.store = Roo.factory(this.store, Roo.data);
18796 this.store.on('load', this.onLoad, this);
18797 this.store.on('beforeload', this.onBeforeLoad, this);
18801 this.cells = this.el.select('.fc-day',true);
18802 //Roo.log(this.cells);
18803 this.textNodes = this.el.query('.fc-day-number');
18804 this.cells.addClassOnOver('fc-state-hover');
18806 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18807 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18808 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18809 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18811 this.on('monthchange', this.onMonthChange, this);
18813 this.update(new Date().clearTime());
18816 resize : function() {
18817 var sz = this.el.getSize();
18819 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18820 this.el.select('.fc-day-content div',true).setHeight(34);
18825 showPrevMonth : function(e){
18826 this.update(this.activeDate.add("mo", -1));
18828 showToday : function(e){
18829 this.update(new Date().clearTime());
18832 showNextMonth : function(e){
18833 this.update(this.activeDate.add("mo", 1));
18837 showPrevYear : function(){
18838 this.update(this.activeDate.add("y", -1));
18842 showNextYear : function(){
18843 this.update(this.activeDate.add("y", 1));
18848 update : function(date)
18850 var vd = this.activeDate;
18851 this.activeDate = date;
18852 // if(vd && this.el){
18853 // var t = date.getTime();
18854 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18855 // Roo.log('using add remove');
18857 // this.fireEvent('monthchange', this, date);
18859 // this.cells.removeClass("fc-state-highlight");
18860 // this.cells.each(function(c){
18861 // if(c.dateValue == t){
18862 // c.addClass("fc-state-highlight");
18863 // setTimeout(function(){
18864 // try{c.dom.firstChild.focus();}catch(e){}
18874 var days = date.getDaysInMonth();
18876 var firstOfMonth = date.getFirstDateOfMonth();
18877 var startingPos = firstOfMonth.getDay()-this.startDay;
18879 if(startingPos < this.startDay){
18883 var pm = date.add(Date.MONTH, -1);
18884 var prevStart = pm.getDaysInMonth()-startingPos;
18886 this.cells = this.el.select('.fc-day',true);
18887 this.textNodes = this.el.query('.fc-day-number');
18888 this.cells.addClassOnOver('fc-state-hover');
18890 var cells = this.cells.elements;
18891 var textEls = this.textNodes;
18893 Roo.each(cells, function(cell){
18894 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18897 days += startingPos;
18899 // convert everything to numbers so it's fast
18900 var day = 86400000;
18901 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18904 //Roo.log(prevStart);
18906 var today = new Date().clearTime().getTime();
18907 var sel = date.clearTime().getTime();
18908 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18909 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18910 var ddMatch = this.disabledDatesRE;
18911 var ddText = this.disabledDatesText;
18912 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18913 var ddaysText = this.disabledDaysText;
18914 var format = this.format;
18916 var setCellClass = function(cal, cell){
18920 //Roo.log('set Cell Class');
18922 var t = d.getTime();
18926 cell.dateValue = t;
18928 cell.className += " fc-today";
18929 cell.className += " fc-state-highlight";
18930 cell.title = cal.todayText;
18933 // disable highlight in other month..
18934 //cell.className += " fc-state-highlight";
18939 cell.className = " fc-state-disabled";
18940 cell.title = cal.minText;
18944 cell.className = " fc-state-disabled";
18945 cell.title = cal.maxText;
18949 if(ddays.indexOf(d.getDay()) != -1){
18950 cell.title = ddaysText;
18951 cell.className = " fc-state-disabled";
18954 if(ddMatch && format){
18955 var fvalue = d.dateFormat(format);
18956 if(ddMatch.test(fvalue)){
18957 cell.title = ddText.replace("%0", fvalue);
18958 cell.className = " fc-state-disabled";
18962 if (!cell.initialClassName) {
18963 cell.initialClassName = cell.dom.className;
18966 cell.dom.className = cell.initialClassName + ' ' + cell.className;
18971 for(; i < startingPos; i++) {
18972 textEls[i].innerHTML = (++prevStart);
18973 d.setDate(d.getDate()+1);
18975 cells[i].className = "fc-past fc-other-month";
18976 setCellClass(this, cells[i]);
18981 for(; i < days; i++){
18982 intDay = i - startingPos + 1;
18983 textEls[i].innerHTML = (intDay);
18984 d.setDate(d.getDate()+1);
18986 cells[i].className = ''; // "x-date-active";
18987 setCellClass(this, cells[i]);
18991 for(; i < 42; i++) {
18992 textEls[i].innerHTML = (++extraDays);
18993 d.setDate(d.getDate()+1);
18995 cells[i].className = "fc-future fc-other-month";
18996 setCellClass(this, cells[i]);
18999 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19001 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19003 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19004 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19006 if(totalRows != 6){
19007 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19008 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19011 this.fireEvent('monthchange', this, date);
19015 if(!this.internalRender){
19016 var main = this.el.dom.firstChild;
19017 var w = main.offsetWidth;
19018 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19019 Roo.fly(main).setWidth(w);
19020 this.internalRender = true;
19021 // opera does not respect the auto grow header center column
19022 // then, after it gets a width opera refuses to recalculate
19023 // without a second pass
19024 if(Roo.isOpera && !this.secondPass){
19025 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19026 this.secondPass = true;
19027 this.update.defer(10, this, [date]);
19034 findCell : function(dt) {
19035 dt = dt.clearTime().getTime();
19037 this.cells.each(function(c){
19038 //Roo.log("check " +c.dateValue + '?=' + dt);
19039 if(c.dateValue == dt){
19049 findCells : function(ev) {
19050 var s = ev.start.clone().clearTime().getTime();
19052 var e= ev.end.clone().clearTime().getTime();
19055 this.cells.each(function(c){
19056 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19058 if(c.dateValue > e){
19061 if(c.dateValue < s){
19070 // findBestRow: function(cells)
19074 // for (var i =0 ; i < cells.length;i++) {
19075 // ret = Math.max(cells[i].rows || 0,ret);
19082 addItem : function(ev)
19084 // look for vertical location slot in
19085 var cells = this.findCells(ev);
19087 // ev.row = this.findBestRow(cells);
19089 // work out the location.
19093 for(var i =0; i < cells.length; i++) {
19095 cells[i].row = cells[0].row;
19098 cells[i].row = cells[i].row + 1;
19108 if (crow.start.getY() == cells[i].getY()) {
19110 crow.end = cells[i];
19127 cells[0].events.push(ev);
19129 this.calevents.push(ev);
19132 clearEvents: function() {
19134 if(!this.calevents){
19138 Roo.each(this.cells.elements, function(c){
19144 Roo.each(this.calevents, function(e) {
19145 Roo.each(e.els, function(el) {
19146 el.un('mouseenter' ,this.onEventEnter, this);
19147 el.un('mouseleave' ,this.onEventLeave, this);
19152 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19158 renderEvents: function()
19162 this.cells.each(function(c) {
19171 if(c.row != c.events.length){
19172 r = 4 - (4 - (c.row - c.events.length));
19175 c.events = ev.slice(0, r);
19176 c.more = ev.slice(r);
19178 if(c.more.length && c.more.length == 1){
19179 c.events.push(c.more.pop());
19182 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19186 this.cells.each(function(c) {
19188 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19191 for (var e = 0; e < c.events.length; e++){
19192 var ev = c.events[e];
19193 var rows = ev.rows;
19195 for(var i = 0; i < rows.length; i++) {
19197 // how many rows should it span..
19200 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19201 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19203 unselectable : "on",
19206 cls: 'fc-event-inner',
19210 // cls: 'fc-event-time',
19211 // html : cells.length > 1 ? '' : ev.time
19215 cls: 'fc-event-title',
19216 html : String.format('{0}', ev.title)
19223 cls: 'ui-resizable-handle ui-resizable-e',
19224 html : '  '
19231 cfg.cls += ' fc-event-start';
19233 if ((i+1) == rows.length) {
19234 cfg.cls += ' fc-event-end';
19237 var ctr = _this.el.select('.fc-event-container',true).first();
19238 var cg = ctr.createChild(cfg);
19240 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19241 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19243 var r = (c.more.length) ? 1 : 0;
19244 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19245 cg.setWidth(ebox.right - sbox.x -2);
19247 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19248 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19249 cg.on('click', _this.onEventClick, _this, ev);
19260 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19261 style : 'position: absolute',
19262 unselectable : "on",
19265 cls: 'fc-event-inner',
19269 cls: 'fc-event-title',
19277 cls: 'ui-resizable-handle ui-resizable-e',
19278 html : '  '
19284 var ctr = _this.el.select('.fc-event-container',true).first();
19285 var cg = ctr.createChild(cfg);
19287 var sbox = c.select('.fc-day-content',true).first().getBox();
19288 var ebox = c.select('.fc-day-content',true).first().getBox();
19290 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19291 cg.setWidth(ebox.right - sbox.x -2);
19293 cg.on('click', _this.onMoreEventClick, _this, c.more);
19303 onEventEnter: function (e, el,event,d) {
19304 this.fireEvent('evententer', this, el, event);
19307 onEventLeave: function (e, el,event,d) {
19308 this.fireEvent('eventleave', this, el, event);
19311 onEventClick: function (e, el,event,d) {
19312 this.fireEvent('eventclick', this, el, event);
19315 onMonthChange: function () {
19319 onMoreEventClick: function(e, el, more)
19323 this.calpopover.placement = 'right';
19324 this.calpopover.setTitle('More');
19326 this.calpopover.setContent('');
19328 var ctr = this.calpopover.el.select('.popover-content', true).first();
19330 Roo.each(more, function(m){
19332 cls : 'fc-event-hori fc-event-draggable',
19335 var cg = ctr.createChild(cfg);
19337 cg.on('click', _this.onEventClick, _this, m);
19340 this.calpopover.show(el);
19345 onLoad: function ()
19347 this.calevents = [];
19350 if(this.store.getCount() > 0){
19351 this.store.data.each(function(d){
19354 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19355 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19356 time : d.data.start_time,
19357 title : d.data.title,
19358 description : d.data.description,
19359 venue : d.data.venue
19364 this.renderEvents();
19366 if(this.calevents.length && this.loadMask){
19367 this.maskEl.hide();
19371 onBeforeLoad: function()
19373 this.clearEvents();
19375 this.maskEl.show();
19389 * @class Roo.bootstrap.Popover
19390 * @extends Roo.bootstrap.Component
19391 * Bootstrap Popover class
19392 * @cfg {String} html contents of the popover (or false to use children..)
19393 * @cfg {String} title of popover (or false to hide)
19394 * @cfg {String} placement how it is placed
19395 * @cfg {String} trigger click || hover (or false to trigger manually)
19396 * @cfg {String} over what (parent or false to trigger manually.)
19397 * @cfg {Number} delay - delay before showing
19400 * Create a new Popover
19401 * @param {Object} config The config object
19404 Roo.bootstrap.Popover = function(config){
19405 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19411 * After the popover show
19413 * @param {Roo.bootstrap.Popover} this
19418 * After the popover hide
19420 * @param {Roo.bootstrap.Popover} this
19426 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19428 title: 'Fill in a title',
19431 placement : 'right',
19432 trigger : 'hover', // hover
19438 can_build_overlaid : false,
19440 getChildContainer : function()
19442 return this.el.select('.popover-content',true).first();
19445 getAutoCreate : function(){
19448 cls : 'popover roo-dynamic',
19449 style: 'display:block',
19455 cls : 'popover-inner',
19459 cls: 'popover-title popover-header',
19463 cls : 'popover-content popover-body',
19474 setTitle: function(str)
19477 this.el.select('.popover-title',true).first().dom.innerHTML = str;
19479 setContent: function(str)
19482 this.el.select('.popover-content',true).first().dom.innerHTML = str;
19484 // as it get's added to the bottom of the page.
19485 onRender : function(ct, position)
19487 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19489 var cfg = Roo.apply({}, this.getAutoCreate());
19493 cfg.cls += ' ' + this.cls;
19496 cfg.style = this.style;
19498 //Roo.log("adding to ");
19499 this.el = Roo.get(document.body).createChild(cfg, position);
19500 // Roo.log(this.el);
19505 initEvents : function()
19507 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19508 this.el.enableDisplayMode('block');
19510 if (this.over === false) {
19513 if (this.triggers === false) {
19516 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19517 var triggers = this.trigger ? this.trigger.split(' ') : [];
19518 Roo.each(triggers, function(trigger) {
19520 if (trigger == 'click') {
19521 on_el.on('click', this.toggle, this);
19522 } else if (trigger != 'manual') {
19523 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19524 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19526 on_el.on(eventIn ,this.enter, this);
19527 on_el.on(eventOut, this.leave, this);
19538 toggle : function () {
19539 this.hoverState == 'in' ? this.leave() : this.enter();
19542 enter : function () {
19544 clearTimeout(this.timeout);
19546 this.hoverState = 'in';
19548 if (!this.delay || !this.delay.show) {
19553 this.timeout = setTimeout(function () {
19554 if (_t.hoverState == 'in') {
19557 }, this.delay.show)
19560 leave : function() {
19561 clearTimeout(this.timeout);
19563 this.hoverState = 'out';
19565 if (!this.delay || !this.delay.hide) {
19570 this.timeout = setTimeout(function () {
19571 if (_t.hoverState == 'out') {
19574 }, this.delay.hide)
19577 show : function (on_el)
19580 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19584 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19585 if (this.html !== false) {
19586 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19588 this.el.removeClass([
19589 'fade','top','bottom', 'left', 'right','in',
19590 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19592 if (!this.title.length) {
19593 this.el.select('.popover-title',true).hide();
19596 var placement = typeof this.placement == 'function' ?
19597 this.placement.call(this, this.el, on_el) :
19600 var autoToken = /\s?auto?\s?/i;
19601 var autoPlace = autoToken.test(placement);
19603 placement = placement.replace(autoToken, '') || 'top';
19607 //this.el.setXY([0,0]);
19609 this.el.dom.style.display='block';
19610 this.el.addClass(placement);
19612 //this.el.appendTo(on_el);
19614 var p = this.getPosition();
19615 var box = this.el.getBox();
19620 var align = Roo.bootstrap.Popover.alignment[placement];
19623 this.el.alignTo(on_el, align[0],align[1]);
19624 //var arrow = this.el.select('.arrow',true).first();
19625 //arrow.set(align[2],
19627 this.el.addClass('in');
19630 if (this.el.hasClass('fade')) {
19634 this.hoverState = 'in';
19636 this.fireEvent('show', this);
19641 this.el.setXY([0,0]);
19642 this.el.removeClass('in');
19644 this.hoverState = null;
19646 this.fireEvent('hide', this);
19651 Roo.bootstrap.Popover.alignment = {
19652 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19653 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19654 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19655 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19666 * @class Roo.bootstrap.Progress
19667 * @extends Roo.bootstrap.Component
19668 * Bootstrap Progress class
19669 * @cfg {Boolean} striped striped of the progress bar
19670 * @cfg {Boolean} active animated of the progress bar
19674 * Create a new Progress
19675 * @param {Object} config The config object
19678 Roo.bootstrap.Progress = function(config){
19679 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19682 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19687 getAutoCreate : function(){
19695 cfg.cls += ' progress-striped';
19699 cfg.cls += ' active';
19718 * @class Roo.bootstrap.ProgressBar
19719 * @extends Roo.bootstrap.Component
19720 * Bootstrap ProgressBar class
19721 * @cfg {Number} aria_valuenow aria-value now
19722 * @cfg {Number} aria_valuemin aria-value min
19723 * @cfg {Number} aria_valuemax aria-value max
19724 * @cfg {String} label label for the progress bar
19725 * @cfg {String} panel (success | info | warning | danger )
19726 * @cfg {String} role role of the progress bar
19727 * @cfg {String} sr_only text
19731 * Create a new ProgressBar
19732 * @param {Object} config The config object
19735 Roo.bootstrap.ProgressBar = function(config){
19736 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19739 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19743 aria_valuemax : 100,
19749 getAutoCreate : function()
19754 cls: 'progress-bar',
19755 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19767 cfg.role = this.role;
19770 if(this.aria_valuenow){
19771 cfg['aria-valuenow'] = this.aria_valuenow;
19774 if(this.aria_valuemin){
19775 cfg['aria-valuemin'] = this.aria_valuemin;
19778 if(this.aria_valuemax){
19779 cfg['aria-valuemax'] = this.aria_valuemax;
19782 if(this.label && !this.sr_only){
19783 cfg.html = this.label;
19787 cfg.cls += ' progress-bar-' + this.panel;
19793 update : function(aria_valuenow)
19795 this.aria_valuenow = aria_valuenow;
19797 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19812 * @class Roo.bootstrap.TabGroup
19813 * @extends Roo.bootstrap.Column
19814 * Bootstrap Column class
19815 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19816 * @cfg {Boolean} carousel true to make the group behave like a carousel
19817 * @cfg {Boolean} bullets show bullets for the panels
19818 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19819 * @cfg {Number} timer auto slide timer .. default 0 millisecond
19820 * @cfg {Boolean} showarrow (true|false) show arrow default true
19823 * Create a new TabGroup
19824 * @param {Object} config The config object
19827 Roo.bootstrap.TabGroup = function(config){
19828 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19830 this.navId = Roo.id();
19833 Roo.bootstrap.TabGroup.register(this);
19837 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
19840 transition : false,
19845 slideOnTouch : false,
19848 getAutoCreate : function()
19850 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19852 cfg.cls += ' tab-content';
19854 if (this.carousel) {
19855 cfg.cls += ' carousel slide';
19858 cls : 'carousel-inner',
19862 if(this.bullets && !Roo.isTouch){
19865 cls : 'carousel-bullets',
19869 if(this.bullets_cls){
19870 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19877 cfg.cn[0].cn.push(bullets);
19880 if(this.showarrow){
19881 cfg.cn[0].cn.push({
19883 class : 'carousel-arrow',
19887 class : 'carousel-prev',
19891 class : 'fa fa-chevron-left'
19897 class : 'carousel-next',
19901 class : 'fa fa-chevron-right'
19914 initEvents: function()
19916 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19917 // this.el.on("touchstart", this.onTouchStart, this);
19920 if(this.autoslide){
19923 this.slideFn = window.setInterval(function() {
19924 _this.showPanelNext();
19928 if(this.showarrow){
19929 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19930 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19936 // onTouchStart : function(e, el, o)
19938 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19942 // this.showPanelNext();
19946 getChildContainer : function()
19948 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19952 * register a Navigation item
19953 * @param {Roo.bootstrap.NavItem} the navitem to add
19955 register : function(item)
19957 this.tabs.push( item);
19958 item.navId = this.navId; // not really needed..
19963 getActivePanel : function()
19966 Roo.each(this.tabs, function(t) {
19976 getPanelByName : function(n)
19979 Roo.each(this.tabs, function(t) {
19980 if (t.tabId == n) {
19988 indexOfPanel : function(p)
19991 Roo.each(this.tabs, function(t,i) {
19992 if (t.tabId == p.tabId) {
20001 * show a specific panel
20002 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20003 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20005 showPanel : function (pan)
20007 if(this.transition || typeof(pan) == 'undefined'){
20008 Roo.log("waiting for the transitionend");
20012 if (typeof(pan) == 'number') {
20013 pan = this.tabs[pan];
20016 if (typeof(pan) == 'string') {
20017 pan = this.getPanelByName(pan);
20020 var cur = this.getActivePanel();
20023 Roo.log('pan or acitve pan is undefined');
20027 if (pan.tabId == this.getActivePanel().tabId) {
20031 if (false === cur.fireEvent('beforedeactivate')) {
20035 if(this.bullets > 0 && !Roo.isTouch){
20036 this.setActiveBullet(this.indexOfPanel(pan));
20039 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20041 //class="carousel-item carousel-item-next carousel-item-left"
20043 this.transition = true;
20044 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20045 var lr = dir == 'next' ? 'left' : 'right';
20046 pan.el.addClass(dir); // or prev
20047 pan.el.addClass('carousel-item-' + dir); // or prev
20048 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20049 cur.el.addClass(lr); // or right
20050 pan.el.addClass(lr);
20051 cur.el.addClass('carousel-item-' +lr); // or right
20052 pan.el.addClass('carousel-item-' +lr);
20056 cur.el.on('transitionend', function() {
20057 Roo.log("trans end?");
20059 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20060 pan.setActive(true);
20062 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20063 cur.setActive(false);
20065 _this.transition = false;
20067 }, this, { single: true } );
20072 cur.setActive(false);
20073 pan.setActive(true);
20078 showPanelNext : function()
20080 var i = this.indexOfPanel(this.getActivePanel());
20082 if (i >= this.tabs.length - 1 && !this.autoslide) {
20086 if (i >= this.tabs.length - 1 && this.autoslide) {
20090 this.showPanel(this.tabs[i+1]);
20093 showPanelPrev : function()
20095 var i = this.indexOfPanel(this.getActivePanel());
20097 if (i < 1 && !this.autoslide) {
20101 if (i < 1 && this.autoslide) {
20102 i = this.tabs.length;
20105 this.showPanel(this.tabs[i-1]);
20109 addBullet: function()
20111 if(!this.bullets || Roo.isTouch){
20114 var ctr = this.el.select('.carousel-bullets',true).first();
20115 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20116 var bullet = ctr.createChild({
20117 cls : 'bullet bullet-' + i
20118 },ctr.dom.lastChild);
20123 bullet.on('click', (function(e, el, o, ii, t){
20125 e.preventDefault();
20127 this.showPanel(ii);
20129 if(this.autoslide && this.slideFn){
20130 clearInterval(this.slideFn);
20131 this.slideFn = window.setInterval(function() {
20132 _this.showPanelNext();
20136 }).createDelegate(this, [i, bullet], true));
20141 setActiveBullet : function(i)
20147 Roo.each(this.el.select('.bullet', true).elements, function(el){
20148 el.removeClass('selected');
20151 var bullet = this.el.select('.bullet-' + i, true).first();
20157 bullet.addClass('selected');
20168 Roo.apply(Roo.bootstrap.TabGroup, {
20172 * register a Navigation Group
20173 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20175 register : function(navgrp)
20177 this.groups[navgrp.navId] = navgrp;
20181 * fetch a Navigation Group based on the navigation ID
20182 * if one does not exist , it will get created.
20183 * @param {string} the navgroup to add
20184 * @returns {Roo.bootstrap.NavGroup} the navgroup
20186 get: function(navId) {
20187 if (typeof(this.groups[navId]) == 'undefined') {
20188 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20190 return this.groups[navId] ;
20205 * @class Roo.bootstrap.TabPanel
20206 * @extends Roo.bootstrap.Component
20207 * Bootstrap TabPanel class
20208 * @cfg {Boolean} active panel active
20209 * @cfg {String} html panel content
20210 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20211 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20212 * @cfg {String} href click to link..
20216 * Create a new TabPanel
20217 * @param {Object} config The config object
20220 Roo.bootstrap.TabPanel = function(config){
20221 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20225 * Fires when the active status changes
20226 * @param {Roo.bootstrap.TabPanel} this
20227 * @param {Boolean} state the new state
20232 * @event beforedeactivate
20233 * Fires before a tab is de-activated - can be used to do validation on a form.
20234 * @param {Roo.bootstrap.TabPanel} this
20235 * @return {Boolean} false if there is an error
20238 'beforedeactivate': true
20241 this.tabId = this.tabId || Roo.id();
20245 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20253 getAutoCreate : function(){
20258 // item is needed for carousel - not sure if it has any effect otherwise
20259 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20260 html: this.html || ''
20264 cfg.cls += ' active';
20268 cfg.tabId = this.tabId;
20276 initEvents: function()
20278 var p = this.parent();
20280 this.navId = this.navId || p.navId;
20282 if (typeof(this.navId) != 'undefined') {
20283 // not really needed.. but just in case.. parent should be a NavGroup.
20284 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20288 var i = tg.tabs.length - 1;
20290 if(this.active && tg.bullets > 0 && i < tg.bullets){
20291 tg.setActiveBullet(i);
20295 this.el.on('click', this.onClick, this);
20298 this.el.on("touchstart", this.onTouchStart, this);
20299 this.el.on("touchmove", this.onTouchMove, this);
20300 this.el.on("touchend", this.onTouchEnd, this);
20305 onRender : function(ct, position)
20307 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20310 setActive : function(state)
20312 Roo.log("panel - set active " + this.tabId + "=" + state);
20314 this.active = state;
20316 this.el.removeClass('active');
20318 } else if (!this.el.hasClass('active')) {
20319 this.el.addClass('active');
20322 this.fireEvent('changed', this, state);
20325 onClick : function(e)
20327 e.preventDefault();
20329 if(!this.href.length){
20333 window.location.href = this.href;
20342 onTouchStart : function(e)
20344 this.swiping = false;
20346 this.startX = e.browserEvent.touches[0].clientX;
20347 this.startY = e.browserEvent.touches[0].clientY;
20350 onTouchMove : function(e)
20352 this.swiping = true;
20354 this.endX = e.browserEvent.touches[0].clientX;
20355 this.endY = e.browserEvent.touches[0].clientY;
20358 onTouchEnd : function(e)
20365 var tabGroup = this.parent();
20367 if(this.endX > this.startX){ // swiping right
20368 tabGroup.showPanelPrev();
20372 if(this.startX > this.endX){ // swiping left
20373 tabGroup.showPanelNext();
20392 * @class Roo.bootstrap.DateField
20393 * @extends Roo.bootstrap.Input
20394 * Bootstrap DateField class
20395 * @cfg {Number} weekStart default 0
20396 * @cfg {String} viewMode default empty, (months|years)
20397 * @cfg {String} minViewMode default empty, (months|years)
20398 * @cfg {Number} startDate default -Infinity
20399 * @cfg {Number} endDate default Infinity
20400 * @cfg {Boolean} todayHighlight default false
20401 * @cfg {Boolean} todayBtn default false
20402 * @cfg {Boolean} calendarWeeks default false
20403 * @cfg {Object} daysOfWeekDisabled default empty
20404 * @cfg {Boolean} singleMode default false (true | false)
20406 * @cfg {Boolean} keyboardNavigation default true
20407 * @cfg {String} language default en
20410 * Create a new DateField
20411 * @param {Object} config The config object
20414 Roo.bootstrap.DateField = function(config){
20415 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20419 * Fires when this field show.
20420 * @param {Roo.bootstrap.DateField} this
20421 * @param {Mixed} date The date value
20426 * Fires when this field hide.
20427 * @param {Roo.bootstrap.DateField} this
20428 * @param {Mixed} date The date value
20433 * Fires when select a date.
20434 * @param {Roo.bootstrap.DateField} this
20435 * @param {Mixed} date The date value
20439 * @event beforeselect
20440 * Fires when before select a date.
20441 * @param {Roo.bootstrap.DateField} this
20442 * @param {Mixed} date The date value
20444 beforeselect : true
20448 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20451 * @cfg {String} format
20452 * The default date format string which can be overriden for localization support. The format must be
20453 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20457 * @cfg {String} altFormats
20458 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20459 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20461 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20469 todayHighlight : false,
20475 keyboardNavigation: true,
20477 calendarWeeks: false,
20479 startDate: -Infinity,
20483 daysOfWeekDisabled: [],
20487 singleMode : false,
20489 UTCDate: function()
20491 return new Date(Date.UTC.apply(Date, arguments));
20494 UTCToday: function()
20496 var today = new Date();
20497 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20500 getDate: function() {
20501 var d = this.getUTCDate();
20502 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20505 getUTCDate: function() {
20509 setDate: function(d) {
20510 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20513 setUTCDate: function(d) {
20515 this.setValue(this.formatDate(this.date));
20518 onRender: function(ct, position)
20521 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20523 this.language = this.language || 'en';
20524 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20525 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20527 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20528 this.format = this.format || 'm/d/y';
20529 this.isInline = false;
20530 this.isInput = true;
20531 this.component = this.el.select('.add-on', true).first() || false;
20532 this.component = (this.component && this.component.length === 0) ? false : this.component;
20533 this.hasInput = this.component && this.inputEl().length;
20535 if (typeof(this.minViewMode === 'string')) {
20536 switch (this.minViewMode) {
20538 this.minViewMode = 1;
20541 this.minViewMode = 2;
20544 this.minViewMode = 0;
20549 if (typeof(this.viewMode === 'string')) {
20550 switch (this.viewMode) {
20563 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20565 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20567 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20569 this.picker().on('mousedown', this.onMousedown, this);
20570 this.picker().on('click', this.onClick, this);
20572 this.picker().addClass('datepicker-dropdown');
20574 this.startViewMode = this.viewMode;
20576 if(this.singleMode){
20577 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20578 v.setVisibilityMode(Roo.Element.DISPLAY);
20582 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20583 v.setStyle('width', '189px');
20587 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20588 if(!this.calendarWeeks){
20593 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20594 v.attr('colspan', function(i, val){
20595 return parseInt(val) + 1;
20600 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20602 this.setStartDate(this.startDate);
20603 this.setEndDate(this.endDate);
20605 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20612 if(this.isInline) {
20617 picker : function()
20619 return this.pickerEl;
20620 // return this.el.select('.datepicker', true).first();
20623 fillDow: function()
20625 var dowCnt = this.weekStart;
20634 if(this.calendarWeeks){
20642 while (dowCnt < this.weekStart + 7) {
20646 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20650 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20653 fillMonths: function()
20656 var months = this.picker().select('>.datepicker-months td', true).first();
20658 months.dom.innerHTML = '';
20664 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20667 months.createChild(month);
20674 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;
20676 if (this.date < this.startDate) {
20677 this.viewDate = new Date(this.startDate);
20678 } else if (this.date > this.endDate) {
20679 this.viewDate = new Date(this.endDate);
20681 this.viewDate = new Date(this.date);
20689 var d = new Date(this.viewDate),
20690 year = d.getUTCFullYear(),
20691 month = d.getUTCMonth(),
20692 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20693 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20694 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20695 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20696 currentDate = this.date && this.date.valueOf(),
20697 today = this.UTCToday();
20699 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20701 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20703 // this.picker.select('>tfoot th.today').
20704 // .text(dates[this.language].today)
20705 // .toggle(this.todayBtn !== false);
20707 this.updateNavArrows();
20710 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20712 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20714 prevMonth.setUTCDate(day);
20716 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20718 var nextMonth = new Date(prevMonth);
20720 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20722 nextMonth = nextMonth.valueOf();
20724 var fillMonths = false;
20726 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20728 while(prevMonth.valueOf() <= nextMonth) {
20731 if (prevMonth.getUTCDay() === this.weekStart) {
20733 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20741 if(this.calendarWeeks){
20742 // ISO 8601: First week contains first thursday.
20743 // ISO also states week starts on Monday, but we can be more abstract here.
20745 // Start of current week: based on weekstart/current date
20746 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20747 // Thursday of this week
20748 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20749 // First Thursday of year, year from thursday
20750 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20751 // Calendar week: ms between thursdays, div ms per day, div 7 days
20752 calWeek = (th - yth) / 864e5 / 7 + 1;
20754 fillMonths.cn.push({
20762 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20764 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20767 if (this.todayHighlight &&
20768 prevMonth.getUTCFullYear() == today.getFullYear() &&
20769 prevMonth.getUTCMonth() == today.getMonth() &&
20770 prevMonth.getUTCDate() == today.getDate()) {
20771 clsName += ' today';
20774 if (currentDate && prevMonth.valueOf() === currentDate) {
20775 clsName += ' active';
20778 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20779 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20780 clsName += ' disabled';
20783 fillMonths.cn.push({
20785 cls: 'day ' + clsName,
20786 html: prevMonth.getDate()
20789 prevMonth.setDate(prevMonth.getDate()+1);
20792 var currentYear = this.date && this.date.getUTCFullYear();
20793 var currentMonth = this.date && this.date.getUTCMonth();
20795 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20797 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20798 v.removeClass('active');
20800 if(currentYear === year && k === currentMonth){
20801 v.addClass('active');
20804 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20805 v.addClass('disabled');
20811 year = parseInt(year/10, 10) * 10;
20813 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20815 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20818 for (var i = -1; i < 11; i++) {
20819 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20821 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20829 showMode: function(dir)
20832 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20835 Roo.each(this.picker().select('>div',true).elements, function(v){
20836 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20839 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20844 if(this.isInline) {
20848 this.picker().removeClass(['bottom', 'top']);
20850 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20852 * place to the top of element!
20856 this.picker().addClass('top');
20857 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20862 this.picker().addClass('bottom');
20864 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20867 parseDate : function(value)
20869 if(!value || value instanceof Date){
20872 var v = Date.parseDate(value, this.format);
20873 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20874 v = Date.parseDate(value, 'Y-m-d');
20876 if(!v && this.altFormats){
20877 if(!this.altFormatsArray){
20878 this.altFormatsArray = this.altFormats.split("|");
20880 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20881 v = Date.parseDate(value, this.altFormatsArray[i]);
20887 formatDate : function(date, fmt)
20889 return (!date || !(date instanceof Date)) ?
20890 date : date.dateFormat(fmt || this.format);
20893 onFocus : function()
20895 Roo.bootstrap.DateField.superclass.onFocus.call(this);
20899 onBlur : function()
20901 Roo.bootstrap.DateField.superclass.onBlur.call(this);
20903 var d = this.inputEl().getValue();
20910 showPopup : function()
20912 this.picker().show();
20916 this.fireEvent('showpopup', this, this.date);
20919 hidePopup : function()
20921 if(this.isInline) {
20924 this.picker().hide();
20925 this.viewMode = this.startViewMode;
20928 this.fireEvent('hidepopup', this, this.date);
20932 onMousedown: function(e)
20934 e.stopPropagation();
20935 e.preventDefault();
20940 Roo.bootstrap.DateField.superclass.keyup.call(this);
20944 setValue: function(v)
20946 if(this.fireEvent('beforeselect', this, v) !== false){
20947 var d = new Date(this.parseDate(v) ).clearTime();
20949 if(isNaN(d.getTime())){
20950 this.date = this.viewDate = '';
20951 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20955 v = this.formatDate(d);
20957 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20959 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20963 this.fireEvent('select', this, this.date);
20967 getValue: function()
20969 return this.formatDate(this.date);
20972 fireKey: function(e)
20974 if (!this.picker().isVisible()){
20975 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20981 var dateChanged = false,
20983 newDate, newViewDate;
20988 e.preventDefault();
20992 if (!this.keyboardNavigation) {
20995 dir = e.keyCode == 37 ? -1 : 1;
20998 newDate = this.moveYear(this.date, dir);
20999 newViewDate = this.moveYear(this.viewDate, dir);
21000 } else if (e.shiftKey){
21001 newDate = this.moveMonth(this.date, dir);
21002 newViewDate = this.moveMonth(this.viewDate, dir);
21004 newDate = new Date(this.date);
21005 newDate.setUTCDate(this.date.getUTCDate() + dir);
21006 newViewDate = new Date(this.viewDate);
21007 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21009 if (this.dateWithinRange(newDate)){
21010 this.date = newDate;
21011 this.viewDate = newViewDate;
21012 this.setValue(this.formatDate(this.date));
21014 e.preventDefault();
21015 dateChanged = true;
21020 if (!this.keyboardNavigation) {
21023 dir = e.keyCode == 38 ? -1 : 1;
21025 newDate = this.moveYear(this.date, dir);
21026 newViewDate = this.moveYear(this.viewDate, dir);
21027 } else if (e.shiftKey){
21028 newDate = this.moveMonth(this.date, dir);
21029 newViewDate = this.moveMonth(this.viewDate, dir);
21031 newDate = new Date(this.date);
21032 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21033 newViewDate = new Date(this.viewDate);
21034 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21036 if (this.dateWithinRange(newDate)){
21037 this.date = newDate;
21038 this.viewDate = newViewDate;
21039 this.setValue(this.formatDate(this.date));
21041 e.preventDefault();
21042 dateChanged = true;
21046 this.setValue(this.formatDate(this.date));
21048 e.preventDefault();
21051 this.setValue(this.formatDate(this.date));
21065 onClick: function(e)
21067 e.stopPropagation();
21068 e.preventDefault();
21070 var target = e.getTarget();
21072 if(target.nodeName.toLowerCase() === 'i'){
21073 target = Roo.get(target).dom.parentNode;
21076 var nodeName = target.nodeName;
21077 var className = target.className;
21078 var html = target.innerHTML;
21079 //Roo.log(nodeName);
21081 switch(nodeName.toLowerCase()) {
21083 switch(className) {
21089 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21090 switch(this.viewMode){
21092 this.viewDate = this.moveMonth(this.viewDate, dir);
21096 this.viewDate = this.moveYear(this.viewDate, dir);
21102 var date = new Date();
21103 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21105 this.setValue(this.formatDate(this.date));
21112 if (className.indexOf('disabled') < 0) {
21113 this.viewDate.setUTCDate(1);
21114 if (className.indexOf('month') > -1) {
21115 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21117 var year = parseInt(html, 10) || 0;
21118 this.viewDate.setUTCFullYear(year);
21122 if(this.singleMode){
21123 this.setValue(this.formatDate(this.viewDate));
21134 //Roo.log(className);
21135 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21136 var day = parseInt(html, 10) || 1;
21137 var year = this.viewDate.getUTCFullYear(),
21138 month = this.viewDate.getUTCMonth();
21140 if (className.indexOf('old') > -1) {
21147 } else if (className.indexOf('new') > -1) {
21155 //Roo.log([year,month,day]);
21156 this.date = this.UTCDate(year, month, day,0,0,0,0);
21157 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21159 //Roo.log(this.formatDate(this.date));
21160 this.setValue(this.formatDate(this.date));
21167 setStartDate: function(startDate)
21169 this.startDate = startDate || -Infinity;
21170 if (this.startDate !== -Infinity) {
21171 this.startDate = this.parseDate(this.startDate);
21174 this.updateNavArrows();
21177 setEndDate: function(endDate)
21179 this.endDate = endDate || Infinity;
21180 if (this.endDate !== Infinity) {
21181 this.endDate = this.parseDate(this.endDate);
21184 this.updateNavArrows();
21187 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21189 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21190 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21191 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21193 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21194 return parseInt(d, 10);
21197 this.updateNavArrows();
21200 updateNavArrows: function()
21202 if(this.singleMode){
21206 var d = new Date(this.viewDate),
21207 year = d.getUTCFullYear(),
21208 month = d.getUTCMonth();
21210 Roo.each(this.picker().select('.prev', true).elements, function(v){
21212 switch (this.viewMode) {
21215 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21221 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21228 Roo.each(this.picker().select('.next', true).elements, function(v){
21230 switch (this.viewMode) {
21233 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21239 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21247 moveMonth: function(date, dir)
21252 var new_date = new Date(date.valueOf()),
21253 day = new_date.getUTCDate(),
21254 month = new_date.getUTCMonth(),
21255 mag = Math.abs(dir),
21257 dir = dir > 0 ? 1 : -1;
21260 // If going back one month, make sure month is not current month
21261 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21263 return new_date.getUTCMonth() == month;
21265 // If going forward one month, make sure month is as expected
21266 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21268 return new_date.getUTCMonth() != new_month;
21270 new_month = month + dir;
21271 new_date.setUTCMonth(new_month);
21272 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21273 if (new_month < 0 || new_month > 11) {
21274 new_month = (new_month + 12) % 12;
21277 // For magnitudes >1, move one month at a time...
21278 for (var i=0; i<mag; i++) {
21279 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21280 new_date = this.moveMonth(new_date, dir);
21282 // ...then reset the day, keeping it in the new month
21283 new_month = new_date.getUTCMonth();
21284 new_date.setUTCDate(day);
21286 return new_month != new_date.getUTCMonth();
21289 // Common date-resetting loop -- if date is beyond end of month, make it
21292 new_date.setUTCDate(--day);
21293 new_date.setUTCMonth(new_month);
21298 moveYear: function(date, dir)
21300 return this.moveMonth(date, dir*12);
21303 dateWithinRange: function(date)
21305 return date >= this.startDate && date <= this.endDate;
21311 this.picker().remove();
21314 validateValue : function(value)
21316 if(this.getVisibilityEl().hasClass('hidden')){
21320 if(value.length < 1) {
21321 if(this.allowBlank){
21327 if(value.length < this.minLength){
21330 if(value.length > this.maxLength){
21334 var vt = Roo.form.VTypes;
21335 if(!vt[this.vtype](value, this)){
21339 if(typeof this.validator == "function"){
21340 var msg = this.validator(value);
21346 if(this.regex && !this.regex.test(value)){
21350 if(typeof(this.parseDate(value)) == 'undefined'){
21354 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21358 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21368 this.date = this.viewDate = '';
21370 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21375 Roo.apply(Roo.bootstrap.DateField, {
21386 html: '<i class="fa fa-arrow-left"/>'
21396 html: '<i class="fa fa-arrow-right"/>'
21438 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21439 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21440 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21441 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21442 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21455 navFnc: 'FullYear',
21460 navFnc: 'FullYear',
21465 Roo.apply(Roo.bootstrap.DateField, {
21469 cls: 'datepicker dropdown-menu roo-dynamic',
21473 cls: 'datepicker-days',
21477 cls: 'table-condensed',
21479 Roo.bootstrap.DateField.head,
21483 Roo.bootstrap.DateField.footer
21490 cls: 'datepicker-months',
21494 cls: 'table-condensed',
21496 Roo.bootstrap.DateField.head,
21497 Roo.bootstrap.DateField.content,
21498 Roo.bootstrap.DateField.footer
21505 cls: 'datepicker-years',
21509 cls: 'table-condensed',
21511 Roo.bootstrap.DateField.head,
21512 Roo.bootstrap.DateField.content,
21513 Roo.bootstrap.DateField.footer
21532 * @class Roo.bootstrap.TimeField
21533 * @extends Roo.bootstrap.Input
21534 * Bootstrap DateField class
21538 * Create a new TimeField
21539 * @param {Object} config The config object
21542 Roo.bootstrap.TimeField = function(config){
21543 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21547 * Fires when this field show.
21548 * @param {Roo.bootstrap.DateField} thisthis
21549 * @param {Mixed} date The date value
21554 * Fires when this field hide.
21555 * @param {Roo.bootstrap.DateField} this
21556 * @param {Mixed} date The date value
21561 * Fires when select a date.
21562 * @param {Roo.bootstrap.DateField} this
21563 * @param {Mixed} date The date value
21569 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21572 * @cfg {String} format
21573 * The default time format string which can be overriden for localization support. The format must be
21574 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21578 onRender: function(ct, position)
21581 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21583 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21585 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21587 this.pop = this.picker().select('>.datepicker-time',true).first();
21588 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21590 this.picker().on('mousedown', this.onMousedown, this);
21591 this.picker().on('click', this.onClick, this);
21593 this.picker().addClass('datepicker-dropdown');
21598 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21599 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21600 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21601 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21602 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21603 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21607 fireKey: function(e){
21608 if (!this.picker().isVisible()){
21609 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21615 e.preventDefault();
21623 this.onTogglePeriod();
21626 this.onIncrementMinutes();
21629 this.onDecrementMinutes();
21638 onClick: function(e) {
21639 e.stopPropagation();
21640 e.preventDefault();
21643 picker : function()
21645 return this.el.select('.datepicker', true).first();
21648 fillTime: function()
21650 var time = this.pop.select('tbody', true).first();
21652 time.dom.innerHTML = '';
21667 cls: 'hours-up glyphicon glyphicon-chevron-up'
21687 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21708 cls: 'timepicker-hour',
21723 cls: 'timepicker-minute',
21738 cls: 'btn btn-primary period',
21760 cls: 'hours-down glyphicon glyphicon-chevron-down'
21780 cls: 'minutes-down glyphicon glyphicon-chevron-down'
21798 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21805 var hours = this.time.getHours();
21806 var minutes = this.time.getMinutes();
21819 hours = hours - 12;
21823 hours = '0' + hours;
21827 minutes = '0' + minutes;
21830 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21831 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21832 this.pop.select('button', true).first().dom.innerHTML = period;
21838 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21840 var cls = ['bottom'];
21842 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21849 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21854 this.picker().addClass(cls.join('-'));
21858 Roo.each(cls, function(c){
21860 _this.picker().setTop(_this.inputEl().getHeight());
21864 _this.picker().setTop(0 - _this.picker().getHeight());
21869 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21873 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21880 onFocus : function()
21882 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21886 onBlur : function()
21888 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21894 this.picker().show();
21899 this.fireEvent('show', this, this.date);
21904 this.picker().hide();
21907 this.fireEvent('hide', this, this.date);
21910 setTime : function()
21913 this.setValue(this.time.format(this.format));
21915 this.fireEvent('select', this, this.date);
21920 onMousedown: function(e){
21921 e.stopPropagation();
21922 e.preventDefault();
21925 onIncrementHours: function()
21927 Roo.log('onIncrementHours');
21928 this.time = this.time.add(Date.HOUR, 1);
21933 onDecrementHours: function()
21935 Roo.log('onDecrementHours');
21936 this.time = this.time.add(Date.HOUR, -1);
21940 onIncrementMinutes: function()
21942 Roo.log('onIncrementMinutes');
21943 this.time = this.time.add(Date.MINUTE, 1);
21947 onDecrementMinutes: function()
21949 Roo.log('onDecrementMinutes');
21950 this.time = this.time.add(Date.MINUTE, -1);
21954 onTogglePeriod: function()
21956 Roo.log('onTogglePeriod');
21957 this.time = this.time.add(Date.HOUR, 12);
21964 Roo.apply(Roo.bootstrap.TimeField, {
21994 cls: 'btn btn-info ok',
22006 Roo.apply(Roo.bootstrap.TimeField, {
22010 cls: 'datepicker dropdown-menu',
22014 cls: 'datepicker-time',
22018 cls: 'table-condensed',
22020 Roo.bootstrap.TimeField.content,
22021 Roo.bootstrap.TimeField.footer
22040 * @class Roo.bootstrap.MonthField
22041 * @extends Roo.bootstrap.Input
22042 * Bootstrap MonthField class
22044 * @cfg {String} language default en
22047 * Create a new MonthField
22048 * @param {Object} config The config object
22051 Roo.bootstrap.MonthField = function(config){
22052 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22057 * Fires when this field show.
22058 * @param {Roo.bootstrap.MonthField} this
22059 * @param {Mixed} date The date value
22064 * Fires when this field hide.
22065 * @param {Roo.bootstrap.MonthField} this
22066 * @param {Mixed} date The date value
22071 * Fires when select a date.
22072 * @param {Roo.bootstrap.MonthField} this
22073 * @param {String} oldvalue The old value
22074 * @param {String} newvalue The new value
22080 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22082 onRender: function(ct, position)
22085 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22087 this.language = this.language || 'en';
22088 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22089 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22091 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22092 this.isInline = false;
22093 this.isInput = true;
22094 this.component = this.el.select('.add-on', true).first() || false;
22095 this.component = (this.component && this.component.length === 0) ? false : this.component;
22096 this.hasInput = this.component && this.inputEL().length;
22098 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22100 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22102 this.picker().on('mousedown', this.onMousedown, this);
22103 this.picker().on('click', this.onClick, this);
22105 this.picker().addClass('datepicker-dropdown');
22107 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22108 v.setStyle('width', '189px');
22115 if(this.isInline) {
22121 setValue: function(v, suppressEvent)
22123 var o = this.getValue();
22125 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22129 if(suppressEvent !== true){
22130 this.fireEvent('select', this, o, v);
22135 getValue: function()
22140 onClick: function(e)
22142 e.stopPropagation();
22143 e.preventDefault();
22145 var target = e.getTarget();
22147 if(target.nodeName.toLowerCase() === 'i'){
22148 target = Roo.get(target).dom.parentNode;
22151 var nodeName = target.nodeName;
22152 var className = target.className;
22153 var html = target.innerHTML;
22155 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22159 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22161 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22167 picker : function()
22169 return this.pickerEl;
22172 fillMonths: function()
22175 var months = this.picker().select('>.datepicker-months td', true).first();
22177 months.dom.innerHTML = '';
22183 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22186 months.createChild(month);
22195 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22196 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22199 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22200 e.removeClass('active');
22202 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22203 e.addClass('active');
22210 if(this.isInline) {
22214 this.picker().removeClass(['bottom', 'top']);
22216 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22218 * place to the top of element!
22222 this.picker().addClass('top');
22223 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22228 this.picker().addClass('bottom');
22230 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22233 onFocus : function()
22235 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22239 onBlur : function()
22241 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22243 var d = this.inputEl().getValue();
22252 this.picker().show();
22253 this.picker().select('>.datepicker-months', true).first().show();
22257 this.fireEvent('show', this, this.date);
22262 if(this.isInline) {
22265 this.picker().hide();
22266 this.fireEvent('hide', this, this.date);
22270 onMousedown: function(e)
22272 e.stopPropagation();
22273 e.preventDefault();
22278 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22282 fireKey: function(e)
22284 if (!this.picker().isVisible()){
22285 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22296 e.preventDefault();
22300 dir = e.keyCode == 37 ? -1 : 1;
22302 this.vIndex = this.vIndex + dir;
22304 if(this.vIndex < 0){
22308 if(this.vIndex > 11){
22312 if(isNaN(this.vIndex)){
22316 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22322 dir = e.keyCode == 38 ? -1 : 1;
22324 this.vIndex = this.vIndex + dir * 4;
22326 if(this.vIndex < 0){
22330 if(this.vIndex > 11){
22334 if(isNaN(this.vIndex)){
22338 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22343 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22344 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22348 e.preventDefault();
22351 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22352 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22368 this.picker().remove();
22373 Roo.apply(Roo.bootstrap.MonthField, {
22392 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22393 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22398 Roo.apply(Roo.bootstrap.MonthField, {
22402 cls: 'datepicker dropdown-menu roo-dynamic',
22406 cls: 'datepicker-months',
22410 cls: 'table-condensed',
22412 Roo.bootstrap.DateField.content
22432 * @class Roo.bootstrap.CheckBox
22433 * @extends Roo.bootstrap.Input
22434 * Bootstrap CheckBox class
22436 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22437 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22438 * @cfg {String} boxLabel The text that appears beside the checkbox
22439 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22440 * @cfg {Boolean} checked initnal the element
22441 * @cfg {Boolean} inline inline the element (default false)
22442 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22443 * @cfg {String} tooltip label tooltip
22446 * Create a new CheckBox
22447 * @param {Object} config The config object
22450 Roo.bootstrap.CheckBox = function(config){
22451 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22456 * Fires when the element is checked or unchecked.
22457 * @param {Roo.bootstrap.CheckBox} this This input
22458 * @param {Boolean} checked The new checked value
22463 * Fires when the element is click.
22464 * @param {Roo.bootstrap.CheckBox} this This input
22471 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22473 inputType: 'checkbox',
22482 // checkbox success does not make any sense really..
22487 getAutoCreate : function()
22489 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22495 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22498 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22504 type : this.inputType,
22505 value : this.inputValue,
22506 cls : 'roo-' + this.inputType, //'form-box',
22507 placeholder : this.placeholder || ''
22511 if(this.inputType != 'radio'){
22515 cls : 'roo-hidden-value',
22516 value : this.checked ? this.inputValue : this.valueOff
22521 if (this.weight) { // Validity check?
22522 cfg.cls += " " + this.inputType + "-" + this.weight;
22525 if (this.disabled) {
22526 input.disabled=true;
22530 input.checked = this.checked;
22535 input.name = this.name;
22537 if(this.inputType != 'radio'){
22538 hidden.name = this.name;
22539 input.name = '_hidden_' + this.name;
22544 input.cls += ' input-' + this.size;
22549 ['xs','sm','md','lg'].map(function(size){
22550 if (settings[size]) {
22551 cfg.cls += ' col-' + size + '-' + settings[size];
22555 var inputblock = input;
22557 if (this.before || this.after) {
22560 cls : 'input-group',
22565 inputblock.cn.push({
22567 cls : 'input-group-addon',
22572 inputblock.cn.push(input);
22574 if(this.inputType != 'radio'){
22575 inputblock.cn.push(hidden);
22579 inputblock.cn.push({
22581 cls : 'input-group-addon',
22587 var boxLabelCfg = false;
22593 //'for': id, // box label is handled by onclick - so no for...
22595 html: this.boxLabel
22598 boxLabelCfg.tooltip = this.tooltip;
22604 if (align ==='left' && this.fieldLabel.length) {
22605 // Roo.log("left and has label");
22610 cls : 'control-label',
22611 html : this.fieldLabel
22622 cfg.cn[1].cn.push(boxLabelCfg);
22625 if(this.labelWidth > 12){
22626 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22629 if(this.labelWidth < 13 && this.labelmd == 0){
22630 this.labelmd = this.labelWidth;
22633 if(this.labellg > 0){
22634 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22635 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22638 if(this.labelmd > 0){
22639 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22640 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22643 if(this.labelsm > 0){
22644 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22645 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22648 if(this.labelxs > 0){
22649 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22650 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22653 } else if ( this.fieldLabel.length) {
22654 // Roo.log(" label");
22658 tag: this.boxLabel ? 'span' : 'label',
22660 cls: 'control-label box-input-label',
22661 //cls : 'input-group-addon',
22662 html : this.fieldLabel
22669 cfg.cn.push(boxLabelCfg);
22674 // Roo.log(" no label && no align");
22675 cfg.cn = [ inputblock ] ;
22677 cfg.cn.push(boxLabelCfg);
22685 if(this.inputType != 'radio'){
22686 cfg.cn.push(hidden);
22694 * return the real input element.
22696 inputEl: function ()
22698 return this.el.select('input.roo-' + this.inputType,true).first();
22700 hiddenEl: function ()
22702 return this.el.select('input.roo-hidden-value',true).first();
22705 labelEl: function()
22707 return this.el.select('label.control-label',true).first();
22709 /* depricated... */
22713 return this.labelEl();
22716 boxLabelEl: function()
22718 return this.el.select('label.box-label',true).first();
22721 initEvents : function()
22723 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22725 this.inputEl().on('click', this.onClick, this);
22727 if (this.boxLabel) {
22728 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22731 this.startValue = this.getValue();
22734 Roo.bootstrap.CheckBox.register(this);
22738 onClick : function(e)
22740 if(this.fireEvent('click', this, e) !== false){
22741 this.setChecked(!this.checked);
22746 setChecked : function(state,suppressEvent)
22748 this.startValue = this.getValue();
22750 if(this.inputType == 'radio'){
22752 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22753 e.dom.checked = false;
22756 this.inputEl().dom.checked = true;
22758 this.inputEl().dom.value = this.inputValue;
22760 if(suppressEvent !== true){
22761 this.fireEvent('check', this, true);
22769 this.checked = state;
22771 this.inputEl().dom.checked = state;
22774 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22776 if(suppressEvent !== true){
22777 this.fireEvent('check', this, state);
22783 getValue : function()
22785 if(this.inputType == 'radio'){
22786 return this.getGroupValue();
22789 return this.hiddenEl().dom.value;
22793 getGroupValue : function()
22795 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22799 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22802 setValue : function(v,suppressEvent)
22804 if(this.inputType == 'radio'){
22805 this.setGroupValue(v, suppressEvent);
22809 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22814 setGroupValue : function(v, suppressEvent)
22816 this.startValue = this.getValue();
22818 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22819 e.dom.checked = false;
22821 if(e.dom.value == v){
22822 e.dom.checked = true;
22826 if(suppressEvent !== true){
22827 this.fireEvent('check', this, true);
22835 validate : function()
22837 if(this.getVisibilityEl().hasClass('hidden')){
22843 (this.inputType == 'radio' && this.validateRadio()) ||
22844 (this.inputType == 'checkbox' && this.validateCheckbox())
22850 this.markInvalid();
22854 validateRadio : function()
22856 if(this.getVisibilityEl().hasClass('hidden')){
22860 if(this.allowBlank){
22866 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22867 if(!e.dom.checked){
22879 validateCheckbox : function()
22882 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22883 //return (this.getValue() == this.inputValue) ? true : false;
22886 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22894 for(var i in group){
22895 if(group[i].el.isVisible(true)){
22903 for(var i in group){
22908 r = (group[i].getValue() == group[i].inputValue) ? true : false;
22915 * Mark this field as valid
22917 markValid : function()
22921 this.fireEvent('valid', this);
22923 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22926 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22933 if(this.inputType == 'radio'){
22934 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22935 var fg = e.findParent('.form-group', false, true);
22936 if (Roo.bootstrap.version == 3) {
22937 fg.removeClass([_this.invalidClass, _this.validClass]);
22938 fg.addClass(_this.validClass);
22940 fg.removeClass(['is-valid', 'is-invalid']);
22941 fg.addClass('is-valid');
22949 var fg = this.el.findParent('.form-group', false, true);
22950 if (Roo.bootstrap.version == 3) {
22951 fg.removeClass([this.invalidClass, this.validClass]);
22952 fg.addClass(this.validClass);
22954 fg.removeClass(['is-valid', 'is-invalid']);
22955 fg.addClass('is-valid');
22960 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22966 for(var i in group){
22967 var fg = group[i].el.findParent('.form-group', false, true);
22968 if (Roo.bootstrap.version == 3) {
22969 fg.removeClass([this.invalidClass, this.validClass]);
22970 fg.addClass(this.validClass);
22972 fg.removeClass(['is-valid', 'is-invalid']);
22973 fg.addClass('is-valid');
22979 * Mark this field as invalid
22980 * @param {String} msg The validation message
22982 markInvalid : function(msg)
22984 if(this.allowBlank){
22990 this.fireEvent('invalid', this, msg);
22992 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22995 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22999 label.markInvalid();
23002 if(this.inputType == 'radio'){
23004 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23005 var fg = e.findParent('.form-group', false, true);
23006 if (Roo.bootstrap.version == 3) {
23007 fg.removeClass([_this.invalidClass, _this.validClass]);
23008 fg.addClass(_this.invalidClass);
23010 fg.removeClass(['is-invalid', 'is-valid']);
23011 fg.addClass('is-invalid');
23019 var fg = this.el.findParent('.form-group', false, true);
23020 if (Roo.bootstrap.version == 3) {
23021 fg.removeClass([_this.invalidClass, _this.validClass]);
23022 fg.addClass(_this.invalidClass);
23024 fg.removeClass(['is-invalid', 'is-valid']);
23025 fg.addClass('is-invalid');
23030 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23036 for(var i in group){
23037 var fg = group[i].el.findParent('.form-group', false, true);
23038 if (Roo.bootstrap.version == 3) {
23039 fg.removeClass([_this.invalidClass, _this.validClass]);
23040 fg.addClass(_this.invalidClass);
23042 fg.removeClass(['is-invalid', 'is-valid']);
23043 fg.addClass('is-invalid');
23049 clearInvalid : function()
23051 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23053 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23055 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23057 if (label && label.iconEl) {
23058 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23059 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23063 disable : function()
23065 if(this.inputType != 'radio'){
23066 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23073 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23074 _this.getActionEl().addClass(this.disabledClass);
23075 e.dom.disabled = true;
23079 this.disabled = true;
23080 this.fireEvent("disable", this);
23084 enable : function()
23086 if(this.inputType != 'radio'){
23087 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23094 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23095 _this.getActionEl().removeClass(this.disabledClass);
23096 e.dom.disabled = false;
23100 this.disabled = false;
23101 this.fireEvent("enable", this);
23105 setBoxLabel : function(v)
23110 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23116 Roo.apply(Roo.bootstrap.CheckBox, {
23121 * register a CheckBox Group
23122 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23124 register : function(checkbox)
23126 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23127 this.groups[checkbox.groupId] = {};
23130 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23134 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23138 * fetch a CheckBox Group based on the group ID
23139 * @param {string} the group ID
23140 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23142 get: function(groupId) {
23143 if (typeof(this.groups[groupId]) == 'undefined') {
23147 return this.groups[groupId] ;
23160 * @class Roo.bootstrap.Radio
23161 * @extends Roo.bootstrap.Component
23162 * Bootstrap Radio class
23163 * @cfg {String} boxLabel - the label associated
23164 * @cfg {String} value - the value of radio
23167 * Create a new Radio
23168 * @param {Object} config The config object
23170 Roo.bootstrap.Radio = function(config){
23171 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23175 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23181 getAutoCreate : function()
23185 cls : 'form-group radio',
23190 html : this.boxLabel
23198 initEvents : function()
23200 this.parent().register(this);
23202 this.el.on('click', this.onClick, this);
23206 onClick : function(e)
23208 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23209 this.setChecked(true);
23213 setChecked : function(state, suppressEvent)
23215 this.parent().setValue(this.value, suppressEvent);
23219 setBoxLabel : function(v)
23224 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23239 * @class Roo.bootstrap.SecurePass
23240 * @extends Roo.bootstrap.Input
23241 * Bootstrap SecurePass class
23245 * Create a new SecurePass
23246 * @param {Object} config The config object
23249 Roo.bootstrap.SecurePass = function (config) {
23250 // these go here, so the translation tool can replace them..
23252 PwdEmpty: "Please type a password, and then retype it to confirm.",
23253 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23254 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23255 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23256 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23257 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23258 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23259 TooWeak: "Your password is Too Weak."
23261 this.meterLabel = "Password strength:";
23262 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23263 this.meterClass = [
23264 "roo-password-meter-tooweak",
23265 "roo-password-meter-weak",
23266 "roo-password-meter-medium",
23267 "roo-password-meter-strong",
23268 "roo-password-meter-grey"
23273 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23276 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23278 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23280 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23281 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23282 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23283 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23284 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23285 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23286 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23296 * @cfg {String/Object} Label for the strength meter (defaults to
23297 * 'Password strength:')
23302 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23303 * ['Weak', 'Medium', 'Strong'])
23306 pwdStrengths: false,
23319 initEvents: function ()
23321 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23323 if (this.el.is('input[type=password]') && Roo.isSafari) {
23324 this.el.on('keydown', this.SafariOnKeyDown, this);
23327 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23330 onRender: function (ct, position)
23332 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23333 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23334 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23336 this.trigger.createChild({
23341 cls: 'roo-password-meter-grey col-xs-12',
23344 //width: this.meterWidth + 'px'
23348 cls: 'roo-password-meter-text'
23354 if (this.hideTrigger) {
23355 this.trigger.setDisplayed(false);
23357 this.setSize(this.width || '', this.height || '');
23360 onDestroy: function ()
23362 if (this.trigger) {
23363 this.trigger.removeAllListeners();
23364 this.trigger.remove();
23367 this.wrap.remove();
23369 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23372 checkStrength: function ()
23374 var pwd = this.inputEl().getValue();
23375 if (pwd == this._lastPwd) {
23380 if (this.ClientSideStrongPassword(pwd)) {
23382 } else if (this.ClientSideMediumPassword(pwd)) {
23384 } else if (this.ClientSideWeakPassword(pwd)) {
23390 Roo.log('strength1: ' + strength);
23392 //var pm = this.trigger.child('div/div/div').dom;
23393 var pm = this.trigger.child('div/div');
23394 pm.removeClass(this.meterClass);
23395 pm.addClass(this.meterClass[strength]);
23398 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23400 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23402 this._lastPwd = pwd;
23406 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23408 this._lastPwd = '';
23410 var pm = this.trigger.child('div/div');
23411 pm.removeClass(this.meterClass);
23412 pm.addClass('roo-password-meter-grey');
23415 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23418 this.inputEl().dom.type='password';
23421 validateValue: function (value)
23424 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23427 if (value.length == 0) {
23428 if (this.allowBlank) {
23429 this.clearInvalid();
23433 this.markInvalid(this.errors.PwdEmpty);
23434 this.errorMsg = this.errors.PwdEmpty;
23442 if ('[\x21-\x7e]*'.match(value)) {
23443 this.markInvalid(this.errors.PwdBadChar);
23444 this.errorMsg = this.errors.PwdBadChar;
23447 if (value.length < 6) {
23448 this.markInvalid(this.errors.PwdShort);
23449 this.errorMsg = this.errors.PwdShort;
23452 if (value.length > 16) {
23453 this.markInvalid(this.errors.PwdLong);
23454 this.errorMsg = this.errors.PwdLong;
23458 if (this.ClientSideStrongPassword(value)) {
23460 } else if (this.ClientSideMediumPassword(value)) {
23462 } else if (this.ClientSideWeakPassword(value)) {
23469 if (strength < 2) {
23470 //this.markInvalid(this.errors.TooWeak);
23471 this.errorMsg = this.errors.TooWeak;
23476 console.log('strength2: ' + strength);
23478 //var pm = this.trigger.child('div/div/div').dom;
23480 var pm = this.trigger.child('div/div');
23481 pm.removeClass(this.meterClass);
23482 pm.addClass(this.meterClass[strength]);
23484 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23486 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23488 this.errorMsg = '';
23492 CharacterSetChecks: function (type)
23495 this.fResult = false;
23498 isctype: function (character, type)
23501 case this.kCapitalLetter:
23502 if (character >= 'A' && character <= 'Z') {
23507 case this.kSmallLetter:
23508 if (character >= 'a' && character <= 'z') {
23514 if (character >= '0' && character <= '9') {
23519 case this.kPunctuation:
23520 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23531 IsLongEnough: function (pwd, size)
23533 return !(pwd == null || isNaN(size) || pwd.length < size);
23536 SpansEnoughCharacterSets: function (word, nb)
23538 if (!this.IsLongEnough(word, nb))
23543 var characterSetChecks = new Array(
23544 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23545 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23548 for (var index = 0; index < word.length; ++index) {
23549 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23550 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23551 characterSetChecks[nCharSet].fResult = true;
23558 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23559 if (characterSetChecks[nCharSet].fResult) {
23564 if (nCharSets < nb) {
23570 ClientSideStrongPassword: function (pwd)
23572 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23575 ClientSideMediumPassword: function (pwd)
23577 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23580 ClientSideWeakPassword: function (pwd)
23582 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23585 })//<script type="text/javascript">
23588 * Based Ext JS Library 1.1.1
23589 * Copyright(c) 2006-2007, Ext JS, LLC.
23595 * @class Roo.HtmlEditorCore
23596 * @extends Roo.Component
23597 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23599 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23602 Roo.HtmlEditorCore = function(config){
23605 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23610 * @event initialize
23611 * Fires when the editor is fully initialized (including the iframe)
23612 * @param {Roo.HtmlEditorCore} this
23617 * Fires when the editor is first receives the focus. Any insertion must wait
23618 * until after this event.
23619 * @param {Roo.HtmlEditorCore} this
23623 * @event beforesync
23624 * Fires before the textarea is updated with content from the editor iframe. Return false
23625 * to cancel the sync.
23626 * @param {Roo.HtmlEditorCore} this
23627 * @param {String} html
23631 * @event beforepush
23632 * Fires before the iframe editor is updated with content from the textarea. Return false
23633 * to cancel the push.
23634 * @param {Roo.HtmlEditorCore} this
23635 * @param {String} html
23640 * Fires when the textarea is updated with content from the editor iframe.
23641 * @param {Roo.HtmlEditorCore} this
23642 * @param {String} html
23647 * Fires when the iframe editor is updated with content from the textarea.
23648 * @param {Roo.HtmlEditorCore} this
23649 * @param {String} html
23654 * @event editorevent
23655 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23656 * @param {Roo.HtmlEditorCore} this
23662 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23664 // defaults : white / black...
23665 this.applyBlacklists();
23672 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23676 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23682 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23687 * @cfg {Number} height (in pixels)
23691 * @cfg {Number} width (in pixels)
23696 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23699 stylesheets: false,
23704 // private properties
23705 validationEvent : false,
23707 initialized : false,
23709 sourceEditMode : false,
23710 onFocus : Roo.emptyFn,
23712 hideMode:'offsets',
23716 // blacklist + whitelisted elements..
23723 * Protected method that will not generally be called directly. It
23724 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23725 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23727 getDocMarkup : function(){
23731 // inherit styels from page...??
23732 if (this.stylesheets === false) {
23734 Roo.get(document.head).select('style').each(function(node) {
23735 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23738 Roo.get(document.head).select('link').each(function(node) {
23739 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23742 } else if (!this.stylesheets.length) {
23744 st = '<style type="text/css">' +
23745 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23748 st = '<style type="text/css">' +
23753 st += '<style type="text/css">' +
23754 'IMG { cursor: pointer } ' +
23757 var cls = 'roo-htmleditor-body';
23759 if(this.bodyCls.length){
23760 cls += ' ' + this.bodyCls;
23763 return '<html><head>' + st +
23764 //<style type="text/css">' +
23765 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23767 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23771 onRender : function(ct, position)
23774 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23775 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23778 this.el.dom.style.border = '0 none';
23779 this.el.dom.setAttribute('tabIndex', -1);
23780 this.el.addClass('x-hidden hide');
23784 if(Roo.isIE){ // fix IE 1px bogus margin
23785 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23789 this.frameId = Roo.id();
23793 var iframe = this.owner.wrap.createChild({
23795 cls: 'form-control', // bootstrap..
23797 name: this.frameId,
23798 frameBorder : 'no',
23799 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
23804 this.iframe = iframe.dom;
23806 this.assignDocWin();
23808 this.doc.designMode = 'on';
23811 this.doc.write(this.getDocMarkup());
23815 var task = { // must defer to wait for browser to be ready
23817 //console.log("run task?" + this.doc.readyState);
23818 this.assignDocWin();
23819 if(this.doc.body || this.doc.readyState == 'complete'){
23821 this.doc.designMode="on";
23825 Roo.TaskMgr.stop(task);
23826 this.initEditor.defer(10, this);
23833 Roo.TaskMgr.start(task);
23838 onResize : function(w, h)
23840 Roo.log('resize: ' +w + ',' + h );
23841 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23845 if(typeof w == 'number'){
23847 this.iframe.style.width = w + 'px';
23849 if(typeof h == 'number'){
23851 this.iframe.style.height = h + 'px';
23853 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23860 * Toggles the editor between standard and source edit mode.
23861 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23863 toggleSourceEdit : function(sourceEditMode){
23865 this.sourceEditMode = sourceEditMode === true;
23867 if(this.sourceEditMode){
23869 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
23872 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23873 //this.iframe.className = '';
23876 //this.setSize(this.owner.wrap.getSize());
23877 //this.fireEvent('editmodechange', this, this.sourceEditMode);
23884 * Protected method that will not generally be called directly. If you need/want
23885 * custom HTML cleanup, this is the method you should override.
23886 * @param {String} html The HTML to be cleaned
23887 * return {String} The cleaned HTML
23889 cleanHtml : function(html){
23890 html = String(html);
23891 if(html.length > 5){
23892 if(Roo.isSafari){ // strip safari nonsense
23893 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23896 if(html == ' '){
23903 * HTML Editor -> Textarea
23904 * Protected method that will not generally be called directly. Syncs the contents
23905 * of the editor iframe with the textarea.
23907 syncValue : function(){
23908 if(this.initialized){
23909 var bd = (this.doc.body || this.doc.documentElement);
23910 //this.cleanUpPaste(); -- this is done else where and causes havoc..
23911 var html = bd.innerHTML;
23913 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23914 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23916 html = '<div style="'+m[0]+'">' + html + '</div>';
23919 html = this.cleanHtml(html);
23920 // fix up the special chars.. normaly like back quotes in word...
23921 // however we do not want to do this with chinese..
23922 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23924 var cc = match.charCodeAt();
23926 // Get the character value, handling surrogate pairs
23927 if (match.length == 2) {
23928 // It's a surrogate pair, calculate the Unicode code point
23929 var high = match.charCodeAt(0) - 0xD800;
23930 var low = match.charCodeAt(1) - 0xDC00;
23931 cc = (high * 0x400) + low + 0x10000;
23933 (cc >= 0x4E00 && cc < 0xA000 ) ||
23934 (cc >= 0x3400 && cc < 0x4E00 ) ||
23935 (cc >= 0xf900 && cc < 0xfb00 )
23940 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23941 return "&#" + cc + ";";
23948 if(this.owner.fireEvent('beforesync', this, html) !== false){
23949 this.el.dom.value = html;
23950 this.owner.fireEvent('sync', this, html);
23956 * Protected method that will not generally be called directly. Pushes the value of the textarea
23957 * into the iframe editor.
23959 pushValue : function(){
23960 if(this.initialized){
23961 var v = this.el.dom.value.trim();
23963 // if(v.length < 1){
23967 if(this.owner.fireEvent('beforepush', this, v) !== false){
23968 var d = (this.doc.body || this.doc.documentElement);
23970 this.cleanUpPaste();
23971 this.el.dom.value = d.innerHTML;
23972 this.owner.fireEvent('push', this, v);
23978 deferFocus : function(){
23979 this.focus.defer(10, this);
23983 focus : function(){
23984 if(this.win && !this.sourceEditMode){
23991 assignDocWin: function()
23993 var iframe = this.iframe;
23996 this.doc = iframe.contentWindow.document;
23997 this.win = iframe.contentWindow;
23999 // if (!Roo.get(this.frameId)) {
24002 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24003 // this.win = Roo.get(this.frameId).dom.contentWindow;
24005 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24009 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24010 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24015 initEditor : function(){
24016 //console.log("INIT EDITOR");
24017 this.assignDocWin();
24021 this.doc.designMode="on";
24023 this.doc.write(this.getDocMarkup());
24026 var dbody = (this.doc.body || this.doc.documentElement);
24027 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24028 // this copies styles from the containing element into thsi one..
24029 // not sure why we need all of this..
24030 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24032 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24033 //ss['background-attachment'] = 'fixed'; // w3c
24034 dbody.bgProperties = 'fixed'; // ie
24035 //Roo.DomHelper.applyStyles(dbody, ss);
24036 Roo.EventManager.on(this.doc, {
24037 //'mousedown': this.onEditorEvent,
24038 'mouseup': this.onEditorEvent,
24039 'dblclick': this.onEditorEvent,
24040 'click': this.onEditorEvent,
24041 'keyup': this.onEditorEvent,
24046 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24048 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24049 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24051 this.initialized = true;
24053 this.owner.fireEvent('initialize', this);
24058 onDestroy : function(){
24064 //for (var i =0; i < this.toolbars.length;i++) {
24065 // // fixme - ask toolbars for heights?
24066 // this.toolbars[i].onDestroy();
24069 //this.wrap.dom.innerHTML = '';
24070 //this.wrap.remove();
24075 onFirstFocus : function(){
24077 this.assignDocWin();
24080 this.activated = true;
24083 if(Roo.isGecko){ // prevent silly gecko errors
24085 var s = this.win.getSelection();
24086 if(!s.focusNode || s.focusNode.nodeType != 3){
24087 var r = s.getRangeAt(0);
24088 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24093 this.execCmd('useCSS', true);
24094 this.execCmd('styleWithCSS', false);
24097 this.owner.fireEvent('activate', this);
24101 adjustFont: function(btn){
24102 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24103 //if(Roo.isSafari){ // safari
24106 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24107 if(Roo.isSafari){ // safari
24108 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24109 v = (v < 10) ? 10 : v;
24110 v = (v > 48) ? 48 : v;
24111 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24116 v = Math.max(1, v+adjust);
24118 this.execCmd('FontSize', v );
24121 onEditorEvent : function(e)
24123 this.owner.fireEvent('editorevent', this, e);
24124 // this.updateToolbar();
24125 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24128 insertTag : function(tg)
24130 // could be a bit smarter... -> wrap the current selected tRoo..
24131 if (tg.toLowerCase() == 'span' ||
24132 tg.toLowerCase() == 'code' ||
24133 tg.toLowerCase() == 'sup' ||
24134 tg.toLowerCase() == 'sub'
24137 range = this.createRange(this.getSelection());
24138 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24139 wrappingNode.appendChild(range.extractContents());
24140 range.insertNode(wrappingNode);
24147 this.execCmd("formatblock", tg);
24151 insertText : function(txt)
24155 var range = this.createRange();
24156 range.deleteContents();
24157 //alert(Sender.getAttribute('label'));
24159 range.insertNode(this.doc.createTextNode(txt));
24165 * Executes a Midas editor command on the editor document and performs necessary focus and
24166 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24167 * @param {String} cmd The Midas command
24168 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24170 relayCmd : function(cmd, value){
24172 this.execCmd(cmd, value);
24173 this.owner.fireEvent('editorevent', this);
24174 //this.updateToolbar();
24175 this.owner.deferFocus();
24179 * Executes a Midas editor command directly on the editor document.
24180 * For visual commands, you should use {@link #relayCmd} instead.
24181 * <b>This should only be called after the editor is initialized.</b>
24182 * @param {String} cmd The Midas command
24183 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24185 execCmd : function(cmd, value){
24186 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24193 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24195 * @param {String} text | dom node..
24197 insertAtCursor : function(text)
24200 if(!this.activated){
24206 var r = this.doc.selection.createRange();
24217 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24221 // from jquery ui (MIT licenced)
24223 var win = this.win;
24225 if (win.getSelection && win.getSelection().getRangeAt) {
24226 range = win.getSelection().getRangeAt(0);
24227 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24228 range.insertNode(node);
24229 } else if (win.document.selection && win.document.selection.createRange) {
24230 // no firefox support
24231 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24232 win.document.selection.createRange().pasteHTML(txt);
24234 // no firefox support
24235 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24236 this.execCmd('InsertHTML', txt);
24245 mozKeyPress : function(e){
24247 var c = e.getCharCode(), cmd;
24250 c = String.fromCharCode(c).toLowerCase();
24264 this.cleanUpPaste.defer(100, this);
24272 e.preventDefault();
24280 fixKeys : function(){ // load time branching for fastest keydown performance
24282 return function(e){
24283 var k = e.getKey(), r;
24286 r = this.doc.selection.createRange();
24289 r.pasteHTML('    ');
24296 r = this.doc.selection.createRange();
24298 var target = r.parentElement();
24299 if(!target || target.tagName.toLowerCase() != 'li'){
24301 r.pasteHTML('<br />');
24307 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24308 this.cleanUpPaste.defer(100, this);
24314 }else if(Roo.isOpera){
24315 return function(e){
24316 var k = e.getKey();
24320 this.execCmd('InsertHTML','    ');
24323 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24324 this.cleanUpPaste.defer(100, this);
24329 }else if(Roo.isSafari){
24330 return function(e){
24331 var k = e.getKey();
24335 this.execCmd('InsertText','\t');
24339 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24340 this.cleanUpPaste.defer(100, this);
24348 getAllAncestors: function()
24350 var p = this.getSelectedNode();
24353 a.push(p); // push blank onto stack..
24354 p = this.getParentElement();
24358 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24362 a.push(this.doc.body);
24366 lastSelNode : false,
24369 getSelection : function()
24371 this.assignDocWin();
24372 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24375 getSelectedNode: function()
24377 // this may only work on Gecko!!!
24379 // should we cache this!!!!
24384 var range = this.createRange(this.getSelection()).cloneRange();
24387 var parent = range.parentElement();
24389 var testRange = range.duplicate();
24390 testRange.moveToElementText(parent);
24391 if (testRange.inRange(range)) {
24394 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24397 parent = parent.parentElement;
24402 // is ancestor a text element.
24403 var ac = range.commonAncestorContainer;
24404 if (ac.nodeType == 3) {
24405 ac = ac.parentNode;
24408 var ar = ac.childNodes;
24411 var other_nodes = [];
24412 var has_other_nodes = false;
24413 for (var i=0;i<ar.length;i++) {
24414 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24417 // fullly contained node.
24419 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24424 // probably selected..
24425 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24426 other_nodes.push(ar[i]);
24430 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24435 has_other_nodes = true;
24437 if (!nodes.length && other_nodes.length) {
24438 nodes= other_nodes;
24440 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24446 createRange: function(sel)
24448 // this has strange effects when using with
24449 // top toolbar - not sure if it's a great idea.
24450 //this.editor.contentWindow.focus();
24451 if (typeof sel != "undefined") {
24453 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24455 return this.doc.createRange();
24458 return this.doc.createRange();
24461 getParentElement: function()
24464 this.assignDocWin();
24465 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24467 var range = this.createRange(sel);
24470 var p = range.commonAncestorContainer;
24471 while (p.nodeType == 3) { // text node
24482 * Range intersection.. the hard stuff...
24486 * [ -- selected range --- ]
24490 * if end is before start or hits it. fail.
24491 * if start is after end or hits it fail.
24493 * if either hits (but other is outside. - then it's not
24499 // @see http://www.thismuchiknow.co.uk/?p=64.
24500 rangeIntersectsNode : function(range, node)
24502 var nodeRange = node.ownerDocument.createRange();
24504 nodeRange.selectNode(node);
24506 nodeRange.selectNodeContents(node);
24509 var rangeStartRange = range.cloneRange();
24510 rangeStartRange.collapse(true);
24512 var rangeEndRange = range.cloneRange();
24513 rangeEndRange.collapse(false);
24515 var nodeStartRange = nodeRange.cloneRange();
24516 nodeStartRange.collapse(true);
24518 var nodeEndRange = nodeRange.cloneRange();
24519 nodeEndRange.collapse(false);
24521 return rangeStartRange.compareBoundaryPoints(
24522 Range.START_TO_START, nodeEndRange) == -1 &&
24523 rangeEndRange.compareBoundaryPoints(
24524 Range.START_TO_START, nodeStartRange) == 1;
24528 rangeCompareNode : function(range, node)
24530 var nodeRange = node.ownerDocument.createRange();
24532 nodeRange.selectNode(node);
24534 nodeRange.selectNodeContents(node);
24538 range.collapse(true);
24540 nodeRange.collapse(true);
24542 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24543 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24545 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24547 var nodeIsBefore = ss == 1;
24548 var nodeIsAfter = ee == -1;
24550 if (nodeIsBefore && nodeIsAfter) {
24553 if (!nodeIsBefore && nodeIsAfter) {
24554 return 1; //right trailed.
24557 if (nodeIsBefore && !nodeIsAfter) {
24558 return 2; // left trailed.
24564 // private? - in a new class?
24565 cleanUpPaste : function()
24567 // cleans up the whole document..
24568 Roo.log('cleanuppaste');
24570 this.cleanUpChildren(this.doc.body);
24571 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24572 if (clean != this.doc.body.innerHTML) {
24573 this.doc.body.innerHTML = clean;
24578 cleanWordChars : function(input) {// change the chars to hex code
24579 var he = Roo.HtmlEditorCore;
24581 var output = input;
24582 Roo.each(he.swapCodes, function(sw) {
24583 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24585 output = output.replace(swapper, sw[1]);
24592 cleanUpChildren : function (n)
24594 if (!n.childNodes.length) {
24597 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24598 this.cleanUpChild(n.childNodes[i]);
24605 cleanUpChild : function (node)
24608 //console.log(node);
24609 if (node.nodeName == "#text") {
24610 // clean up silly Windows -- stuff?
24613 if (node.nodeName == "#comment") {
24614 node.parentNode.removeChild(node);
24615 // clean up silly Windows -- stuff?
24618 var lcname = node.tagName.toLowerCase();
24619 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24620 // whitelist of tags..
24622 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24624 node.parentNode.removeChild(node);
24629 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24631 // spans with no attributes - just remove them..
24632 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24633 remove_keep_children = true;
24636 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24637 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24639 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24640 // remove_keep_children = true;
24643 if (remove_keep_children) {
24644 this.cleanUpChildren(node);
24645 // inserts everything just before this node...
24646 while (node.childNodes.length) {
24647 var cn = node.childNodes[0];
24648 node.removeChild(cn);
24649 node.parentNode.insertBefore(cn, node);
24651 node.parentNode.removeChild(node);
24655 if (!node.attributes || !node.attributes.length) {
24660 this.cleanUpChildren(node);
24664 function cleanAttr(n,v)
24667 if (v.match(/^\./) || v.match(/^\//)) {
24670 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24673 if (v.match(/^#/)) {
24676 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24677 node.removeAttribute(n);
24681 var cwhite = this.cwhite;
24682 var cblack = this.cblack;
24684 function cleanStyle(n,v)
24686 if (v.match(/expression/)) { //XSS?? should we even bother..
24687 node.removeAttribute(n);
24691 var parts = v.split(/;/);
24694 Roo.each(parts, function(p) {
24695 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24699 var l = p.split(':').shift().replace(/\s+/g,'');
24700 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24702 if ( cwhite.length && cblack.indexOf(l) > -1) {
24703 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24704 //node.removeAttribute(n);
24708 // only allow 'c whitelisted system attributes'
24709 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24710 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24711 //node.removeAttribute(n);
24721 if (clean.length) {
24722 node.setAttribute(n, clean.join(';'));
24724 node.removeAttribute(n);
24730 for (var i = node.attributes.length-1; i > -1 ; i--) {
24731 var a = node.attributes[i];
24734 if (a.name.toLowerCase().substr(0,2)=='on') {
24735 node.removeAttribute(a.name);
24738 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24739 node.removeAttribute(a.name);
24742 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24743 cleanAttr(a.name,a.value); // fixme..
24746 if (a.name == 'style') {
24747 cleanStyle(a.name,a.value);
24750 /// clean up MS crap..
24751 // tecnically this should be a list of valid class'es..
24754 if (a.name == 'class') {
24755 if (a.value.match(/^Mso/)) {
24756 node.removeAttribute('class');
24759 if (a.value.match(/^body$/)) {
24760 node.removeAttribute('class');
24771 this.cleanUpChildren(node);
24777 * Clean up MS wordisms...
24779 cleanWord : function(node)
24782 this.cleanWord(this.doc.body);
24787 node.nodeName == 'SPAN' &&
24788 !node.hasAttributes() &&
24789 node.childNodes.length == 1 &&
24790 node.firstChild.nodeName == "#text"
24792 var textNode = node.firstChild;
24793 node.removeChild(textNode);
24794 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24795 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24797 node.parentNode.insertBefore(textNode, node);
24798 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24799 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24801 node.parentNode.removeChild(node);
24804 if (node.nodeName == "#text") {
24805 // clean up silly Windows -- stuff?
24808 if (node.nodeName == "#comment") {
24809 node.parentNode.removeChild(node);
24810 // clean up silly Windows -- stuff?
24814 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24815 node.parentNode.removeChild(node);
24818 //Roo.log(node.tagName);
24819 // remove - but keep children..
24820 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24821 //Roo.log('-- removed');
24822 while (node.childNodes.length) {
24823 var cn = node.childNodes[0];
24824 node.removeChild(cn);
24825 node.parentNode.insertBefore(cn, node);
24826 // move node to parent - and clean it..
24827 this.cleanWord(cn);
24829 node.parentNode.removeChild(node);
24830 /// no need to iterate chidlren = it's got none..
24831 //this.iterateChildren(node, this.cleanWord);
24835 if (node.className.length) {
24837 var cn = node.className.split(/\W+/);
24839 Roo.each(cn, function(cls) {
24840 if (cls.match(/Mso[a-zA-Z]+/)) {
24845 node.className = cna.length ? cna.join(' ') : '';
24847 node.removeAttribute("class");
24851 if (node.hasAttribute("lang")) {
24852 node.removeAttribute("lang");
24855 if (node.hasAttribute("style")) {
24857 var styles = node.getAttribute("style").split(";");
24859 Roo.each(styles, function(s) {
24860 if (!s.match(/:/)) {
24863 var kv = s.split(":");
24864 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24867 // what ever is left... we allow.
24870 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24871 if (!nstyle.length) {
24872 node.removeAttribute('style');
24875 this.iterateChildren(node, this.cleanWord);
24881 * iterateChildren of a Node, calling fn each time, using this as the scole..
24882 * @param {DomNode} node node to iterate children of.
24883 * @param {Function} fn method of this class to call on each item.
24885 iterateChildren : function(node, fn)
24887 if (!node.childNodes.length) {
24890 for (var i = node.childNodes.length-1; i > -1 ; i--) {
24891 fn.call(this, node.childNodes[i])
24897 * cleanTableWidths.
24899 * Quite often pasting from word etc.. results in tables with column and widths.
24900 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24903 cleanTableWidths : function(node)
24908 this.cleanTableWidths(this.doc.body);
24913 if (node.nodeName == "#text" || node.nodeName == "#comment") {
24916 Roo.log(node.tagName);
24917 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24918 this.iterateChildren(node, this.cleanTableWidths);
24921 if (node.hasAttribute('width')) {
24922 node.removeAttribute('width');
24926 if (node.hasAttribute("style")) {
24929 var styles = node.getAttribute("style").split(";");
24931 Roo.each(styles, function(s) {
24932 if (!s.match(/:/)) {
24935 var kv = s.split(":");
24936 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24939 // what ever is left... we allow.
24942 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24943 if (!nstyle.length) {
24944 node.removeAttribute('style');
24948 this.iterateChildren(node, this.cleanTableWidths);
24956 domToHTML : function(currentElement, depth, nopadtext) {
24958 depth = depth || 0;
24959 nopadtext = nopadtext || false;
24961 if (!currentElement) {
24962 return this.domToHTML(this.doc.body);
24965 //Roo.log(currentElement);
24967 var allText = false;
24968 var nodeName = currentElement.nodeName;
24969 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24971 if (nodeName == '#text') {
24973 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24978 if (nodeName != 'BODY') {
24981 // Prints the node tagName, such as <A>, <IMG>, etc
24984 for(i = 0; i < currentElement.attributes.length;i++) {
24986 var aname = currentElement.attributes.item(i).name;
24987 if (!currentElement.attributes.item(i).value.length) {
24990 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24993 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25002 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25005 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25010 // Traverse the tree
25012 var currentElementChild = currentElement.childNodes.item(i);
25013 var allText = true;
25014 var innerHTML = '';
25016 while (currentElementChild) {
25017 // Formatting code (indent the tree so it looks nice on the screen)
25018 var nopad = nopadtext;
25019 if (lastnode == 'SPAN') {
25023 if (currentElementChild.nodeName == '#text') {
25024 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25025 toadd = nopadtext ? toadd : toadd.trim();
25026 if (!nopad && toadd.length > 80) {
25027 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25029 innerHTML += toadd;
25032 currentElementChild = currentElement.childNodes.item(i);
25038 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25040 // Recursively traverse the tree structure of the child node
25041 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25042 lastnode = currentElementChild.nodeName;
25044 currentElementChild=currentElement.childNodes.item(i);
25050 // The remaining code is mostly for formatting the tree
25051 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25056 ret+= "</"+tagName+">";
25062 applyBlacklists : function()
25064 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25065 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25069 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25070 if (b.indexOf(tag) > -1) {
25073 this.white.push(tag);
25077 Roo.each(w, function(tag) {
25078 if (b.indexOf(tag) > -1) {
25081 if (this.white.indexOf(tag) > -1) {
25084 this.white.push(tag);
25089 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25090 if (w.indexOf(tag) > -1) {
25093 this.black.push(tag);
25097 Roo.each(b, function(tag) {
25098 if (w.indexOf(tag) > -1) {
25101 if (this.black.indexOf(tag) > -1) {
25104 this.black.push(tag);
25109 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25110 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25114 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25115 if (b.indexOf(tag) > -1) {
25118 this.cwhite.push(tag);
25122 Roo.each(w, function(tag) {
25123 if (b.indexOf(tag) > -1) {
25126 if (this.cwhite.indexOf(tag) > -1) {
25129 this.cwhite.push(tag);
25134 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25135 if (w.indexOf(tag) > -1) {
25138 this.cblack.push(tag);
25142 Roo.each(b, function(tag) {
25143 if (w.indexOf(tag) > -1) {
25146 if (this.cblack.indexOf(tag) > -1) {
25149 this.cblack.push(tag);
25154 setStylesheets : function(stylesheets)
25156 if(typeof(stylesheets) == 'string'){
25157 Roo.get(this.iframe.contentDocument.head).createChild({
25159 rel : 'stylesheet',
25168 Roo.each(stylesheets, function(s) {
25173 Roo.get(_this.iframe.contentDocument.head).createChild({
25175 rel : 'stylesheet',
25184 removeStylesheets : function()
25188 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25193 setStyle : function(style)
25195 Roo.get(this.iframe.contentDocument.head).createChild({
25204 // hide stuff that is not compatible
25218 * @event specialkey
25222 * @cfg {String} fieldClass @hide
25225 * @cfg {String} focusClass @hide
25228 * @cfg {String} autoCreate @hide
25231 * @cfg {String} inputType @hide
25234 * @cfg {String} invalidClass @hide
25237 * @cfg {String} invalidText @hide
25240 * @cfg {String} msgFx @hide
25243 * @cfg {String} validateOnBlur @hide
25247 Roo.HtmlEditorCore.white = [
25248 'area', 'br', 'img', 'input', 'hr', 'wbr',
25250 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25251 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25252 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25253 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25254 'table', 'ul', 'xmp',
25256 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25259 'dir', 'menu', 'ol', 'ul', 'dl',
25265 Roo.HtmlEditorCore.black = [
25266 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25268 'base', 'basefont', 'bgsound', 'blink', 'body',
25269 'frame', 'frameset', 'head', 'html', 'ilayer',
25270 'iframe', 'layer', 'link', 'meta', 'object',
25271 'script', 'style' ,'title', 'xml' // clean later..
25273 Roo.HtmlEditorCore.clean = [
25274 'script', 'style', 'title', 'xml'
25276 Roo.HtmlEditorCore.remove = [
25281 Roo.HtmlEditorCore.ablack = [
25285 Roo.HtmlEditorCore.aclean = [
25286 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25290 Roo.HtmlEditorCore.pwhite= [
25291 'http', 'https', 'mailto'
25294 // white listed style attributes.
25295 Roo.HtmlEditorCore.cwhite= [
25296 // 'text-align', /// default is to allow most things..
25302 // black listed style attributes.
25303 Roo.HtmlEditorCore.cblack= [
25304 // 'font-size' -- this can be set by the project
25308 Roo.HtmlEditorCore.swapCodes =[
25327 * @class Roo.bootstrap.HtmlEditor
25328 * @extends Roo.bootstrap.TextArea
25329 * Bootstrap HtmlEditor class
25332 * Create a new HtmlEditor
25333 * @param {Object} config The config object
25336 Roo.bootstrap.HtmlEditor = function(config){
25337 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25338 if (!this.toolbars) {
25339 this.toolbars = [];
25342 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25345 * @event initialize
25346 * Fires when the editor is fully initialized (including the iframe)
25347 * @param {HtmlEditor} this
25352 * Fires when the editor is first receives the focus. Any insertion must wait
25353 * until after this event.
25354 * @param {HtmlEditor} this
25358 * @event beforesync
25359 * Fires before the textarea is updated with content from the editor iframe. Return false
25360 * to cancel the sync.
25361 * @param {HtmlEditor} this
25362 * @param {String} html
25366 * @event beforepush
25367 * Fires before the iframe editor is updated with content from the textarea. Return false
25368 * to cancel the push.
25369 * @param {HtmlEditor} this
25370 * @param {String} html
25375 * Fires when the textarea is updated with content from the editor iframe.
25376 * @param {HtmlEditor} this
25377 * @param {String} html
25382 * Fires when the iframe editor is updated with content from the textarea.
25383 * @param {HtmlEditor} this
25384 * @param {String} html
25388 * @event editmodechange
25389 * Fires when the editor switches edit modes
25390 * @param {HtmlEditor} this
25391 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25393 editmodechange: true,
25395 * @event editorevent
25396 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25397 * @param {HtmlEditor} this
25401 * @event firstfocus
25402 * Fires when on first focus - needed by toolbars..
25403 * @param {HtmlEditor} this
25408 * Auto save the htmlEditor value as a file into Events
25409 * @param {HtmlEditor} this
25413 * @event savedpreview
25414 * preview the saved version of htmlEditor
25415 * @param {HtmlEditor} this
25422 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25426 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25431 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25436 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25441 * @cfg {Number} height (in pixels)
25445 * @cfg {Number} width (in pixels)
25450 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25453 stylesheets: false,
25458 // private properties
25459 validationEvent : false,
25461 initialized : false,
25464 onFocus : Roo.emptyFn,
25466 hideMode:'offsets',
25468 tbContainer : false,
25472 toolbarContainer :function() {
25473 return this.wrap.select('.x-html-editor-tb',true).first();
25477 * Protected method that will not generally be called directly. It
25478 * is called when the editor creates its toolbar. Override this method if you need to
25479 * add custom toolbar buttons.
25480 * @param {HtmlEditor} editor
25482 createToolbar : function(){
25483 Roo.log('renewing');
25484 Roo.log("create toolbars");
25486 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25487 this.toolbars[0].render(this.toolbarContainer());
25491 // if (!editor.toolbars || !editor.toolbars.length) {
25492 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25495 // for (var i =0 ; i < editor.toolbars.length;i++) {
25496 // editor.toolbars[i] = Roo.factory(
25497 // typeof(editor.toolbars[i]) == 'string' ?
25498 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25499 // Roo.bootstrap.HtmlEditor);
25500 // editor.toolbars[i].init(editor);
25506 onRender : function(ct, position)
25508 // Roo.log("Call onRender: " + this.xtype);
25510 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25512 this.wrap = this.inputEl().wrap({
25513 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25516 this.editorcore.onRender(ct, position);
25518 if (this.resizable) {
25519 this.resizeEl = new Roo.Resizable(this.wrap, {
25523 minHeight : this.height,
25524 height: this.height,
25525 handles : this.resizable,
25528 resize : function(r, w, h) {
25529 _t.onResize(w,h); // -something
25535 this.createToolbar(this);
25538 if(!this.width && this.resizable){
25539 this.setSize(this.wrap.getSize());
25541 if (this.resizeEl) {
25542 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25543 // should trigger onReize..
25549 onResize : function(w, h)
25551 Roo.log('resize: ' +w + ',' + h );
25552 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25556 if(this.inputEl() ){
25557 if(typeof w == 'number'){
25558 var aw = w - this.wrap.getFrameWidth('lr');
25559 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25562 if(typeof h == 'number'){
25563 var tbh = -11; // fixme it needs to tool bar size!
25564 for (var i =0; i < this.toolbars.length;i++) {
25565 // fixme - ask toolbars for heights?
25566 tbh += this.toolbars[i].el.getHeight();
25567 //if (this.toolbars[i].footer) {
25568 // tbh += this.toolbars[i].footer.el.getHeight();
25576 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25577 ah -= 5; // knock a few pixes off for look..
25578 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25582 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25583 this.editorcore.onResize(ew,eh);
25588 * Toggles the editor between standard and source edit mode.
25589 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25591 toggleSourceEdit : function(sourceEditMode)
25593 this.editorcore.toggleSourceEdit(sourceEditMode);
25595 if(this.editorcore.sourceEditMode){
25596 Roo.log('editor - showing textarea');
25599 // Roo.log(this.syncValue());
25601 this.inputEl().removeClass(['hide', 'x-hidden']);
25602 this.inputEl().dom.removeAttribute('tabIndex');
25603 this.inputEl().focus();
25605 Roo.log('editor - hiding textarea');
25607 // Roo.log(this.pushValue());
25610 this.inputEl().addClass(['hide', 'x-hidden']);
25611 this.inputEl().dom.setAttribute('tabIndex', -1);
25612 //this.deferFocus();
25615 if(this.resizable){
25616 this.setSize(this.wrap.getSize());
25619 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25622 // private (for BoxComponent)
25623 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25625 // private (for BoxComponent)
25626 getResizeEl : function(){
25630 // private (for BoxComponent)
25631 getPositionEl : function(){
25636 initEvents : function(){
25637 this.originalValue = this.getValue();
25641 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25644 // markInvalid : Roo.emptyFn,
25646 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25649 // clearInvalid : Roo.emptyFn,
25651 setValue : function(v){
25652 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25653 this.editorcore.pushValue();
25658 deferFocus : function(){
25659 this.focus.defer(10, this);
25663 focus : function(){
25664 this.editorcore.focus();
25670 onDestroy : function(){
25676 for (var i =0; i < this.toolbars.length;i++) {
25677 // fixme - ask toolbars for heights?
25678 this.toolbars[i].onDestroy();
25681 this.wrap.dom.innerHTML = '';
25682 this.wrap.remove();
25687 onFirstFocus : function(){
25688 //Roo.log("onFirstFocus");
25689 this.editorcore.onFirstFocus();
25690 for (var i =0; i < this.toolbars.length;i++) {
25691 this.toolbars[i].onFirstFocus();
25697 syncValue : function()
25699 this.editorcore.syncValue();
25702 pushValue : function()
25704 this.editorcore.pushValue();
25708 // hide stuff that is not compatible
25722 * @event specialkey
25726 * @cfg {String} fieldClass @hide
25729 * @cfg {String} focusClass @hide
25732 * @cfg {String} autoCreate @hide
25735 * @cfg {String} inputType @hide
25739 * @cfg {String} invalidText @hide
25742 * @cfg {String} msgFx @hide
25745 * @cfg {String} validateOnBlur @hide
25754 Roo.namespace('Roo.bootstrap.htmleditor');
25756 * @class Roo.bootstrap.HtmlEditorToolbar1
25762 new Roo.bootstrap.HtmlEditor({
25765 new Roo.bootstrap.HtmlEditorToolbar1({
25766 disable : { fonts: 1 , format: 1, ..., ... , ...],
25772 * @cfg {Object} disable List of elements to disable..
25773 * @cfg {Array} btns List of additional buttons.
25777 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25780 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25783 Roo.apply(this, config);
25785 // default disabled, based on 'good practice'..
25786 this.disable = this.disable || {};
25787 Roo.applyIf(this.disable, {
25790 specialElements : true
25792 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25794 this.editor = config.editor;
25795 this.editorcore = config.editor.editorcore;
25797 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25799 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25800 // dont call parent... till later.
25802 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
25807 editorcore : false,
25812 "h1","h2","h3","h4","h5","h6",
25814 "abbr", "acronym", "address", "cite", "samp", "var",
25818 onRender : function(ct, position)
25820 // Roo.log("Call onRender: " + this.xtype);
25822 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25824 this.el.dom.style.marginBottom = '0';
25826 var editorcore = this.editorcore;
25827 var editor= this.editor;
25830 var btn = function(id,cmd , toggle, handler, html){
25832 var event = toggle ? 'toggle' : 'click';
25837 xns: Roo.bootstrap,
25841 enableToggle:toggle !== false,
25843 pressed : toggle ? false : null,
25846 a.listeners[toggle ? 'toggle' : 'click'] = function() {
25847 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
25853 // var cb_box = function...
25858 xns: Roo.bootstrap,
25863 xns: Roo.bootstrap,
25867 Roo.each(this.formats, function(f) {
25868 style.menu.items.push({
25870 xns: Roo.bootstrap,
25871 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25876 editorcore.insertTag(this.tagname);
25883 children.push(style);
25885 btn('bold',false,true);
25886 btn('italic',false,true);
25887 btn('align-left', 'justifyleft',true);
25888 btn('align-center', 'justifycenter',true);
25889 btn('align-right' , 'justifyright',true);
25890 btn('link', false, false, function(btn) {
25891 //Roo.log("create link?");
25892 var url = prompt(this.createLinkText, this.defaultLinkValue);
25893 if(url && url != 'http:/'+'/'){
25894 this.editorcore.relayCmd('createlink', url);
25897 btn('list','insertunorderedlist',true);
25898 btn('pencil', false,true, function(btn){
25900 this.toggleSourceEdit(btn.pressed);
25903 if (this.editor.btns.length > 0) {
25904 for (var i = 0; i<this.editor.btns.length; i++) {
25905 children.push(this.editor.btns[i]);
25913 xns: Roo.bootstrap,
25918 xns: Roo.bootstrap,
25923 cog.menu.items.push({
25925 xns: Roo.bootstrap,
25926 html : Clean styles,
25931 editorcore.insertTag(this.tagname);
25940 this.xtype = 'NavSimplebar';
25942 for(var i=0;i< children.length;i++) {
25944 this.buttons.add(this.addxtypeChild(children[i]));
25948 editor.on('editorevent', this.updateToolbar, this);
25950 onBtnClick : function(id)
25952 this.editorcore.relayCmd(id);
25953 this.editorcore.focus();
25957 * Protected method that will not generally be called directly. It triggers
25958 * a toolbar update by reading the markup state of the current selection in the editor.
25960 updateToolbar: function(){
25962 if(!this.editorcore.activated){
25963 this.editor.onFirstFocus(); // is this neeed?
25967 var btns = this.buttons;
25968 var doc = this.editorcore.doc;
25969 btns.get('bold').setActive(doc.queryCommandState('bold'));
25970 btns.get('italic').setActive(doc.queryCommandState('italic'));
25971 //btns.get('underline').setActive(doc.queryCommandState('underline'));
25973 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25974 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25975 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25977 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25978 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25981 var ans = this.editorcore.getAllAncestors();
25982 if (this.formatCombo) {
25985 var store = this.formatCombo.store;
25986 this.formatCombo.setValue("");
25987 for (var i =0; i < ans.length;i++) {
25988 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25990 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25998 // hides menus... - so this cant be on a menu...
25999 Roo.bootstrap.MenuMgr.hideAll();
26001 Roo.bootstrap.MenuMgr.hideAll();
26002 //this.editorsyncValue();
26004 onFirstFocus: function() {
26005 this.buttons.each(function(item){
26009 toggleSourceEdit : function(sourceEditMode){
26012 if(sourceEditMode){
26013 Roo.log("disabling buttons");
26014 this.buttons.each( function(item){
26015 if(item.cmd != 'pencil'){
26021 Roo.log("enabling buttons");
26022 if(this.editorcore.initialized){
26023 this.buttons.each( function(item){
26029 Roo.log("calling toggole on editor");
26030 // tell the editor that it's been pressed..
26031 this.editor.toggleSourceEdit(sourceEditMode);
26041 * @class Roo.bootstrap.Table.AbstractSelectionModel
26042 * @extends Roo.util.Observable
26043 * Abstract base class for grid SelectionModels. It provides the interface that should be
26044 * implemented by descendant classes. This class should not be directly instantiated.
26047 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26048 this.locked = false;
26049 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26053 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26054 /** @ignore Called by the grid automatically. Do not call directly. */
26055 init : function(grid){
26061 * Locks the selections.
26064 this.locked = true;
26068 * Unlocks the selections.
26070 unlock : function(){
26071 this.locked = false;
26075 * Returns true if the selections are locked.
26076 * @return {Boolean}
26078 isLocked : function(){
26079 return this.locked;
26083 initEvents : function ()
26089 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26090 * @class Roo.bootstrap.Table.RowSelectionModel
26091 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26092 * It supports multiple selections and keyboard selection/navigation.
26094 * @param {Object} config
26097 Roo.bootstrap.Table.RowSelectionModel = function(config){
26098 Roo.apply(this, config);
26099 this.selections = new Roo.util.MixedCollection(false, function(o){
26104 this.lastActive = false;
26108 * @event selectionchange
26109 * Fires when the selection changes
26110 * @param {SelectionModel} this
26112 "selectionchange" : true,
26114 * @event afterselectionchange
26115 * Fires after the selection changes (eg. by key press or clicking)
26116 * @param {SelectionModel} this
26118 "afterselectionchange" : true,
26120 * @event beforerowselect
26121 * Fires when a row is selected being selected, return false to cancel.
26122 * @param {SelectionModel} this
26123 * @param {Number} rowIndex The selected index
26124 * @param {Boolean} keepExisting False if other selections will be cleared
26126 "beforerowselect" : true,
26129 * Fires when a row is selected.
26130 * @param {SelectionModel} this
26131 * @param {Number} rowIndex The selected index
26132 * @param {Roo.data.Record} r The record
26134 "rowselect" : true,
26136 * @event rowdeselect
26137 * Fires when a row is deselected.
26138 * @param {SelectionModel} this
26139 * @param {Number} rowIndex The selected index
26141 "rowdeselect" : true
26143 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26144 this.locked = false;
26147 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26149 * @cfg {Boolean} singleSelect
26150 * True to allow selection of only one row at a time (defaults to false)
26152 singleSelect : false,
26155 initEvents : function()
26158 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26159 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26160 //}else{ // allow click to work like normal
26161 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26163 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26164 this.grid.on("rowclick", this.handleMouseDown, this);
26166 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26167 "up" : function(e){
26169 this.selectPrevious(e.shiftKey);
26170 }else if(this.last !== false && this.lastActive !== false){
26171 var last = this.last;
26172 this.selectRange(this.last, this.lastActive-1);
26173 this.grid.getView().focusRow(this.lastActive);
26174 if(last !== false){
26178 this.selectFirstRow();
26180 this.fireEvent("afterselectionchange", this);
26182 "down" : function(e){
26184 this.selectNext(e.shiftKey);
26185 }else if(this.last !== false && this.lastActive !== false){
26186 var last = this.last;
26187 this.selectRange(this.last, this.lastActive+1);
26188 this.grid.getView().focusRow(this.lastActive);
26189 if(last !== false){
26193 this.selectFirstRow();
26195 this.fireEvent("afterselectionchange", this);
26199 this.grid.store.on('load', function(){
26200 this.selections.clear();
26203 var view = this.grid.view;
26204 view.on("refresh", this.onRefresh, this);
26205 view.on("rowupdated", this.onRowUpdated, this);
26206 view.on("rowremoved", this.onRemove, this);
26211 onRefresh : function()
26213 var ds = this.grid.store, i, v = this.grid.view;
26214 var s = this.selections;
26215 s.each(function(r){
26216 if((i = ds.indexOfId(r.id)) != -1){
26225 onRemove : function(v, index, r){
26226 this.selections.remove(r);
26230 onRowUpdated : function(v, index, r){
26231 if(this.isSelected(r)){
26232 v.onRowSelect(index);
26238 * @param {Array} records The records to select
26239 * @param {Boolean} keepExisting (optional) True to keep existing selections
26241 selectRecords : function(records, keepExisting)
26244 this.clearSelections();
26246 var ds = this.grid.store;
26247 for(var i = 0, len = records.length; i < len; i++){
26248 this.selectRow(ds.indexOf(records[i]), true);
26253 * Gets the number of selected rows.
26256 getCount : function(){
26257 return this.selections.length;
26261 * Selects the first row in the grid.
26263 selectFirstRow : function(){
26268 * Select the last row.
26269 * @param {Boolean} keepExisting (optional) True to keep existing selections
26271 selectLastRow : function(keepExisting){
26272 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26273 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26277 * Selects the row immediately following the last selected row.
26278 * @param {Boolean} keepExisting (optional) True to keep existing selections
26280 selectNext : function(keepExisting)
26282 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26283 this.selectRow(this.last+1, keepExisting);
26284 this.grid.getView().focusRow(this.last);
26289 * Selects the row that precedes the last selected row.
26290 * @param {Boolean} keepExisting (optional) True to keep existing selections
26292 selectPrevious : function(keepExisting){
26294 this.selectRow(this.last-1, keepExisting);
26295 this.grid.getView().focusRow(this.last);
26300 * Returns the selected records
26301 * @return {Array} Array of selected records
26303 getSelections : function(){
26304 return [].concat(this.selections.items);
26308 * Returns the first selected record.
26311 getSelected : function(){
26312 return this.selections.itemAt(0);
26317 * Clears all selections.
26319 clearSelections : function(fast)
26325 var ds = this.grid.store;
26326 var s = this.selections;
26327 s.each(function(r){
26328 this.deselectRow(ds.indexOfId(r.id));
26332 this.selections.clear();
26339 * Selects all rows.
26341 selectAll : function(){
26345 this.selections.clear();
26346 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26347 this.selectRow(i, true);
26352 * Returns True if there is a selection.
26353 * @return {Boolean}
26355 hasSelection : function(){
26356 return this.selections.length > 0;
26360 * Returns True if the specified row is selected.
26361 * @param {Number/Record} record The record or index of the record to check
26362 * @return {Boolean}
26364 isSelected : function(index){
26365 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26366 return (r && this.selections.key(r.id) ? true : false);
26370 * Returns True if the specified record id is selected.
26371 * @param {String} id The id of record to check
26372 * @return {Boolean}
26374 isIdSelected : function(id){
26375 return (this.selections.key(id) ? true : false);
26380 handleMouseDBClick : function(e, t){
26384 handleMouseDown : function(e, t)
26386 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26387 if(this.isLocked() || rowIndex < 0 ){
26390 if(e.shiftKey && this.last !== false){
26391 var last = this.last;
26392 this.selectRange(last, rowIndex, e.ctrlKey);
26393 this.last = last; // reset the last
26397 var isSelected = this.isSelected(rowIndex);
26398 //Roo.log("select row:" + rowIndex);
26400 this.deselectRow(rowIndex);
26402 this.selectRow(rowIndex, true);
26406 if(e.button !== 0 && isSelected){
26407 alert('rowIndex 2: ' + rowIndex);
26408 view.focusRow(rowIndex);
26409 }else if(e.ctrlKey && isSelected){
26410 this.deselectRow(rowIndex);
26411 }else if(!isSelected){
26412 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26413 view.focusRow(rowIndex);
26417 this.fireEvent("afterselectionchange", this);
26420 handleDragableRowClick : function(grid, rowIndex, e)
26422 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26423 this.selectRow(rowIndex, false);
26424 grid.view.focusRow(rowIndex);
26425 this.fireEvent("afterselectionchange", this);
26430 * Selects multiple rows.
26431 * @param {Array} rows Array of the indexes of the row to select
26432 * @param {Boolean} keepExisting (optional) True to keep existing selections
26434 selectRows : function(rows, keepExisting){
26436 this.clearSelections();
26438 for(var i = 0, len = rows.length; i < len; i++){
26439 this.selectRow(rows[i], true);
26444 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26445 * @param {Number} startRow The index of the first row in the range
26446 * @param {Number} endRow The index of the last row in the range
26447 * @param {Boolean} keepExisting (optional) True to retain existing selections
26449 selectRange : function(startRow, endRow, keepExisting){
26454 this.clearSelections();
26456 if(startRow <= endRow){
26457 for(var i = startRow; i <= endRow; i++){
26458 this.selectRow(i, true);
26461 for(var i = startRow; i >= endRow; i--){
26462 this.selectRow(i, true);
26468 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26469 * @param {Number} startRow The index of the first row in the range
26470 * @param {Number} endRow The index of the last row in the range
26472 deselectRange : function(startRow, endRow, preventViewNotify){
26476 for(var i = startRow; i <= endRow; i++){
26477 this.deselectRow(i, preventViewNotify);
26483 * @param {Number} row The index of the row to select
26484 * @param {Boolean} keepExisting (optional) True to keep existing selections
26486 selectRow : function(index, keepExisting, preventViewNotify)
26488 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26491 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26492 if(!keepExisting || this.singleSelect){
26493 this.clearSelections();
26496 var r = this.grid.store.getAt(index);
26497 //console.log('selectRow - record id :' + r.id);
26499 this.selections.add(r);
26500 this.last = this.lastActive = index;
26501 if(!preventViewNotify){
26502 var proxy = new Roo.Element(
26503 this.grid.getRowDom(index)
26505 proxy.addClass('bg-info info');
26507 this.fireEvent("rowselect", this, index, r);
26508 this.fireEvent("selectionchange", this);
26514 * @param {Number} row The index of the row to deselect
26516 deselectRow : function(index, preventViewNotify)
26521 if(this.last == index){
26524 if(this.lastActive == index){
26525 this.lastActive = false;
26528 var r = this.grid.store.getAt(index);
26533 this.selections.remove(r);
26534 //.console.log('deselectRow - record id :' + r.id);
26535 if(!preventViewNotify){
26537 var proxy = new Roo.Element(
26538 this.grid.getRowDom(index)
26540 proxy.removeClass('bg-info info');
26542 this.fireEvent("rowdeselect", this, index);
26543 this.fireEvent("selectionchange", this);
26547 restoreLast : function(){
26549 this.last = this._last;
26554 acceptsNav : function(row, col, cm){
26555 return !cm.isHidden(col) && cm.isCellEditable(col, row);
26559 onEditorKey : function(field, e){
26560 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26565 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26567 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26569 }else if(k == e.ENTER && !e.ctrlKey){
26573 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26575 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26577 }else if(k == e.ESC){
26581 g.startEditing(newCell[0], newCell[1]);
26587 * Ext JS Library 1.1.1
26588 * Copyright(c) 2006-2007, Ext JS, LLC.
26590 * Originally Released Under LGPL - original licence link has changed is not relivant.
26593 * <script type="text/javascript">
26597 * @class Roo.bootstrap.PagingToolbar
26598 * @extends Roo.bootstrap.NavSimplebar
26599 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26601 * Create a new PagingToolbar
26602 * @param {Object} config The config object
26603 * @param {Roo.data.Store} store
26605 Roo.bootstrap.PagingToolbar = function(config)
26607 // old args format still supported... - xtype is prefered..
26608 // created from xtype...
26610 this.ds = config.dataSource;
26612 if (config.store && !this.ds) {
26613 this.store= Roo.factory(config.store, Roo.data);
26614 this.ds = this.store;
26615 this.ds.xmodule = this.xmodule || false;
26618 this.toolbarItems = [];
26619 if (config.items) {
26620 this.toolbarItems = config.items;
26623 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26628 this.bind(this.ds);
26631 if (Roo.bootstrap.version == 4) {
26632 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26634 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26639 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26641 * @cfg {Roo.data.Store} dataSource
26642 * The underlying data store providing the paged data
26645 * @cfg {String/HTMLElement/Element} container
26646 * container The id or element that will contain the toolbar
26649 * @cfg {Boolean} displayInfo
26650 * True to display the displayMsg (defaults to false)
26653 * @cfg {Number} pageSize
26654 * The number of records to display per page (defaults to 20)
26658 * @cfg {String} displayMsg
26659 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26661 displayMsg : 'Displaying {0} - {1} of {2}',
26663 * @cfg {String} emptyMsg
26664 * The message to display when no records are found (defaults to "No data to display")
26666 emptyMsg : 'No data to display',
26668 * Customizable piece of the default paging text (defaults to "Page")
26671 beforePageText : "Page",
26673 * Customizable piece of the default paging text (defaults to "of %0")
26676 afterPageText : "of {0}",
26678 * Customizable piece of the default paging text (defaults to "First Page")
26681 firstText : "First Page",
26683 * Customizable piece of the default paging text (defaults to "Previous Page")
26686 prevText : "Previous Page",
26688 * Customizable piece of the default paging text (defaults to "Next Page")
26691 nextText : "Next Page",
26693 * Customizable piece of the default paging text (defaults to "Last Page")
26696 lastText : "Last Page",
26698 * Customizable piece of the default paging text (defaults to "Refresh")
26701 refreshText : "Refresh",
26705 onRender : function(ct, position)
26707 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26708 this.navgroup.parentId = this.id;
26709 this.navgroup.onRender(this.el, null);
26710 // add the buttons to the navgroup
26712 if(this.displayInfo){
26713 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26714 this.displayEl = this.el.select('.x-paging-info', true).first();
26715 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26716 // this.displayEl = navel.el.select('span',true).first();
26722 Roo.each(_this.buttons, function(e){ // this might need to use render????
26723 Roo.factory(e).render(_this.el);
26727 Roo.each(_this.toolbarItems, function(e) {
26728 _this.navgroup.addItem(e);
26732 this.first = this.navgroup.addItem({
26733 tooltip: this.firstText,
26734 cls: "prev btn-outline-secondary",
26735 html : ' <i class="fa fa-step-backward"></i>',
26737 preventDefault: true,
26738 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26741 this.prev = this.navgroup.addItem({
26742 tooltip: this.prevText,
26743 cls: "prev btn-outline-secondary",
26744 html : ' <i class="fa fa-backward"></i>',
26746 preventDefault: true,
26747 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
26749 //this.addSeparator();
26752 var field = this.navgroup.addItem( {
26754 cls : 'x-paging-position btn-outline-secondary',
26756 html : this.beforePageText +
26757 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26758 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
26761 this.field = field.el.select('input', true).first();
26762 this.field.on("keydown", this.onPagingKeydown, this);
26763 this.field.on("focus", function(){this.dom.select();});
26766 this.afterTextEl = field.el.select('.x-paging-after',true).first();
26767 //this.field.setHeight(18);
26768 //this.addSeparator();
26769 this.next = this.navgroup.addItem({
26770 tooltip: this.nextText,
26771 cls: "next btn-outline-secondary",
26772 html : ' <i class="fa fa-forward"></i>',
26774 preventDefault: true,
26775 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
26777 this.last = this.navgroup.addItem({
26778 tooltip: this.lastText,
26779 html : ' <i class="fa fa-step-forward"></i>',
26780 cls: "next btn-outline-secondary",
26782 preventDefault: true,
26783 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
26785 //this.addSeparator();
26786 this.loading = this.navgroup.addItem({
26787 tooltip: this.refreshText,
26788 cls: "btn-outline-secondary",
26789 html : ' <i class="fa fa-refresh"></i>',
26790 preventDefault: true,
26791 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26797 updateInfo : function(){
26798 if(this.displayEl){
26799 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26800 var msg = count == 0 ?
26804 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
26806 this.displayEl.update(msg);
26811 onLoad : function(ds, r, o)
26813 this.cursor = o.params.start ? o.params.start : 0;
26815 var d = this.getPageData(),
26820 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26821 this.field.dom.value = ap;
26822 this.first.setDisabled(ap == 1);
26823 this.prev.setDisabled(ap == 1);
26824 this.next.setDisabled(ap == ps);
26825 this.last.setDisabled(ap == ps);
26826 this.loading.enable();
26831 getPageData : function(){
26832 var total = this.ds.getTotalCount();
26835 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26836 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26841 onLoadError : function(){
26842 this.loading.enable();
26846 onPagingKeydown : function(e){
26847 var k = e.getKey();
26848 var d = this.getPageData();
26850 var v = this.field.dom.value, pageNum;
26851 if(!v || isNaN(pageNum = parseInt(v, 10))){
26852 this.field.dom.value = d.activePage;
26855 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26856 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26859 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))
26861 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26862 this.field.dom.value = pageNum;
26863 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26866 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26868 var v = this.field.dom.value, pageNum;
26869 var increment = (e.shiftKey) ? 10 : 1;
26870 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26873 if(!v || isNaN(pageNum = parseInt(v, 10))) {
26874 this.field.dom.value = d.activePage;
26877 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26879 this.field.dom.value = parseInt(v, 10) + increment;
26880 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26881 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26888 beforeLoad : function(){
26890 this.loading.disable();
26895 onClick : function(which){
26904 ds.load({params:{start: 0, limit: this.pageSize}});
26907 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26910 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26913 var total = ds.getTotalCount();
26914 var extra = total % this.pageSize;
26915 var lastStart = extra ? (total - extra) : total-this.pageSize;
26916 ds.load({params:{start: lastStart, limit: this.pageSize}});
26919 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26925 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26926 * @param {Roo.data.Store} store The data store to unbind
26928 unbind : function(ds){
26929 ds.un("beforeload", this.beforeLoad, this);
26930 ds.un("load", this.onLoad, this);
26931 ds.un("loadexception", this.onLoadError, this);
26932 ds.un("remove", this.updateInfo, this);
26933 ds.un("add", this.updateInfo, this);
26934 this.ds = undefined;
26938 * Binds the paging toolbar to the specified {@link Roo.data.Store}
26939 * @param {Roo.data.Store} store The data store to bind
26941 bind : function(ds){
26942 ds.on("beforeload", this.beforeLoad, this);
26943 ds.on("load", this.onLoad, this);
26944 ds.on("loadexception", this.onLoadError, this);
26945 ds.on("remove", this.updateInfo, this);
26946 ds.on("add", this.updateInfo, this);
26957 * @class Roo.bootstrap.MessageBar
26958 * @extends Roo.bootstrap.Component
26959 * Bootstrap MessageBar class
26960 * @cfg {String} html contents of the MessageBar
26961 * @cfg {String} weight (info | success | warning | danger) default info
26962 * @cfg {String} beforeClass insert the bar before the given class
26963 * @cfg {Boolean} closable (true | false) default false
26964 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26967 * Create a new Element
26968 * @param {Object} config The config object
26971 Roo.bootstrap.MessageBar = function(config){
26972 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26975 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
26981 beforeClass: 'bootstrap-sticky-wrap',
26983 getAutoCreate : function(){
26987 cls: 'alert alert-dismissable alert-' + this.weight,
26992 html: this.html || ''
26998 cfg.cls += ' alert-messages-fixed';
27012 onRender : function(ct, position)
27014 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27017 var cfg = Roo.apply({}, this.getAutoCreate());
27021 cfg.cls += ' ' + this.cls;
27024 cfg.style = this.style;
27026 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27028 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27031 this.el.select('>button.close').on('click', this.hide, this);
27037 if (!this.rendered) {
27043 this.fireEvent('show', this);
27049 if (!this.rendered) {
27055 this.fireEvent('hide', this);
27058 update : function()
27060 // var e = this.el.dom.firstChild;
27062 // if(this.closable){
27063 // e = e.nextSibling;
27066 // e.data = this.html || '';
27068 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27084 * @class Roo.bootstrap.Graph
27085 * @extends Roo.bootstrap.Component
27086 * Bootstrap Graph class
27090 @cfg {String} graphtype bar | vbar | pie
27091 @cfg {number} g_x coodinator | centre x (pie)
27092 @cfg {number} g_y coodinator | centre y (pie)
27093 @cfg {number} g_r radius (pie)
27094 @cfg {number} g_height height of the chart (respected by all elements in the set)
27095 @cfg {number} g_width width of the chart (respected by all elements in the set)
27096 @cfg {Object} title The title of the chart
27099 -opts (object) options for the chart
27101 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27102 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27104 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.
27105 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27107 o stretch (boolean)
27109 -opts (object) options for the pie
27112 o startAngle (number)
27113 o endAngle (number)
27117 * Create a new Input
27118 * @param {Object} config The config object
27121 Roo.bootstrap.Graph = function(config){
27122 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27128 * The img click event for the img.
27129 * @param {Roo.EventObject} e
27135 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27146 //g_colors: this.colors,
27153 getAutoCreate : function(){
27164 onRender : function(ct,position){
27167 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27169 if (typeof(Raphael) == 'undefined') {
27170 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27174 this.raphael = Raphael(this.el.dom);
27176 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27177 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27178 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27179 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27181 r.text(160, 10, "Single Series Chart").attr(txtattr);
27182 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27183 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27184 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27186 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27187 r.barchart(330, 10, 300, 220, data1);
27188 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27189 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27192 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27193 // r.barchart(30, 30, 560, 250, xdata, {
27194 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27195 // axis : "0 0 1 1",
27196 // axisxlabels : xdata
27197 // //yvalues : cols,
27200 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27202 // this.load(null,xdata,{
27203 // axis : "0 0 1 1",
27204 // axisxlabels : xdata
27209 load : function(graphtype,xdata,opts)
27211 this.raphael.clear();
27213 graphtype = this.graphtype;
27218 var r = this.raphael,
27219 fin = function () {
27220 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27222 fout = function () {
27223 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27225 pfin = function() {
27226 this.sector.stop();
27227 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27230 this.label[0].stop();
27231 this.label[0].attr({ r: 7.5 });
27232 this.label[1].attr({ "font-weight": 800 });
27235 pfout = function() {
27236 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27239 this.label[0].animate({ r: 5 }, 500, "bounce");
27240 this.label[1].attr({ "font-weight": 400 });
27246 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27249 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27252 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27253 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27255 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27262 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27267 setTitle: function(o)
27272 initEvents: function() {
27275 this.el.on('click', this.onClick, this);
27279 onClick : function(e)
27281 Roo.log('img onclick');
27282 this.fireEvent('click', this, e);
27294 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27297 * @class Roo.bootstrap.dash.NumberBox
27298 * @extends Roo.bootstrap.Component
27299 * Bootstrap NumberBox class
27300 * @cfg {String} headline Box headline
27301 * @cfg {String} content Box content
27302 * @cfg {String} icon Box icon
27303 * @cfg {String} footer Footer text
27304 * @cfg {String} fhref Footer href
27307 * Create a new NumberBox
27308 * @param {Object} config The config object
27312 Roo.bootstrap.dash.NumberBox = function(config){
27313 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27317 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27326 getAutoCreate : function(){
27330 cls : 'small-box ',
27338 cls : 'roo-headline',
27339 html : this.headline
27343 cls : 'roo-content',
27344 html : this.content
27358 cls : 'ion ' + this.icon
27367 cls : 'small-box-footer',
27368 href : this.fhref || '#',
27372 cfg.cn.push(footer);
27379 onRender : function(ct,position){
27380 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27387 setHeadline: function (value)
27389 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27392 setFooter: function (value, href)
27394 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27397 this.el.select('a.small-box-footer',true).first().attr('href', href);
27402 setContent: function (value)
27404 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27407 initEvents: function()
27421 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27424 * @class Roo.bootstrap.dash.TabBox
27425 * @extends Roo.bootstrap.Component
27426 * Bootstrap TabBox class
27427 * @cfg {String} title Title of the TabBox
27428 * @cfg {String} icon Icon of the TabBox
27429 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27430 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27433 * Create a new TabBox
27434 * @param {Object} config The config object
27438 Roo.bootstrap.dash.TabBox = function(config){
27439 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27444 * When a pane is added
27445 * @param {Roo.bootstrap.dash.TabPane} pane
27449 * @event activatepane
27450 * When a pane is activated
27451 * @param {Roo.bootstrap.dash.TabPane} pane
27453 "activatepane" : true
27461 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27466 tabScrollable : false,
27468 getChildContainer : function()
27470 return this.el.select('.tab-content', true).first();
27473 getAutoCreate : function(){
27477 cls: 'pull-left header',
27485 cls: 'fa ' + this.icon
27491 cls: 'nav nav-tabs pull-right',
27497 if(this.tabScrollable){
27504 cls: 'nav nav-tabs pull-right',
27515 cls: 'nav-tabs-custom',
27520 cls: 'tab-content no-padding',
27528 initEvents : function()
27530 //Roo.log('add add pane handler');
27531 this.on('addpane', this.onAddPane, this);
27534 * Updates the box title
27535 * @param {String} html to set the title to.
27537 setTitle : function(value)
27539 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27541 onAddPane : function(pane)
27543 this.panes.push(pane);
27544 //Roo.log('addpane');
27546 // tabs are rendere left to right..
27547 if(!this.showtabs){
27551 var ctr = this.el.select('.nav-tabs', true).first();
27554 var existing = ctr.select('.nav-tab',true);
27555 var qty = existing.getCount();;
27558 var tab = ctr.createChild({
27560 cls : 'nav-tab' + (qty ? '' : ' active'),
27568 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27571 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27573 pane.el.addClass('active');
27578 onTabClick : function(ev,un,ob,pane)
27580 //Roo.log('tab - prev default');
27581 ev.preventDefault();
27584 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27585 pane.tab.addClass('active');
27586 //Roo.log(pane.title);
27587 this.getChildContainer().select('.tab-pane',true).removeClass('active');
27588 // technically we should have a deactivate event.. but maybe add later.
27589 // and it should not de-activate the selected tab...
27590 this.fireEvent('activatepane', pane);
27591 pane.el.addClass('active');
27592 pane.fireEvent('activate');
27597 getActivePane : function()
27600 Roo.each(this.panes, function(p) {
27601 if(p.el.hasClass('active')){
27622 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27624 * @class Roo.bootstrap.TabPane
27625 * @extends Roo.bootstrap.Component
27626 * Bootstrap TabPane class
27627 * @cfg {Boolean} active (false | true) Default false
27628 * @cfg {String} title title of panel
27632 * Create a new TabPane
27633 * @param {Object} config The config object
27636 Roo.bootstrap.dash.TabPane = function(config){
27637 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27643 * When a pane is activated
27644 * @param {Roo.bootstrap.dash.TabPane} pane
27651 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27656 // the tabBox that this is attached to.
27659 getAutoCreate : function()
27667 cfg.cls += ' active';
27672 initEvents : function()
27674 //Roo.log('trigger add pane handler');
27675 this.parent().fireEvent('addpane', this)
27679 * Updates the tab title
27680 * @param {String} html to set the title to.
27682 setTitle: function(str)
27688 this.tab.select('a', true).first().dom.innerHTML = str;
27705 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27708 * @class Roo.bootstrap.menu.Menu
27709 * @extends Roo.bootstrap.Component
27710 * Bootstrap Menu class - container for Menu
27711 * @cfg {String} html Text of the menu
27712 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27713 * @cfg {String} icon Font awesome icon
27714 * @cfg {String} pos Menu align to (top | bottom) default bottom
27718 * Create a new Menu
27719 * @param {Object} config The config object
27723 Roo.bootstrap.menu.Menu = function(config){
27724 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27728 * @event beforeshow
27729 * Fires before this menu is displayed
27730 * @param {Roo.bootstrap.menu.Menu} this
27734 * @event beforehide
27735 * Fires before this menu is hidden
27736 * @param {Roo.bootstrap.menu.Menu} this
27741 * Fires after this menu is displayed
27742 * @param {Roo.bootstrap.menu.Menu} this
27747 * Fires after this menu is hidden
27748 * @param {Roo.bootstrap.menu.Menu} this
27753 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27754 * @param {Roo.bootstrap.menu.Menu} this
27755 * @param {Roo.EventObject} e
27762 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
27766 weight : 'default',
27771 getChildContainer : function() {
27772 if(this.isSubMenu){
27776 return this.el.select('ul.dropdown-menu', true).first();
27779 getAutoCreate : function()
27784 cls : 'roo-menu-text',
27792 cls : 'fa ' + this.icon
27803 cls : 'dropdown-button btn btn-' + this.weight,
27808 cls : 'dropdown-toggle btn btn-' + this.weight,
27818 cls : 'dropdown-menu'
27824 if(this.pos == 'top'){
27825 cfg.cls += ' dropup';
27828 if(this.isSubMenu){
27831 cls : 'dropdown-menu'
27838 onRender : function(ct, position)
27840 this.isSubMenu = ct.hasClass('dropdown-submenu');
27842 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27845 initEvents : function()
27847 if(this.isSubMenu){
27851 this.hidden = true;
27853 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27854 this.triggerEl.on('click', this.onTriggerPress, this);
27856 this.buttonEl = this.el.select('button.dropdown-button', true).first();
27857 this.buttonEl.on('click', this.onClick, this);
27863 if(this.isSubMenu){
27867 return this.el.select('ul.dropdown-menu', true).first();
27870 onClick : function(e)
27872 this.fireEvent("click", this, e);
27875 onTriggerPress : function(e)
27877 if (this.isVisible()) {
27884 isVisible : function(){
27885 return !this.hidden;
27890 this.fireEvent("beforeshow", this);
27892 this.hidden = false;
27893 this.el.addClass('open');
27895 Roo.get(document).on("mouseup", this.onMouseUp, this);
27897 this.fireEvent("show", this);
27904 this.fireEvent("beforehide", this);
27906 this.hidden = true;
27907 this.el.removeClass('open');
27909 Roo.get(document).un("mouseup", this.onMouseUp);
27911 this.fireEvent("hide", this);
27914 onMouseUp : function()
27928 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27931 * @class Roo.bootstrap.menu.Item
27932 * @extends Roo.bootstrap.Component
27933 * Bootstrap MenuItem class
27934 * @cfg {Boolean} submenu (true | false) default false
27935 * @cfg {String} html text of the item
27936 * @cfg {String} href the link
27937 * @cfg {Boolean} disable (true | false) default false
27938 * @cfg {Boolean} preventDefault (true | false) default true
27939 * @cfg {String} icon Font awesome icon
27940 * @cfg {String} pos Submenu align to (left | right) default right
27944 * Create a new Item
27945 * @param {Object} config The config object
27949 Roo.bootstrap.menu.Item = function(config){
27950 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27954 * Fires when the mouse is hovering over this menu
27955 * @param {Roo.bootstrap.menu.Item} this
27956 * @param {Roo.EventObject} e
27961 * Fires when the mouse exits this menu
27962 * @param {Roo.bootstrap.menu.Item} this
27963 * @param {Roo.EventObject} e
27969 * The raw click event for the entire grid.
27970 * @param {Roo.EventObject} e
27976 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
27981 preventDefault: true,
27986 getAutoCreate : function()
27991 cls : 'roo-menu-item-text',
27999 cls : 'fa ' + this.icon
28008 href : this.href || '#',
28015 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28019 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28021 if(this.pos == 'left'){
28022 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28029 initEvents : function()
28031 this.el.on('mouseover', this.onMouseOver, this);
28032 this.el.on('mouseout', this.onMouseOut, this);
28034 this.el.select('a', true).first().on('click', this.onClick, this);
28038 onClick : function(e)
28040 if(this.preventDefault){
28041 e.preventDefault();
28044 this.fireEvent("click", this, e);
28047 onMouseOver : function(e)
28049 if(this.submenu && this.pos == 'left'){
28050 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28053 this.fireEvent("mouseover", this, e);
28056 onMouseOut : function(e)
28058 this.fireEvent("mouseout", this, e);
28070 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28073 * @class Roo.bootstrap.menu.Separator
28074 * @extends Roo.bootstrap.Component
28075 * Bootstrap Separator class
28078 * Create a new Separator
28079 * @param {Object} config The config object
28083 Roo.bootstrap.menu.Separator = function(config){
28084 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28087 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28089 getAutoCreate : function(){
28110 * @class Roo.bootstrap.Tooltip
28111 * Bootstrap Tooltip class
28112 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28113 * to determine which dom element triggers the tooltip.
28115 * It needs to add support for additional attributes like tooltip-position
28118 * Create a new Toolti
28119 * @param {Object} config The config object
28122 Roo.bootstrap.Tooltip = function(config){
28123 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28125 this.alignment = Roo.bootstrap.Tooltip.alignment;
28127 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28128 this.alignment = config.alignment;
28133 Roo.apply(Roo.bootstrap.Tooltip, {
28135 * @function init initialize tooltip monitoring.
28139 currentTip : false,
28140 currentRegion : false,
28146 Roo.get(document).on('mouseover', this.enter ,this);
28147 Roo.get(document).on('mouseout', this.leave, this);
28150 this.currentTip = new Roo.bootstrap.Tooltip();
28153 enter : function(ev)
28155 var dom = ev.getTarget();
28157 //Roo.log(['enter',dom]);
28158 var el = Roo.fly(dom);
28159 if (this.currentEl) {
28161 //Roo.log(this.currentEl);
28162 //Roo.log(this.currentEl.contains(dom));
28163 if (this.currentEl == el) {
28166 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28172 if (this.currentTip.el) {
28173 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28177 if(!el || el.dom == document){
28183 // you can not look for children, as if el is the body.. then everythign is the child..
28184 if (!el.attr('tooltip')) { //
28185 if (!el.select("[tooltip]").elements.length) {
28188 // is the mouse over this child...?
28189 bindEl = el.select("[tooltip]").first();
28190 var xy = ev.getXY();
28191 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28192 //Roo.log("not in region.");
28195 //Roo.log("child element over..");
28198 this.currentEl = bindEl;
28199 this.currentTip.bind(bindEl);
28200 this.currentRegion = Roo.lib.Region.getRegion(dom);
28201 this.currentTip.enter();
28204 leave : function(ev)
28206 var dom = ev.getTarget();
28207 //Roo.log(['leave',dom]);
28208 if (!this.currentEl) {
28213 if (dom != this.currentEl.dom) {
28216 var xy = ev.getXY();
28217 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28220 // only activate leave if mouse cursor is outside... bounding box..
28225 if (this.currentTip) {
28226 this.currentTip.leave();
28228 //Roo.log('clear currentEl');
28229 this.currentEl = false;
28234 'left' : ['r-l', [-2,0], 'right'],
28235 'right' : ['l-r', [2,0], 'left'],
28236 'bottom' : ['t-b', [0,2], 'top'],
28237 'top' : [ 'b-t', [0,-2], 'bottom']
28243 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28248 delay : null, // can be { show : 300 , hide: 500}
28252 hoverState : null, //???
28254 placement : 'bottom',
28258 getAutoCreate : function(){
28265 cls : 'tooltip-arrow'
28268 cls : 'tooltip-inner'
28275 bind : function(el)
28281 enter : function () {
28283 if (this.timeout != null) {
28284 clearTimeout(this.timeout);
28287 this.hoverState = 'in';
28288 //Roo.log("enter - show");
28289 if (!this.delay || !this.delay.show) {
28294 this.timeout = setTimeout(function () {
28295 if (_t.hoverState == 'in') {
28298 }, this.delay.show);
28302 clearTimeout(this.timeout);
28304 this.hoverState = 'out';
28305 if (!this.delay || !this.delay.hide) {
28311 this.timeout = setTimeout(function () {
28312 //Roo.log("leave - timeout");
28314 if (_t.hoverState == 'out') {
28316 Roo.bootstrap.Tooltip.currentEl = false;
28321 show : function (msg)
28324 this.render(document.body);
28327 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28329 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28331 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28333 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
28335 var placement = typeof this.placement == 'function' ?
28336 this.placement.call(this, this.el, on_el) :
28339 var autoToken = /\s?auto?\s?/i;
28340 var autoPlace = autoToken.test(placement);
28342 placement = placement.replace(autoToken, '') || 'top';
28346 //this.el.setXY([0,0]);
28348 //this.el.dom.style.display='block';
28350 //this.el.appendTo(on_el);
28352 var p = this.getPosition();
28353 var box = this.el.getBox();
28359 var align = this.alignment[placement];
28361 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28363 if(placement == 'top' || placement == 'bottom'){
28365 placement = 'right';
28368 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28369 placement = 'left';
28372 var scroll = Roo.select('body', true).first().getScroll();
28374 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28378 align = this.alignment[placement];
28381 this.el.alignTo(this.bindEl, align[0],align[1]);
28382 //var arrow = this.el.select('.arrow',true).first();
28383 //arrow.set(align[2],
28385 this.el.addClass(placement);
28387 this.el.addClass('in fade');
28389 this.hoverState = null;
28391 if (this.el.hasClass('fade')) {
28402 //this.el.setXY([0,0]);
28403 this.el.removeClass('in');
28419 * @class Roo.bootstrap.LocationPicker
28420 * @extends Roo.bootstrap.Component
28421 * Bootstrap LocationPicker class
28422 * @cfg {Number} latitude Position when init default 0
28423 * @cfg {Number} longitude Position when init default 0
28424 * @cfg {Number} zoom default 15
28425 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28426 * @cfg {Boolean} mapTypeControl default false
28427 * @cfg {Boolean} disableDoubleClickZoom default false
28428 * @cfg {Boolean} scrollwheel default true
28429 * @cfg {Boolean} streetViewControl default false
28430 * @cfg {Number} radius default 0
28431 * @cfg {String} locationName
28432 * @cfg {Boolean} draggable default true
28433 * @cfg {Boolean} enableAutocomplete default false
28434 * @cfg {Boolean} enableReverseGeocode default true
28435 * @cfg {String} markerTitle
28438 * Create a new LocationPicker
28439 * @param {Object} config The config object
28443 Roo.bootstrap.LocationPicker = function(config){
28445 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28450 * Fires when the picker initialized.
28451 * @param {Roo.bootstrap.LocationPicker} this
28452 * @param {Google Location} location
28456 * @event positionchanged
28457 * Fires when the picker position changed.
28458 * @param {Roo.bootstrap.LocationPicker} this
28459 * @param {Google Location} location
28461 positionchanged : true,
28464 * Fires when the map resize.
28465 * @param {Roo.bootstrap.LocationPicker} this
28470 * Fires when the map show.
28471 * @param {Roo.bootstrap.LocationPicker} this
28476 * Fires when the map hide.
28477 * @param {Roo.bootstrap.LocationPicker} this
28482 * Fires when click the map.
28483 * @param {Roo.bootstrap.LocationPicker} this
28484 * @param {Map event} e
28488 * @event mapRightClick
28489 * Fires when right click the map.
28490 * @param {Roo.bootstrap.LocationPicker} this
28491 * @param {Map event} e
28493 mapRightClick : true,
28495 * @event markerClick
28496 * Fires when click the marker.
28497 * @param {Roo.bootstrap.LocationPicker} this
28498 * @param {Map event} e
28500 markerClick : true,
28502 * @event markerRightClick
28503 * Fires when right click the marker.
28504 * @param {Roo.bootstrap.LocationPicker} this
28505 * @param {Map event} e
28507 markerRightClick : true,
28509 * @event OverlayViewDraw
28510 * Fires when OverlayView Draw
28511 * @param {Roo.bootstrap.LocationPicker} this
28513 OverlayViewDraw : true,
28515 * @event OverlayViewOnAdd
28516 * Fires when OverlayView Draw
28517 * @param {Roo.bootstrap.LocationPicker} this
28519 OverlayViewOnAdd : true,
28521 * @event OverlayViewOnRemove
28522 * Fires when OverlayView Draw
28523 * @param {Roo.bootstrap.LocationPicker} this
28525 OverlayViewOnRemove : true,
28527 * @event OverlayViewShow
28528 * Fires when OverlayView Draw
28529 * @param {Roo.bootstrap.LocationPicker} this
28530 * @param {Pixel} cpx
28532 OverlayViewShow : true,
28534 * @event OverlayViewHide
28535 * Fires when OverlayView Draw
28536 * @param {Roo.bootstrap.LocationPicker} this
28538 OverlayViewHide : true,
28540 * @event loadexception
28541 * Fires when load google lib failed.
28542 * @param {Roo.bootstrap.LocationPicker} this
28544 loadexception : true
28549 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
28551 gMapContext: false,
28557 mapTypeControl: false,
28558 disableDoubleClickZoom: false,
28560 streetViewControl: false,
28564 enableAutocomplete: false,
28565 enableReverseGeocode: true,
28568 getAutoCreate: function()
28573 cls: 'roo-location-picker'
28579 initEvents: function(ct, position)
28581 if(!this.el.getWidth() || this.isApplied()){
28585 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28590 initial: function()
28592 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28593 this.fireEvent('loadexception', this);
28597 if(!this.mapTypeId){
28598 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28601 this.gMapContext = this.GMapContext();
28603 this.initOverlayView();
28605 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28609 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28610 _this.setPosition(_this.gMapContext.marker.position);
28613 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28614 _this.fireEvent('mapClick', this, event);
28618 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28619 _this.fireEvent('mapRightClick', this, event);
28623 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28624 _this.fireEvent('markerClick', this, event);
28628 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28629 _this.fireEvent('markerRightClick', this, event);
28633 this.setPosition(this.gMapContext.location);
28635 this.fireEvent('initial', this, this.gMapContext.location);
28638 initOverlayView: function()
28642 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28646 _this.fireEvent('OverlayViewDraw', _this);
28651 _this.fireEvent('OverlayViewOnAdd', _this);
28654 onRemove: function()
28656 _this.fireEvent('OverlayViewOnRemove', _this);
28659 show: function(cpx)
28661 _this.fireEvent('OverlayViewShow', _this, cpx);
28666 _this.fireEvent('OverlayViewHide', _this);
28672 fromLatLngToContainerPixel: function(event)
28674 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28677 isApplied: function()
28679 return this.getGmapContext() == false ? false : true;
28682 getGmapContext: function()
28684 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28687 GMapContext: function()
28689 var position = new google.maps.LatLng(this.latitude, this.longitude);
28691 var _map = new google.maps.Map(this.el.dom, {
28694 mapTypeId: this.mapTypeId,
28695 mapTypeControl: this.mapTypeControl,
28696 disableDoubleClickZoom: this.disableDoubleClickZoom,
28697 scrollwheel: this.scrollwheel,
28698 streetViewControl: this.streetViewControl,
28699 locationName: this.locationName,
28700 draggable: this.draggable,
28701 enableAutocomplete: this.enableAutocomplete,
28702 enableReverseGeocode: this.enableReverseGeocode
28705 var _marker = new google.maps.Marker({
28706 position: position,
28708 title: this.markerTitle,
28709 draggable: this.draggable
28716 location: position,
28717 radius: this.radius,
28718 locationName: this.locationName,
28719 addressComponents: {
28720 formatted_address: null,
28721 addressLine1: null,
28722 addressLine2: null,
28724 streetNumber: null,
28728 stateOrProvince: null
28731 domContainer: this.el.dom,
28732 geodecoder: new google.maps.Geocoder()
28736 drawCircle: function(center, radius, options)
28738 if (this.gMapContext.circle != null) {
28739 this.gMapContext.circle.setMap(null);
28743 options = Roo.apply({}, options, {
28744 strokeColor: "#0000FF",
28745 strokeOpacity: .35,
28747 fillColor: "#0000FF",
28751 options.map = this.gMapContext.map;
28752 options.radius = radius;
28753 options.center = center;
28754 this.gMapContext.circle = new google.maps.Circle(options);
28755 return this.gMapContext.circle;
28761 setPosition: function(location)
28763 this.gMapContext.location = location;
28764 this.gMapContext.marker.setPosition(location);
28765 this.gMapContext.map.panTo(location);
28766 this.drawCircle(location, this.gMapContext.radius, {});
28770 if (this.gMapContext.settings.enableReverseGeocode) {
28771 this.gMapContext.geodecoder.geocode({
28772 latLng: this.gMapContext.location
28773 }, function(results, status) {
28775 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28776 _this.gMapContext.locationName = results[0].formatted_address;
28777 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28779 _this.fireEvent('positionchanged', this, location);
28786 this.fireEvent('positionchanged', this, location);
28791 google.maps.event.trigger(this.gMapContext.map, "resize");
28793 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28795 this.fireEvent('resize', this);
28798 setPositionByLatLng: function(latitude, longitude)
28800 this.setPosition(new google.maps.LatLng(latitude, longitude));
28803 getCurrentPosition: function()
28806 latitude: this.gMapContext.location.lat(),
28807 longitude: this.gMapContext.location.lng()
28811 getAddressName: function()
28813 return this.gMapContext.locationName;
28816 getAddressComponents: function()
28818 return this.gMapContext.addressComponents;
28821 address_component_from_google_geocode: function(address_components)
28825 for (var i = 0; i < address_components.length; i++) {
28826 var component = address_components[i];
28827 if (component.types.indexOf("postal_code") >= 0) {
28828 result.postalCode = component.short_name;
28829 } else if (component.types.indexOf("street_number") >= 0) {
28830 result.streetNumber = component.short_name;
28831 } else if (component.types.indexOf("route") >= 0) {
28832 result.streetName = component.short_name;
28833 } else if (component.types.indexOf("neighborhood") >= 0) {
28834 result.city = component.short_name;
28835 } else if (component.types.indexOf("locality") >= 0) {
28836 result.city = component.short_name;
28837 } else if (component.types.indexOf("sublocality") >= 0) {
28838 result.district = component.short_name;
28839 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28840 result.stateOrProvince = component.short_name;
28841 } else if (component.types.indexOf("country") >= 0) {
28842 result.country = component.short_name;
28846 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28847 result.addressLine2 = "";
28851 setZoomLevel: function(zoom)
28853 this.gMapContext.map.setZoom(zoom);
28866 this.fireEvent('show', this);
28877 this.fireEvent('hide', this);
28882 Roo.apply(Roo.bootstrap.LocationPicker, {
28884 OverlayView : function(map, options)
28886 options = options || {};
28893 * @class Roo.bootstrap.Alert
28894 * @extends Roo.bootstrap.Component
28895 * Bootstrap Alert class - shows an alert area box
28897 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28898 Enter a valid email address
28901 * @cfg {String} title The title of alert
28902 * @cfg {String} html The content of alert
28903 * @cfg {String} weight ( success | info | warning | danger )
28904 * @cfg {String} faicon font-awesomeicon
28907 * Create a new alert
28908 * @param {Object} config The config object
28912 Roo.bootstrap.Alert = function(config){
28913 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28917 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
28924 getAutoCreate : function()
28933 cls : 'roo-alert-icon'
28938 cls : 'roo-alert-title',
28943 cls : 'roo-alert-text',
28950 cfg.cn[0].cls += ' fa ' + this.faicon;
28954 cfg.cls += ' alert-' + this.weight;
28960 initEvents: function()
28962 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28965 setTitle : function(str)
28967 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28970 setText : function(str)
28972 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28975 setWeight : function(weight)
28978 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28981 this.weight = weight;
28983 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28986 setIcon : function(icon)
28989 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28992 this.faicon = icon;
28994 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29015 * @class Roo.bootstrap.UploadCropbox
29016 * @extends Roo.bootstrap.Component
29017 * Bootstrap UploadCropbox class
29018 * @cfg {String} emptyText show when image has been loaded
29019 * @cfg {String} rotateNotify show when image too small to rotate
29020 * @cfg {Number} errorTimeout default 3000
29021 * @cfg {Number} minWidth default 300
29022 * @cfg {Number} minHeight default 300
29023 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29024 * @cfg {Boolean} isDocument (true|false) default false
29025 * @cfg {String} url action url
29026 * @cfg {String} paramName default 'imageUpload'
29027 * @cfg {String} method default POST
29028 * @cfg {Boolean} loadMask (true|false) default true
29029 * @cfg {Boolean} loadingText default 'Loading...'
29032 * Create a new UploadCropbox
29033 * @param {Object} config The config object
29036 Roo.bootstrap.UploadCropbox = function(config){
29037 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29041 * @event beforeselectfile
29042 * Fire before select file
29043 * @param {Roo.bootstrap.UploadCropbox} this
29045 "beforeselectfile" : true,
29048 * Fire after initEvent
29049 * @param {Roo.bootstrap.UploadCropbox} this
29054 * Fire after initEvent
29055 * @param {Roo.bootstrap.UploadCropbox} this
29056 * @param {String} data
29061 * Fire when preparing the file data
29062 * @param {Roo.bootstrap.UploadCropbox} this
29063 * @param {Object} file
29068 * Fire when get exception
29069 * @param {Roo.bootstrap.UploadCropbox} this
29070 * @param {XMLHttpRequest} xhr
29072 "exception" : true,
29074 * @event beforeloadcanvas
29075 * Fire before load the canvas
29076 * @param {Roo.bootstrap.UploadCropbox} this
29077 * @param {String} src
29079 "beforeloadcanvas" : true,
29082 * Fire when trash image
29083 * @param {Roo.bootstrap.UploadCropbox} this
29088 * Fire when download the image
29089 * @param {Roo.bootstrap.UploadCropbox} this
29093 * @event footerbuttonclick
29094 * Fire when footerbuttonclick
29095 * @param {Roo.bootstrap.UploadCropbox} this
29096 * @param {String} type
29098 "footerbuttonclick" : true,
29102 * @param {Roo.bootstrap.UploadCropbox} this
29107 * Fire when rotate the image
29108 * @param {Roo.bootstrap.UploadCropbox} this
29109 * @param {String} pos
29114 * Fire when inspect the file
29115 * @param {Roo.bootstrap.UploadCropbox} this
29116 * @param {Object} file
29121 * Fire when xhr upload the file
29122 * @param {Roo.bootstrap.UploadCropbox} this
29123 * @param {Object} data
29128 * Fire when arrange the file data
29129 * @param {Roo.bootstrap.UploadCropbox} this
29130 * @param {Object} formData
29135 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29138 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29140 emptyText : 'Click to upload image',
29141 rotateNotify : 'Image is too small to rotate',
29142 errorTimeout : 3000,
29156 cropType : 'image/jpeg',
29158 canvasLoaded : false,
29159 isDocument : false,
29161 paramName : 'imageUpload',
29163 loadingText : 'Loading...',
29166 getAutoCreate : function()
29170 cls : 'roo-upload-cropbox',
29174 cls : 'roo-upload-cropbox-selector',
29179 cls : 'roo-upload-cropbox-body',
29180 style : 'cursor:pointer',
29184 cls : 'roo-upload-cropbox-preview'
29188 cls : 'roo-upload-cropbox-thumb'
29192 cls : 'roo-upload-cropbox-empty-notify',
29193 html : this.emptyText
29197 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29198 html : this.rotateNotify
29204 cls : 'roo-upload-cropbox-footer',
29207 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29217 onRender : function(ct, position)
29219 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29221 if (this.buttons.length) {
29223 Roo.each(this.buttons, function(bb) {
29225 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29227 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29233 this.maskEl = this.el;
29237 initEvents : function()
29239 this.urlAPI = (window.createObjectURL && window) ||
29240 (window.URL && URL.revokeObjectURL && URL) ||
29241 (window.webkitURL && webkitURL);
29243 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29244 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29246 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29247 this.selectorEl.hide();
29249 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29250 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29252 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29253 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29254 this.thumbEl.hide();
29256 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29257 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29259 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29260 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29261 this.errorEl.hide();
29263 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29264 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29265 this.footerEl.hide();
29267 this.setThumbBoxSize();
29273 this.fireEvent('initial', this);
29280 window.addEventListener("resize", function() { _this.resize(); } );
29282 this.bodyEl.on('click', this.beforeSelectFile, this);
29285 this.bodyEl.on('touchstart', this.onTouchStart, this);
29286 this.bodyEl.on('touchmove', this.onTouchMove, this);
29287 this.bodyEl.on('touchend', this.onTouchEnd, this);
29291 this.bodyEl.on('mousedown', this.onMouseDown, this);
29292 this.bodyEl.on('mousemove', this.onMouseMove, this);
29293 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29294 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29295 Roo.get(document).on('mouseup', this.onMouseUp, this);
29298 this.selectorEl.on('change', this.onFileSelected, this);
29304 this.baseScale = 1;
29306 this.baseRotate = 1;
29307 this.dragable = false;
29308 this.pinching = false;
29311 this.cropData = false;
29312 this.notifyEl.dom.innerHTML = this.emptyText;
29314 this.selectorEl.dom.value = '';
29318 resize : function()
29320 if(this.fireEvent('resize', this) != false){
29321 this.setThumbBoxPosition();
29322 this.setCanvasPosition();
29326 onFooterButtonClick : function(e, el, o, type)
29329 case 'rotate-left' :
29330 this.onRotateLeft(e);
29332 case 'rotate-right' :
29333 this.onRotateRight(e);
29336 this.beforeSelectFile(e);
29351 this.fireEvent('footerbuttonclick', this, type);
29354 beforeSelectFile : function(e)
29356 e.preventDefault();
29358 if(this.fireEvent('beforeselectfile', this) != false){
29359 this.selectorEl.dom.click();
29363 onFileSelected : function(e)
29365 e.preventDefault();
29367 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29371 var file = this.selectorEl.dom.files[0];
29373 if(this.fireEvent('inspect', this, file) != false){
29374 this.prepare(file);
29379 trash : function(e)
29381 this.fireEvent('trash', this);
29384 download : function(e)
29386 this.fireEvent('download', this);
29389 loadCanvas : function(src)
29391 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29395 this.imageEl = document.createElement('img');
29399 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29401 this.imageEl.src = src;
29405 onLoadCanvas : function()
29407 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29408 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29410 this.bodyEl.un('click', this.beforeSelectFile, this);
29412 this.notifyEl.hide();
29413 this.thumbEl.show();
29414 this.footerEl.show();
29416 this.baseRotateLevel();
29418 if(this.isDocument){
29419 this.setThumbBoxSize();
29422 this.setThumbBoxPosition();
29424 this.baseScaleLevel();
29430 this.canvasLoaded = true;
29433 this.maskEl.unmask();
29438 setCanvasPosition : function()
29440 if(!this.canvasEl){
29444 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29445 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29447 this.previewEl.setLeft(pw);
29448 this.previewEl.setTop(ph);
29452 onMouseDown : function(e)
29456 this.dragable = true;
29457 this.pinching = false;
29459 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29460 this.dragable = false;
29464 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29465 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29469 onMouseMove : function(e)
29473 if(!this.canvasLoaded){
29477 if (!this.dragable){
29481 var minX = Math.ceil(this.thumbEl.getLeft(true));
29482 var minY = Math.ceil(this.thumbEl.getTop(true));
29484 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29485 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29487 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29488 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29490 x = x - this.mouseX;
29491 y = y - this.mouseY;
29493 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29494 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29496 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29497 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29499 this.previewEl.setLeft(bgX);
29500 this.previewEl.setTop(bgY);
29502 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29503 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29506 onMouseUp : function(e)
29510 this.dragable = false;
29513 onMouseWheel : function(e)
29517 this.startScale = this.scale;
29519 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29521 if(!this.zoomable()){
29522 this.scale = this.startScale;
29531 zoomable : function()
29533 var minScale = this.thumbEl.getWidth() / this.minWidth;
29535 if(this.minWidth < this.minHeight){
29536 minScale = this.thumbEl.getHeight() / this.minHeight;
29539 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29540 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29544 (this.rotate == 0 || this.rotate == 180) &&
29546 width > this.imageEl.OriginWidth ||
29547 height > this.imageEl.OriginHeight ||
29548 (width < this.minWidth && height < this.minHeight)
29556 (this.rotate == 90 || this.rotate == 270) &&
29558 width > this.imageEl.OriginWidth ||
29559 height > this.imageEl.OriginHeight ||
29560 (width < this.minHeight && height < this.minWidth)
29567 !this.isDocument &&
29568 (this.rotate == 0 || this.rotate == 180) &&
29570 width < this.minWidth ||
29571 width > this.imageEl.OriginWidth ||
29572 height < this.minHeight ||
29573 height > this.imageEl.OriginHeight
29580 !this.isDocument &&
29581 (this.rotate == 90 || this.rotate == 270) &&
29583 width < this.minHeight ||
29584 width > this.imageEl.OriginWidth ||
29585 height < this.minWidth ||
29586 height > this.imageEl.OriginHeight
29596 onRotateLeft : function(e)
29598 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29600 var minScale = this.thumbEl.getWidth() / this.minWidth;
29602 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29603 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29605 this.startScale = this.scale;
29607 while (this.getScaleLevel() < minScale){
29609 this.scale = this.scale + 1;
29611 if(!this.zoomable()){
29616 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29617 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29622 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29629 this.scale = this.startScale;
29631 this.onRotateFail();
29636 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29638 if(this.isDocument){
29639 this.setThumbBoxSize();
29640 this.setThumbBoxPosition();
29641 this.setCanvasPosition();
29646 this.fireEvent('rotate', this, 'left');
29650 onRotateRight : function(e)
29652 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29654 var minScale = this.thumbEl.getWidth() / this.minWidth;
29656 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29657 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29659 this.startScale = this.scale;
29661 while (this.getScaleLevel() < minScale){
29663 this.scale = this.scale + 1;
29665 if(!this.zoomable()){
29670 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29671 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29676 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29683 this.scale = this.startScale;
29685 this.onRotateFail();
29690 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29692 if(this.isDocument){
29693 this.setThumbBoxSize();
29694 this.setThumbBoxPosition();
29695 this.setCanvasPosition();
29700 this.fireEvent('rotate', this, 'right');
29703 onRotateFail : function()
29705 this.errorEl.show(true);
29709 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29714 this.previewEl.dom.innerHTML = '';
29716 var canvasEl = document.createElement("canvas");
29718 var contextEl = canvasEl.getContext("2d");
29720 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29721 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29722 var center = this.imageEl.OriginWidth / 2;
29724 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29725 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29726 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29727 center = this.imageEl.OriginHeight / 2;
29730 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29732 contextEl.translate(center, center);
29733 contextEl.rotate(this.rotate * Math.PI / 180);
29735 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29737 this.canvasEl = document.createElement("canvas");
29739 this.contextEl = this.canvasEl.getContext("2d");
29741 switch (this.rotate) {
29744 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29745 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29747 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29752 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29753 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29755 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29756 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);
29760 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29765 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29766 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29768 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29769 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);
29773 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);
29778 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29779 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29781 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29782 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29786 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);
29793 this.previewEl.appendChild(this.canvasEl);
29795 this.setCanvasPosition();
29800 if(!this.canvasLoaded){
29804 var imageCanvas = document.createElement("canvas");
29806 var imageContext = imageCanvas.getContext("2d");
29808 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29809 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29811 var center = imageCanvas.width / 2;
29813 imageContext.translate(center, center);
29815 imageContext.rotate(this.rotate * Math.PI / 180);
29817 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29819 var canvas = document.createElement("canvas");
29821 var context = canvas.getContext("2d");
29823 canvas.width = this.minWidth;
29824 canvas.height = this.minHeight;
29826 switch (this.rotate) {
29829 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29830 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29832 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29833 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29835 var targetWidth = this.minWidth - 2 * x;
29836 var targetHeight = this.minHeight - 2 * y;
29840 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29841 scale = targetWidth / width;
29844 if(x > 0 && y == 0){
29845 scale = targetHeight / height;
29848 if(x > 0 && y > 0){
29849 scale = targetWidth / width;
29851 if(width < height){
29852 scale = targetHeight / height;
29856 context.scale(scale, scale);
29858 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29859 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29861 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29862 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29864 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29869 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29870 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29872 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29873 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29875 var targetWidth = this.minWidth - 2 * x;
29876 var targetHeight = this.minHeight - 2 * y;
29880 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29881 scale = targetWidth / width;
29884 if(x > 0 && y == 0){
29885 scale = targetHeight / height;
29888 if(x > 0 && y > 0){
29889 scale = targetWidth / width;
29891 if(width < height){
29892 scale = targetHeight / height;
29896 context.scale(scale, scale);
29898 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29899 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29901 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29902 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29904 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29906 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29911 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29912 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29914 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29915 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29917 var targetWidth = this.minWidth - 2 * x;
29918 var targetHeight = this.minHeight - 2 * y;
29922 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29923 scale = targetWidth / width;
29926 if(x > 0 && y == 0){
29927 scale = targetHeight / height;
29930 if(x > 0 && y > 0){
29931 scale = targetWidth / width;
29933 if(width < height){
29934 scale = targetHeight / height;
29938 context.scale(scale, scale);
29940 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29941 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29943 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29944 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29946 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29947 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29949 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29954 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29955 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29957 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29958 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29960 var targetWidth = this.minWidth - 2 * x;
29961 var targetHeight = this.minHeight - 2 * y;
29965 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29966 scale = targetWidth / width;
29969 if(x > 0 && y == 0){
29970 scale = targetHeight / height;
29973 if(x > 0 && y > 0){
29974 scale = targetWidth / width;
29976 if(width < height){
29977 scale = targetHeight / height;
29981 context.scale(scale, scale);
29983 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29984 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29986 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29987 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29989 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29991 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29998 this.cropData = canvas.toDataURL(this.cropType);
30000 if(this.fireEvent('crop', this, this.cropData) !== false){
30001 this.process(this.file, this.cropData);
30008 setThumbBoxSize : function()
30012 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30013 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30014 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30016 this.minWidth = width;
30017 this.minHeight = height;
30019 if(this.rotate == 90 || this.rotate == 270){
30020 this.minWidth = height;
30021 this.minHeight = width;
30026 width = Math.ceil(this.minWidth * height / this.minHeight);
30028 if(this.minWidth > this.minHeight){
30030 height = Math.ceil(this.minHeight * width / this.minWidth);
30033 this.thumbEl.setStyle({
30034 width : width + 'px',
30035 height : height + 'px'
30042 setThumbBoxPosition : function()
30044 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30045 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30047 this.thumbEl.setLeft(x);
30048 this.thumbEl.setTop(y);
30052 baseRotateLevel : function()
30054 this.baseRotate = 1;
30057 typeof(this.exif) != 'undefined' &&
30058 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30059 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30061 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30064 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30068 baseScaleLevel : function()
30072 if(this.isDocument){
30074 if(this.baseRotate == 6 || this.baseRotate == 8){
30076 height = this.thumbEl.getHeight();
30077 this.baseScale = height / this.imageEl.OriginWidth;
30079 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30080 width = this.thumbEl.getWidth();
30081 this.baseScale = width / this.imageEl.OriginHeight;
30087 height = this.thumbEl.getHeight();
30088 this.baseScale = height / this.imageEl.OriginHeight;
30090 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30091 width = this.thumbEl.getWidth();
30092 this.baseScale = width / this.imageEl.OriginWidth;
30098 if(this.baseRotate == 6 || this.baseRotate == 8){
30100 width = this.thumbEl.getHeight();
30101 this.baseScale = width / this.imageEl.OriginHeight;
30103 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30104 height = this.thumbEl.getWidth();
30105 this.baseScale = height / this.imageEl.OriginHeight;
30108 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30109 height = this.thumbEl.getWidth();
30110 this.baseScale = height / this.imageEl.OriginHeight;
30112 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30113 width = this.thumbEl.getHeight();
30114 this.baseScale = width / this.imageEl.OriginWidth;
30121 width = this.thumbEl.getWidth();
30122 this.baseScale = width / this.imageEl.OriginWidth;
30124 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30125 height = this.thumbEl.getHeight();
30126 this.baseScale = height / this.imageEl.OriginHeight;
30129 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30131 height = this.thumbEl.getHeight();
30132 this.baseScale = height / this.imageEl.OriginHeight;
30134 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30135 width = this.thumbEl.getWidth();
30136 this.baseScale = width / this.imageEl.OriginWidth;
30144 getScaleLevel : function()
30146 return this.baseScale * Math.pow(1.1, this.scale);
30149 onTouchStart : function(e)
30151 if(!this.canvasLoaded){
30152 this.beforeSelectFile(e);
30156 var touches = e.browserEvent.touches;
30162 if(touches.length == 1){
30163 this.onMouseDown(e);
30167 if(touches.length != 2){
30173 for(var i = 0, finger; finger = touches[i]; i++){
30174 coords.push(finger.pageX, finger.pageY);
30177 var x = Math.pow(coords[0] - coords[2], 2);
30178 var y = Math.pow(coords[1] - coords[3], 2);
30180 this.startDistance = Math.sqrt(x + y);
30182 this.startScale = this.scale;
30184 this.pinching = true;
30185 this.dragable = false;
30189 onTouchMove : function(e)
30191 if(!this.pinching && !this.dragable){
30195 var touches = e.browserEvent.touches;
30202 this.onMouseMove(e);
30208 for(var i = 0, finger; finger = touches[i]; i++){
30209 coords.push(finger.pageX, finger.pageY);
30212 var x = Math.pow(coords[0] - coords[2], 2);
30213 var y = Math.pow(coords[1] - coords[3], 2);
30215 this.endDistance = Math.sqrt(x + y);
30217 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30219 if(!this.zoomable()){
30220 this.scale = this.startScale;
30228 onTouchEnd : function(e)
30230 this.pinching = false;
30231 this.dragable = false;
30235 process : function(file, crop)
30238 this.maskEl.mask(this.loadingText);
30241 this.xhr = new XMLHttpRequest();
30243 file.xhr = this.xhr;
30245 this.xhr.open(this.method, this.url, true);
30248 "Accept": "application/json",
30249 "Cache-Control": "no-cache",
30250 "X-Requested-With": "XMLHttpRequest"
30253 for (var headerName in headers) {
30254 var headerValue = headers[headerName];
30256 this.xhr.setRequestHeader(headerName, headerValue);
30262 this.xhr.onload = function()
30264 _this.xhrOnLoad(_this.xhr);
30267 this.xhr.onerror = function()
30269 _this.xhrOnError(_this.xhr);
30272 var formData = new FormData();
30274 formData.append('returnHTML', 'NO');
30277 formData.append('crop', crop);
30280 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30281 formData.append(this.paramName, file, file.name);
30284 if(typeof(file.filename) != 'undefined'){
30285 formData.append('filename', file.filename);
30288 if(typeof(file.mimetype) != 'undefined'){
30289 formData.append('mimetype', file.mimetype);
30292 if(this.fireEvent('arrange', this, formData) != false){
30293 this.xhr.send(formData);
30297 xhrOnLoad : function(xhr)
30300 this.maskEl.unmask();
30303 if (xhr.readyState !== 4) {
30304 this.fireEvent('exception', this, xhr);
30308 var response = Roo.decode(xhr.responseText);
30310 if(!response.success){
30311 this.fireEvent('exception', this, xhr);
30315 var response = Roo.decode(xhr.responseText);
30317 this.fireEvent('upload', this, response);
30321 xhrOnError : function()
30324 this.maskEl.unmask();
30327 Roo.log('xhr on error');
30329 var response = Roo.decode(xhr.responseText);
30335 prepare : function(file)
30338 this.maskEl.mask(this.loadingText);
30344 if(typeof(file) === 'string'){
30345 this.loadCanvas(file);
30349 if(!file || !this.urlAPI){
30354 this.cropType = file.type;
30358 if(this.fireEvent('prepare', this, this.file) != false){
30360 var reader = new FileReader();
30362 reader.onload = function (e) {
30363 if (e.target.error) {
30364 Roo.log(e.target.error);
30368 var buffer = e.target.result,
30369 dataView = new DataView(buffer),
30371 maxOffset = dataView.byteLength - 4,
30375 if (dataView.getUint16(0) === 0xffd8) {
30376 while (offset < maxOffset) {
30377 markerBytes = dataView.getUint16(offset);
30379 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30380 markerLength = dataView.getUint16(offset + 2) + 2;
30381 if (offset + markerLength > dataView.byteLength) {
30382 Roo.log('Invalid meta data: Invalid segment size.');
30386 if(markerBytes == 0xffe1){
30387 _this.parseExifData(
30394 offset += markerLength;
30404 var url = _this.urlAPI.createObjectURL(_this.file);
30406 _this.loadCanvas(url);
30411 reader.readAsArrayBuffer(this.file);
30417 parseExifData : function(dataView, offset, length)
30419 var tiffOffset = offset + 10,
30423 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30424 // No Exif data, might be XMP data instead
30428 // Check for the ASCII code for "Exif" (0x45786966):
30429 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30430 // No Exif data, might be XMP data instead
30433 if (tiffOffset + 8 > dataView.byteLength) {
30434 Roo.log('Invalid Exif data: Invalid segment size.');
30437 // Check for the two null bytes:
30438 if (dataView.getUint16(offset + 8) !== 0x0000) {
30439 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30442 // Check the byte alignment:
30443 switch (dataView.getUint16(tiffOffset)) {
30445 littleEndian = true;
30448 littleEndian = false;
30451 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30454 // Check for the TIFF tag marker (0x002A):
30455 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30456 Roo.log('Invalid Exif data: Missing TIFF marker.');
30459 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30460 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30462 this.parseExifTags(
30465 tiffOffset + dirOffset,
30470 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30475 if (dirOffset + 6 > dataView.byteLength) {
30476 Roo.log('Invalid Exif data: Invalid directory offset.');
30479 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30480 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30481 if (dirEndOffset + 4 > dataView.byteLength) {
30482 Roo.log('Invalid Exif data: Invalid directory size.');
30485 for (i = 0; i < tagsNumber; i += 1) {
30489 dirOffset + 2 + 12 * i, // tag offset
30493 // Return the offset to the next directory:
30494 return dataView.getUint32(dirEndOffset, littleEndian);
30497 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30499 var tag = dataView.getUint16(offset, littleEndian);
30501 this.exif[tag] = this.getExifValue(
30505 dataView.getUint16(offset + 2, littleEndian), // tag type
30506 dataView.getUint32(offset + 4, littleEndian), // tag length
30511 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30513 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30522 Roo.log('Invalid Exif data: Invalid tag type.');
30526 tagSize = tagType.size * length;
30527 // Determine if the value is contained in the dataOffset bytes,
30528 // or if the value at the dataOffset is a pointer to the actual data:
30529 dataOffset = tagSize > 4 ?
30530 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30531 if (dataOffset + tagSize > dataView.byteLength) {
30532 Roo.log('Invalid Exif data: Invalid data offset.');
30535 if (length === 1) {
30536 return tagType.getValue(dataView, dataOffset, littleEndian);
30539 for (i = 0; i < length; i += 1) {
30540 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30543 if (tagType.ascii) {
30545 // Concatenate the chars:
30546 for (i = 0; i < values.length; i += 1) {
30548 // Ignore the terminating NULL byte(s):
30549 if (c === '\u0000') {
30561 Roo.apply(Roo.bootstrap.UploadCropbox, {
30563 'Orientation': 0x0112
30567 1: 0, //'top-left',
30569 3: 180, //'bottom-right',
30570 // 4: 'bottom-left',
30572 6: 90, //'right-top',
30573 // 7: 'right-bottom',
30574 8: 270 //'left-bottom'
30578 // byte, 8-bit unsigned int:
30580 getValue: function (dataView, dataOffset) {
30581 return dataView.getUint8(dataOffset);
30585 // ascii, 8-bit byte:
30587 getValue: function (dataView, dataOffset) {
30588 return String.fromCharCode(dataView.getUint8(dataOffset));
30593 // short, 16 bit int:
30595 getValue: function (dataView, dataOffset, littleEndian) {
30596 return dataView.getUint16(dataOffset, littleEndian);
30600 // long, 32 bit int:
30602 getValue: function (dataView, dataOffset, littleEndian) {
30603 return dataView.getUint32(dataOffset, littleEndian);
30607 // rational = two long values, first is numerator, second is denominator:
30609 getValue: function (dataView, dataOffset, littleEndian) {
30610 return dataView.getUint32(dataOffset, littleEndian) /
30611 dataView.getUint32(dataOffset + 4, littleEndian);
30615 // slong, 32 bit signed int:
30617 getValue: function (dataView, dataOffset, littleEndian) {
30618 return dataView.getInt32(dataOffset, littleEndian);
30622 // srational, two slongs, first is numerator, second is denominator:
30624 getValue: function (dataView, dataOffset, littleEndian) {
30625 return dataView.getInt32(dataOffset, littleEndian) /
30626 dataView.getInt32(dataOffset + 4, littleEndian);
30636 cls : 'btn-group roo-upload-cropbox-rotate-left',
30637 action : 'rotate-left',
30641 cls : 'btn btn-default',
30642 html : '<i class="fa fa-undo"></i>'
30648 cls : 'btn-group roo-upload-cropbox-picture',
30649 action : 'picture',
30653 cls : 'btn btn-default',
30654 html : '<i class="fa fa-picture-o"></i>'
30660 cls : 'btn-group roo-upload-cropbox-rotate-right',
30661 action : 'rotate-right',
30665 cls : 'btn btn-default',
30666 html : '<i class="fa fa-repeat"></i>'
30674 cls : 'btn-group roo-upload-cropbox-rotate-left',
30675 action : 'rotate-left',
30679 cls : 'btn btn-default',
30680 html : '<i class="fa fa-undo"></i>'
30686 cls : 'btn-group roo-upload-cropbox-download',
30687 action : 'download',
30691 cls : 'btn btn-default',
30692 html : '<i class="fa fa-download"></i>'
30698 cls : 'btn-group roo-upload-cropbox-crop',
30703 cls : 'btn btn-default',
30704 html : '<i class="fa fa-crop"></i>'
30710 cls : 'btn-group roo-upload-cropbox-trash',
30715 cls : 'btn btn-default',
30716 html : '<i class="fa fa-trash"></i>'
30722 cls : 'btn-group roo-upload-cropbox-rotate-right',
30723 action : 'rotate-right',
30727 cls : 'btn btn-default',
30728 html : '<i class="fa fa-repeat"></i>'
30736 cls : 'btn-group roo-upload-cropbox-rotate-left',
30737 action : 'rotate-left',
30741 cls : 'btn btn-default',
30742 html : '<i class="fa fa-undo"></i>'
30748 cls : 'btn-group roo-upload-cropbox-rotate-right',
30749 action : 'rotate-right',
30753 cls : 'btn btn-default',
30754 html : '<i class="fa fa-repeat"></i>'
30767 * @class Roo.bootstrap.DocumentManager
30768 * @extends Roo.bootstrap.Component
30769 * Bootstrap DocumentManager class
30770 * @cfg {String} paramName default 'imageUpload'
30771 * @cfg {String} toolTipName default 'filename'
30772 * @cfg {String} method default POST
30773 * @cfg {String} url action url
30774 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30775 * @cfg {Boolean} multiple multiple upload default true
30776 * @cfg {Number} thumbSize default 300
30777 * @cfg {String} fieldLabel
30778 * @cfg {Number} labelWidth default 4
30779 * @cfg {String} labelAlign (left|top) default left
30780 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30781 * @cfg {Number} labellg set the width of label (1-12)
30782 * @cfg {Number} labelmd set the width of label (1-12)
30783 * @cfg {Number} labelsm set the width of label (1-12)
30784 * @cfg {Number} labelxs set the width of label (1-12)
30787 * Create a new DocumentManager
30788 * @param {Object} config The config object
30791 Roo.bootstrap.DocumentManager = function(config){
30792 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30795 this.delegates = [];
30800 * Fire when initial the DocumentManager
30801 * @param {Roo.bootstrap.DocumentManager} this
30806 * inspect selected file
30807 * @param {Roo.bootstrap.DocumentManager} this
30808 * @param {File} file
30813 * Fire when xhr load exception
30814 * @param {Roo.bootstrap.DocumentManager} this
30815 * @param {XMLHttpRequest} xhr
30817 "exception" : true,
30819 * @event afterupload
30820 * Fire when xhr load exception
30821 * @param {Roo.bootstrap.DocumentManager} this
30822 * @param {XMLHttpRequest} xhr
30824 "afterupload" : true,
30827 * prepare the form data
30828 * @param {Roo.bootstrap.DocumentManager} this
30829 * @param {Object} formData
30834 * Fire when remove the file
30835 * @param {Roo.bootstrap.DocumentManager} this
30836 * @param {Object} file
30841 * Fire after refresh the file
30842 * @param {Roo.bootstrap.DocumentManager} this
30847 * Fire after click the image
30848 * @param {Roo.bootstrap.DocumentManager} this
30849 * @param {Object} file
30854 * Fire when upload a image and editable set to true
30855 * @param {Roo.bootstrap.DocumentManager} this
30856 * @param {Object} file
30860 * @event beforeselectfile
30861 * Fire before select file
30862 * @param {Roo.bootstrap.DocumentManager} this
30864 "beforeselectfile" : true,
30867 * Fire before process file
30868 * @param {Roo.bootstrap.DocumentManager} this
30869 * @param {Object} file
30873 * @event previewrendered
30874 * Fire when preview rendered
30875 * @param {Roo.bootstrap.DocumentManager} this
30876 * @param {Object} file
30878 "previewrendered" : true,
30881 "previewResize" : true
30886 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
30895 paramName : 'imageUpload',
30896 toolTipName : 'filename',
30899 labelAlign : 'left',
30909 getAutoCreate : function()
30911 var managerWidget = {
30913 cls : 'roo-document-manager',
30917 cls : 'roo-document-manager-selector',
30922 cls : 'roo-document-manager-uploader',
30926 cls : 'roo-document-manager-upload-btn',
30927 html : '<i class="fa fa-plus"></i>'
30938 cls : 'column col-md-12',
30943 if(this.fieldLabel.length){
30948 cls : 'column col-md-12',
30949 html : this.fieldLabel
30953 cls : 'column col-md-12',
30958 if(this.labelAlign == 'left'){
30963 html : this.fieldLabel
30972 if(this.labelWidth > 12){
30973 content[0].style = "width: " + this.labelWidth + 'px';
30976 if(this.labelWidth < 13 && this.labelmd == 0){
30977 this.labelmd = this.labelWidth;
30980 if(this.labellg > 0){
30981 content[0].cls += ' col-lg-' + this.labellg;
30982 content[1].cls += ' col-lg-' + (12 - this.labellg);
30985 if(this.labelmd > 0){
30986 content[0].cls += ' col-md-' + this.labelmd;
30987 content[1].cls += ' col-md-' + (12 - this.labelmd);
30990 if(this.labelsm > 0){
30991 content[0].cls += ' col-sm-' + this.labelsm;
30992 content[1].cls += ' col-sm-' + (12 - this.labelsm);
30995 if(this.labelxs > 0){
30996 content[0].cls += ' col-xs-' + this.labelxs;
30997 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31005 cls : 'row clearfix',
31013 initEvents : function()
31015 this.managerEl = this.el.select('.roo-document-manager', true).first();
31016 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31018 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31019 this.selectorEl.hide();
31022 this.selectorEl.attr('multiple', 'multiple');
31025 this.selectorEl.on('change', this.onFileSelected, this);
31027 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31028 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31030 this.uploader.on('click', this.onUploaderClick, this);
31032 this.renderProgressDialog();
31036 window.addEventListener("resize", function() { _this.refresh(); } );
31038 this.fireEvent('initial', this);
31041 renderProgressDialog : function()
31045 this.progressDialog = new Roo.bootstrap.Modal({
31046 cls : 'roo-document-manager-progress-dialog',
31047 allow_close : false,
31058 btnclick : function() {
31059 _this.uploadCancel();
31065 this.progressDialog.render(Roo.get(document.body));
31067 this.progress = new Roo.bootstrap.Progress({
31068 cls : 'roo-document-manager-progress',
31073 this.progress.render(this.progressDialog.getChildContainer());
31075 this.progressBar = new Roo.bootstrap.ProgressBar({
31076 cls : 'roo-document-manager-progress-bar',
31079 aria_valuemax : 12,
31083 this.progressBar.render(this.progress.getChildContainer());
31086 onUploaderClick : function(e)
31088 e.preventDefault();
31090 if(this.fireEvent('beforeselectfile', this) != false){
31091 this.selectorEl.dom.click();
31096 onFileSelected : function(e)
31098 e.preventDefault();
31100 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31104 Roo.each(this.selectorEl.dom.files, function(file){
31105 if(this.fireEvent('inspect', this, file) != false){
31106 this.files.push(file);
31116 this.selectorEl.dom.value = '';
31118 if(!this.files || !this.files.length){
31122 if(this.boxes > 0 && this.files.length > this.boxes){
31123 this.files = this.files.slice(0, this.boxes);
31126 this.uploader.show();
31128 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31129 this.uploader.hide();
31138 Roo.each(this.files, function(file){
31140 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31141 var f = this.renderPreview(file);
31146 if(file.type.indexOf('image') != -1){
31147 this.delegates.push(
31149 _this.process(file);
31150 }).createDelegate(this)
31158 _this.process(file);
31159 }).createDelegate(this)
31164 this.files = files;
31166 this.delegates = this.delegates.concat(docs);
31168 if(!this.delegates.length){
31173 this.progressBar.aria_valuemax = this.delegates.length;
31180 arrange : function()
31182 if(!this.delegates.length){
31183 this.progressDialog.hide();
31188 var delegate = this.delegates.shift();
31190 this.progressDialog.show();
31192 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31194 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31199 refresh : function()
31201 this.uploader.show();
31203 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31204 this.uploader.hide();
31207 Roo.isTouch ? this.closable(false) : this.closable(true);
31209 this.fireEvent('refresh', this);
31212 onRemove : function(e, el, o)
31214 e.preventDefault();
31216 this.fireEvent('remove', this, o);
31220 remove : function(o)
31224 Roo.each(this.files, function(file){
31225 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31234 this.files = files;
31241 Roo.each(this.files, function(file){
31246 file.target.remove();
31255 onClick : function(e, el, o)
31257 e.preventDefault();
31259 this.fireEvent('click', this, o);
31263 closable : function(closable)
31265 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31267 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31279 xhrOnLoad : function(xhr)
31281 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31285 if (xhr.readyState !== 4) {
31287 this.fireEvent('exception', this, xhr);
31291 var response = Roo.decode(xhr.responseText);
31293 if(!response.success){
31295 this.fireEvent('exception', this, xhr);
31299 var file = this.renderPreview(response.data);
31301 this.files.push(file);
31305 this.fireEvent('afterupload', this, xhr);
31309 xhrOnError : function(xhr)
31311 Roo.log('xhr on error');
31313 var response = Roo.decode(xhr.responseText);
31320 process : function(file)
31322 if(this.fireEvent('process', this, file) !== false){
31323 if(this.editable && file.type.indexOf('image') != -1){
31324 this.fireEvent('edit', this, file);
31328 this.uploadStart(file, false);
31335 uploadStart : function(file, crop)
31337 this.xhr = new XMLHttpRequest();
31339 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31344 file.xhr = this.xhr;
31346 this.managerEl.createChild({
31348 cls : 'roo-document-manager-loading',
31352 tooltip : file.name,
31353 cls : 'roo-document-manager-thumb',
31354 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31360 this.xhr.open(this.method, this.url, true);
31363 "Accept": "application/json",
31364 "Cache-Control": "no-cache",
31365 "X-Requested-With": "XMLHttpRequest"
31368 for (var headerName in headers) {
31369 var headerValue = headers[headerName];
31371 this.xhr.setRequestHeader(headerName, headerValue);
31377 this.xhr.onload = function()
31379 _this.xhrOnLoad(_this.xhr);
31382 this.xhr.onerror = function()
31384 _this.xhrOnError(_this.xhr);
31387 var formData = new FormData();
31389 formData.append('returnHTML', 'NO');
31392 formData.append('crop', crop);
31395 formData.append(this.paramName, file, file.name);
31402 if(this.fireEvent('prepare', this, formData, options) != false){
31404 if(options.manually){
31408 this.xhr.send(formData);
31412 this.uploadCancel();
31415 uploadCancel : function()
31421 this.delegates = [];
31423 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31430 renderPreview : function(file)
31432 if(typeof(file.target) != 'undefined' && file.target){
31436 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31438 var previewEl = this.managerEl.createChild({
31440 cls : 'roo-document-manager-preview',
31444 tooltip : file[this.toolTipName],
31445 cls : 'roo-document-manager-thumb',
31446 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31451 html : '<i class="fa fa-times-circle"></i>'
31456 var close = previewEl.select('button.close', true).first();
31458 close.on('click', this.onRemove, this, file);
31460 file.target = previewEl;
31462 var image = previewEl.select('img', true).first();
31466 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31468 image.on('click', this.onClick, this, file);
31470 this.fireEvent('previewrendered', this, file);
31476 onPreviewLoad : function(file, image)
31478 if(typeof(file.target) == 'undefined' || !file.target){
31482 var width = image.dom.naturalWidth || image.dom.width;
31483 var height = image.dom.naturalHeight || image.dom.height;
31485 if(!this.previewResize) {
31489 if(width > height){
31490 file.target.addClass('wide');
31494 file.target.addClass('tall');
31499 uploadFromSource : function(file, crop)
31501 this.xhr = new XMLHttpRequest();
31503 this.managerEl.createChild({
31505 cls : 'roo-document-manager-loading',
31509 tooltip : file.name,
31510 cls : 'roo-document-manager-thumb',
31511 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31517 this.xhr.open(this.method, this.url, true);
31520 "Accept": "application/json",
31521 "Cache-Control": "no-cache",
31522 "X-Requested-With": "XMLHttpRequest"
31525 for (var headerName in headers) {
31526 var headerValue = headers[headerName];
31528 this.xhr.setRequestHeader(headerName, headerValue);
31534 this.xhr.onload = function()
31536 _this.xhrOnLoad(_this.xhr);
31539 this.xhr.onerror = function()
31541 _this.xhrOnError(_this.xhr);
31544 var formData = new FormData();
31546 formData.append('returnHTML', 'NO');
31548 formData.append('crop', crop);
31550 if(typeof(file.filename) != 'undefined'){
31551 formData.append('filename', file.filename);
31554 if(typeof(file.mimetype) != 'undefined'){
31555 formData.append('mimetype', file.mimetype);
31560 if(this.fireEvent('prepare', this, formData) != false){
31561 this.xhr.send(formData);
31571 * @class Roo.bootstrap.DocumentViewer
31572 * @extends Roo.bootstrap.Component
31573 * Bootstrap DocumentViewer class
31574 * @cfg {Boolean} showDownload (true|false) show download button (default true)
31575 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31578 * Create a new DocumentViewer
31579 * @param {Object} config The config object
31582 Roo.bootstrap.DocumentViewer = function(config){
31583 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31588 * Fire after initEvent
31589 * @param {Roo.bootstrap.DocumentViewer} this
31595 * @param {Roo.bootstrap.DocumentViewer} this
31600 * Fire after download button
31601 * @param {Roo.bootstrap.DocumentViewer} this
31606 * Fire after trash button
31607 * @param {Roo.bootstrap.DocumentViewer} this
31614 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
31616 showDownload : true,
31620 getAutoCreate : function()
31624 cls : 'roo-document-viewer',
31628 cls : 'roo-document-viewer-body',
31632 cls : 'roo-document-viewer-thumb',
31636 cls : 'roo-document-viewer-image'
31644 cls : 'roo-document-viewer-footer',
31647 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31651 cls : 'btn-group roo-document-viewer-download',
31655 cls : 'btn btn-default',
31656 html : '<i class="fa fa-download"></i>'
31662 cls : 'btn-group roo-document-viewer-trash',
31666 cls : 'btn btn-default',
31667 html : '<i class="fa fa-trash"></i>'
31680 initEvents : function()
31682 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31683 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31685 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31686 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31688 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31689 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31691 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31692 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31694 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31695 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31697 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31698 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31700 this.bodyEl.on('click', this.onClick, this);
31701 this.downloadBtn.on('click', this.onDownload, this);
31702 this.trashBtn.on('click', this.onTrash, this);
31704 this.downloadBtn.hide();
31705 this.trashBtn.hide();
31707 if(this.showDownload){
31708 this.downloadBtn.show();
31711 if(this.showTrash){
31712 this.trashBtn.show();
31715 if(!this.showDownload && !this.showTrash) {
31716 this.footerEl.hide();
31721 initial : function()
31723 this.fireEvent('initial', this);
31727 onClick : function(e)
31729 e.preventDefault();
31731 this.fireEvent('click', this);
31734 onDownload : function(e)
31736 e.preventDefault();
31738 this.fireEvent('download', this);
31741 onTrash : function(e)
31743 e.preventDefault();
31745 this.fireEvent('trash', this);
31757 * @class Roo.bootstrap.NavProgressBar
31758 * @extends Roo.bootstrap.Component
31759 * Bootstrap NavProgressBar class
31762 * Create a new nav progress bar
31763 * @param {Object} config The config object
31766 Roo.bootstrap.NavProgressBar = function(config){
31767 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31769 this.bullets = this.bullets || [];
31771 // Roo.bootstrap.NavProgressBar.register(this);
31775 * Fires when the active item changes
31776 * @param {Roo.bootstrap.NavProgressBar} this
31777 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31778 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
31785 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
31790 getAutoCreate : function()
31792 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31796 cls : 'roo-navigation-bar-group',
31800 cls : 'roo-navigation-top-bar'
31804 cls : 'roo-navigation-bullets-bar',
31808 cls : 'roo-navigation-bar'
31815 cls : 'roo-navigation-bottom-bar'
31825 initEvents: function()
31830 onRender : function(ct, position)
31832 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31834 if(this.bullets.length){
31835 Roo.each(this.bullets, function(b){
31844 addItem : function(cfg)
31846 var item = new Roo.bootstrap.NavProgressItem(cfg);
31848 item.parentId = this.id;
31849 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31852 var top = new Roo.bootstrap.Element({
31854 cls : 'roo-navigation-bar-text'
31857 var bottom = new Roo.bootstrap.Element({
31859 cls : 'roo-navigation-bar-text'
31862 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31863 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31865 var topText = new Roo.bootstrap.Element({
31867 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31870 var bottomText = new Roo.bootstrap.Element({
31872 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31875 topText.onRender(top.el, null);
31876 bottomText.onRender(bottom.el, null);
31879 item.bottomEl = bottom;
31882 this.barItems.push(item);
31887 getActive : function()
31889 var active = false;
31891 Roo.each(this.barItems, function(v){
31893 if (!v.isActive()) {
31905 setActiveItem : function(item)
31909 Roo.each(this.barItems, function(v){
31910 if (v.rid == item.rid) {
31914 if (v.isActive()) {
31915 v.setActive(false);
31920 item.setActive(true);
31922 this.fireEvent('changed', this, item, prev);
31925 getBarItem: function(rid)
31929 Roo.each(this.barItems, function(e) {
31930 if (e.rid != rid) {
31941 indexOfItem : function(item)
31945 Roo.each(this.barItems, function(v, i){
31947 if (v.rid != item.rid) {
31958 setActiveNext : function()
31960 var i = this.indexOfItem(this.getActive());
31962 if (i > this.barItems.length) {
31966 this.setActiveItem(this.barItems[i+1]);
31969 setActivePrev : function()
31971 var i = this.indexOfItem(this.getActive());
31977 this.setActiveItem(this.barItems[i-1]);
31980 format : function()
31982 if(!this.barItems.length){
31986 var width = 100 / this.barItems.length;
31988 Roo.each(this.barItems, function(i){
31989 i.el.setStyle('width', width + '%');
31990 i.topEl.el.setStyle('width', width + '%');
31991 i.bottomEl.el.setStyle('width', width + '%');
32000 * Nav Progress Item
32005 * @class Roo.bootstrap.NavProgressItem
32006 * @extends Roo.bootstrap.Component
32007 * Bootstrap NavProgressItem class
32008 * @cfg {String} rid the reference id
32009 * @cfg {Boolean} active (true|false) Is item active default false
32010 * @cfg {Boolean} disabled (true|false) Is item active default false
32011 * @cfg {String} html
32012 * @cfg {String} position (top|bottom) text position default bottom
32013 * @cfg {String} icon show icon instead of number
32016 * Create a new NavProgressItem
32017 * @param {Object} config The config object
32019 Roo.bootstrap.NavProgressItem = function(config){
32020 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32025 * The raw click event for the entire grid.
32026 * @param {Roo.bootstrap.NavProgressItem} this
32027 * @param {Roo.EventObject} e
32034 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32040 position : 'bottom',
32043 getAutoCreate : function()
32045 var iconCls = 'roo-navigation-bar-item-icon';
32047 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32051 cls: 'roo-navigation-bar-item',
32061 cfg.cls += ' active';
32064 cfg.cls += ' disabled';
32070 disable : function()
32072 this.setDisabled(true);
32075 enable : function()
32077 this.setDisabled(false);
32080 initEvents: function()
32082 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32084 this.iconEl.on('click', this.onClick, this);
32087 onClick : function(e)
32089 e.preventDefault();
32095 if(this.fireEvent('click', this, e) === false){
32099 this.parent().setActiveItem(this);
32102 isActive: function ()
32104 return this.active;
32107 setActive : function(state)
32109 if(this.active == state){
32113 this.active = state;
32116 this.el.addClass('active');
32120 this.el.removeClass('active');
32125 setDisabled : function(state)
32127 if(this.disabled == state){
32131 this.disabled = state;
32134 this.el.addClass('disabled');
32138 this.el.removeClass('disabled');
32141 tooltipEl : function()
32143 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32156 * @class Roo.bootstrap.FieldLabel
32157 * @extends Roo.bootstrap.Component
32158 * Bootstrap FieldLabel class
32159 * @cfg {String} html contents of the element
32160 * @cfg {String} tag tag of the element default label
32161 * @cfg {String} cls class of the element
32162 * @cfg {String} target label target
32163 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32164 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32165 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32166 * @cfg {String} iconTooltip default "This field is required"
32167 * @cfg {String} indicatorpos (left|right) default left
32170 * Create a new FieldLabel
32171 * @param {Object} config The config object
32174 Roo.bootstrap.FieldLabel = function(config){
32175 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32180 * Fires after the field has been marked as invalid.
32181 * @param {Roo.form.FieldLabel} this
32182 * @param {String} msg The validation message
32187 * Fires after the field has been validated with no errors.
32188 * @param {Roo.form.FieldLabel} this
32194 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32201 invalidClass : 'has-warning',
32202 validClass : 'has-success',
32203 iconTooltip : 'This field is required',
32204 indicatorpos : 'left',
32206 getAutoCreate : function(){
32209 if (!this.allowBlank) {
32215 cls : 'roo-bootstrap-field-label ' + this.cls,
32220 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32221 tooltip : this.iconTooltip
32230 if(this.indicatorpos == 'right'){
32233 cls : 'roo-bootstrap-field-label ' + this.cls,
32242 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32243 tooltip : this.iconTooltip
32252 initEvents: function()
32254 Roo.bootstrap.Element.superclass.initEvents.call(this);
32256 this.indicator = this.indicatorEl();
32258 if(this.indicator){
32259 this.indicator.removeClass('visible');
32260 this.indicator.addClass('invisible');
32263 Roo.bootstrap.FieldLabel.register(this);
32266 indicatorEl : function()
32268 var indicator = this.el.select('i.roo-required-indicator',true).first();
32279 * Mark this field as valid
32281 markValid : function()
32283 if(this.indicator){
32284 this.indicator.removeClass('visible');
32285 this.indicator.addClass('invisible');
32287 if (Roo.bootstrap.version == 3) {
32288 this.el.removeClass(this.invalidClass);
32289 this.el.addClass(this.validClass);
32291 this.el.removeClass('is-invalid');
32292 this.el.addClass('is-valid');
32296 this.fireEvent('valid', this);
32300 * Mark this field as invalid
32301 * @param {String} msg The validation message
32303 markInvalid : function(msg)
32305 if(this.indicator){
32306 this.indicator.removeClass('invisible');
32307 this.indicator.addClass('visible');
32309 if (Roo.bootstrap.version == 3) {
32310 this.el.removeClass(this.validClass);
32311 this.el.addClass(this.invalidClass);
32313 this.el.removeClass('is-valid');
32314 this.el.addClass('is-invalid');
32318 this.fireEvent('invalid', this, msg);
32324 Roo.apply(Roo.bootstrap.FieldLabel, {
32329 * register a FieldLabel Group
32330 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32332 register : function(label)
32334 if(this.groups.hasOwnProperty(label.target)){
32338 this.groups[label.target] = label;
32342 * fetch a FieldLabel Group based on the target
32343 * @param {string} target
32344 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32346 get: function(target) {
32347 if (typeof(this.groups[target]) == 'undefined') {
32351 return this.groups[target] ;
32360 * page DateSplitField.
32366 * @class Roo.bootstrap.DateSplitField
32367 * @extends Roo.bootstrap.Component
32368 * Bootstrap DateSplitField class
32369 * @cfg {string} fieldLabel - the label associated
32370 * @cfg {Number} labelWidth set the width of label (0-12)
32371 * @cfg {String} labelAlign (top|left)
32372 * @cfg {Boolean} dayAllowBlank (true|false) default false
32373 * @cfg {Boolean} monthAllowBlank (true|false) default false
32374 * @cfg {Boolean} yearAllowBlank (true|false) default false
32375 * @cfg {string} dayPlaceholder
32376 * @cfg {string} monthPlaceholder
32377 * @cfg {string} yearPlaceholder
32378 * @cfg {string} dayFormat default 'd'
32379 * @cfg {string} monthFormat default 'm'
32380 * @cfg {string} yearFormat default 'Y'
32381 * @cfg {Number} labellg set the width of label (1-12)
32382 * @cfg {Number} labelmd set the width of label (1-12)
32383 * @cfg {Number} labelsm set the width of label (1-12)
32384 * @cfg {Number} labelxs set the width of label (1-12)
32388 * Create a new DateSplitField
32389 * @param {Object} config The config object
32392 Roo.bootstrap.DateSplitField = function(config){
32393 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32399 * getting the data of years
32400 * @param {Roo.bootstrap.DateSplitField} this
32401 * @param {Object} years
32406 * getting the data of days
32407 * @param {Roo.bootstrap.DateSplitField} this
32408 * @param {Object} days
32413 * Fires after the field has been marked as invalid.
32414 * @param {Roo.form.Field} this
32415 * @param {String} msg The validation message
32420 * Fires after the field has been validated with no errors.
32421 * @param {Roo.form.Field} this
32427 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32430 labelAlign : 'top',
32432 dayAllowBlank : false,
32433 monthAllowBlank : false,
32434 yearAllowBlank : false,
32435 dayPlaceholder : '',
32436 monthPlaceholder : '',
32437 yearPlaceholder : '',
32441 isFormField : true,
32447 getAutoCreate : function()
32451 cls : 'row roo-date-split-field-group',
32456 cls : 'form-hidden-field roo-date-split-field-group-value',
32462 var labelCls = 'col-md-12';
32463 var contentCls = 'col-md-4';
32465 if(this.fieldLabel){
32469 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32473 html : this.fieldLabel
32478 if(this.labelAlign == 'left'){
32480 if(this.labelWidth > 12){
32481 label.style = "width: " + this.labelWidth + 'px';
32484 if(this.labelWidth < 13 && this.labelmd == 0){
32485 this.labelmd = this.labelWidth;
32488 if(this.labellg > 0){
32489 labelCls = ' col-lg-' + this.labellg;
32490 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32493 if(this.labelmd > 0){
32494 labelCls = ' col-md-' + this.labelmd;
32495 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32498 if(this.labelsm > 0){
32499 labelCls = ' col-sm-' + this.labelsm;
32500 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32503 if(this.labelxs > 0){
32504 labelCls = ' col-xs-' + this.labelxs;
32505 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32509 label.cls += ' ' + labelCls;
32511 cfg.cn.push(label);
32514 Roo.each(['day', 'month', 'year'], function(t){
32517 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32524 inputEl: function ()
32526 return this.el.select('.roo-date-split-field-group-value', true).first();
32529 onRender : function(ct, position)
32533 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32535 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32537 this.dayField = new Roo.bootstrap.ComboBox({
32538 allowBlank : this.dayAllowBlank,
32539 alwaysQuery : true,
32540 displayField : 'value',
32543 forceSelection : true,
32545 placeholder : this.dayPlaceholder,
32546 selectOnFocus : true,
32547 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32548 triggerAction : 'all',
32550 valueField : 'value',
32551 store : new Roo.data.SimpleStore({
32552 data : (function() {
32554 _this.fireEvent('days', _this, days);
32557 fields : [ 'value' ]
32560 select : function (_self, record, index)
32562 _this.setValue(_this.getValue());
32567 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32569 this.monthField = new Roo.bootstrap.MonthField({
32570 after : '<i class=\"fa fa-calendar\"></i>',
32571 allowBlank : this.monthAllowBlank,
32572 placeholder : this.monthPlaceholder,
32575 render : function (_self)
32577 this.el.select('span.input-group-addon', true).first().on('click', function(e){
32578 e.preventDefault();
32582 select : function (_self, oldvalue, newvalue)
32584 _this.setValue(_this.getValue());
32589 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32591 this.yearField = new Roo.bootstrap.ComboBox({
32592 allowBlank : this.yearAllowBlank,
32593 alwaysQuery : true,
32594 displayField : 'value',
32597 forceSelection : true,
32599 placeholder : this.yearPlaceholder,
32600 selectOnFocus : true,
32601 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32602 triggerAction : 'all',
32604 valueField : 'value',
32605 store : new Roo.data.SimpleStore({
32606 data : (function() {
32608 _this.fireEvent('years', _this, years);
32611 fields : [ 'value' ]
32614 select : function (_self, record, index)
32616 _this.setValue(_this.getValue());
32621 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32624 setValue : function(v, format)
32626 this.inputEl.dom.value = v;
32628 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32630 var d = Date.parseDate(v, f);
32637 this.setDay(d.format(this.dayFormat));
32638 this.setMonth(d.format(this.monthFormat));
32639 this.setYear(d.format(this.yearFormat));
32646 setDay : function(v)
32648 this.dayField.setValue(v);
32649 this.inputEl.dom.value = this.getValue();
32654 setMonth : function(v)
32656 this.monthField.setValue(v, true);
32657 this.inputEl.dom.value = this.getValue();
32662 setYear : function(v)
32664 this.yearField.setValue(v);
32665 this.inputEl.dom.value = this.getValue();
32670 getDay : function()
32672 return this.dayField.getValue();
32675 getMonth : function()
32677 return this.monthField.getValue();
32680 getYear : function()
32682 return this.yearField.getValue();
32685 getValue : function()
32687 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32689 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32699 this.inputEl.dom.value = '';
32704 validate : function()
32706 var d = this.dayField.validate();
32707 var m = this.monthField.validate();
32708 var y = this.yearField.validate();
32713 (!this.dayAllowBlank && !d) ||
32714 (!this.monthAllowBlank && !m) ||
32715 (!this.yearAllowBlank && !y)
32720 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32729 this.markInvalid();
32734 markValid : function()
32737 var label = this.el.select('label', true).first();
32738 var icon = this.el.select('i.fa-star', true).first();
32744 this.fireEvent('valid', this);
32748 * Mark this field as invalid
32749 * @param {String} msg The validation message
32751 markInvalid : function(msg)
32754 var label = this.el.select('label', true).first();
32755 var icon = this.el.select('i.fa-star', true).first();
32757 if(label && !icon){
32758 this.el.select('.roo-date-split-field-label', true).createChild({
32760 cls : 'text-danger fa fa-lg fa-star',
32761 tooltip : 'This field is required',
32762 style : 'margin-right:5px;'
32766 this.fireEvent('invalid', this, msg);
32769 clearInvalid : function()
32771 var label = this.el.select('label', true).first();
32772 var icon = this.el.select('i.fa-star', true).first();
32778 this.fireEvent('valid', this);
32781 getName: function()
32791 * http://masonry.desandro.com
32793 * The idea is to render all the bricks based on vertical width...
32795 * The original code extends 'outlayer' - we might need to use that....
32801 * @class Roo.bootstrap.LayoutMasonry
32802 * @extends Roo.bootstrap.Component
32803 * Bootstrap Layout Masonry class
32806 * Create a new Element
32807 * @param {Object} config The config object
32810 Roo.bootstrap.LayoutMasonry = function(config){
32812 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32816 Roo.bootstrap.LayoutMasonry.register(this);
32822 * Fire after layout the items
32823 * @param {Roo.bootstrap.LayoutMasonry} this
32824 * @param {Roo.EventObject} e
32831 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
32834 * @cfg {Boolean} isLayoutInstant = no animation?
32836 isLayoutInstant : false, // needed?
32839 * @cfg {Number} boxWidth width of the columns
32844 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
32849 * @cfg {Number} padWidth padding below box..
32854 * @cfg {Number} gutter gutter width..
32859 * @cfg {Number} maxCols maximum number of columns
32865 * @cfg {Boolean} isAutoInitial defalut true
32867 isAutoInitial : true,
32872 * @cfg {Boolean} isHorizontal defalut false
32874 isHorizontal : false,
32876 currentSize : null,
32882 bricks: null, //CompositeElement
32886 _isLayoutInited : false,
32888 // isAlternative : false, // only use for vertical layout...
32891 * @cfg {Number} alternativePadWidth padding below box..
32893 alternativePadWidth : 50,
32895 selectedBrick : [],
32897 getAutoCreate : function(){
32899 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32903 cls: 'blog-masonary-wrapper ' + this.cls,
32905 cls : 'mas-boxes masonary'
32912 getChildContainer: function( )
32914 if (this.boxesEl) {
32915 return this.boxesEl;
32918 this.boxesEl = this.el.select('.mas-boxes').first();
32920 return this.boxesEl;
32924 initEvents : function()
32928 if(this.isAutoInitial){
32929 Roo.log('hook children rendered');
32930 this.on('childrenrendered', function() {
32931 Roo.log('children rendered');
32937 initial : function()
32939 this.selectedBrick = [];
32941 this.currentSize = this.el.getBox(true);
32943 Roo.EventManager.onWindowResize(this.resize, this);
32945 if(!this.isAutoInitial){
32953 //this.layout.defer(500,this);
32957 resize : function()
32959 var cs = this.el.getBox(true);
32962 this.currentSize.width == cs.width &&
32963 this.currentSize.x == cs.x &&
32964 this.currentSize.height == cs.height &&
32965 this.currentSize.y == cs.y
32967 Roo.log("no change in with or X or Y");
32971 this.currentSize = cs;
32977 layout : function()
32979 this._resetLayout();
32981 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32983 this.layoutItems( isInstant );
32985 this._isLayoutInited = true;
32987 this.fireEvent('layout', this);
32991 _resetLayout : function()
32993 if(this.isHorizontal){
32994 this.horizontalMeasureColumns();
32998 this.verticalMeasureColumns();
33002 verticalMeasureColumns : function()
33004 this.getContainerWidth();
33006 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33007 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33011 var boxWidth = this.boxWidth + this.padWidth;
33013 if(this.containerWidth < this.boxWidth){
33014 boxWidth = this.containerWidth
33017 var containerWidth = this.containerWidth;
33019 var cols = Math.floor(containerWidth / boxWidth);
33021 this.cols = Math.max( cols, 1 );
33023 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33025 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33027 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33029 this.colWidth = boxWidth + avail - this.padWidth;
33031 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33032 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33035 horizontalMeasureColumns : function()
33037 this.getContainerWidth();
33039 var boxWidth = this.boxWidth;
33041 if(this.containerWidth < boxWidth){
33042 boxWidth = this.containerWidth;
33045 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33047 this.el.setHeight(boxWidth);
33051 getContainerWidth : function()
33053 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33056 layoutItems : function( isInstant )
33058 Roo.log(this.bricks);
33060 var items = Roo.apply([], this.bricks);
33062 if(this.isHorizontal){
33063 this._horizontalLayoutItems( items , isInstant );
33067 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33068 // this._verticalAlternativeLayoutItems( items , isInstant );
33072 this._verticalLayoutItems( items , isInstant );
33076 _verticalLayoutItems : function ( items , isInstant)
33078 if ( !items || !items.length ) {
33083 ['xs', 'xs', 'xs', 'tall'],
33084 ['xs', 'xs', 'tall'],
33085 ['xs', 'xs', 'sm'],
33086 ['xs', 'xs', 'xs'],
33092 ['sm', 'xs', 'xs'],
33096 ['tall', 'xs', 'xs', 'xs'],
33097 ['tall', 'xs', 'xs'],
33109 Roo.each(items, function(item, k){
33111 switch (item.size) {
33112 // these layouts take up a full box,
33123 boxes.push([item]);
33146 var filterPattern = function(box, length)
33154 var pattern = box.slice(0, length);
33158 Roo.each(pattern, function(i){
33159 format.push(i.size);
33162 Roo.each(standard, function(s){
33164 if(String(s) != String(format)){
33173 if(!match && length == 1){
33178 filterPattern(box, length - 1);
33182 queue.push(pattern);
33184 box = box.slice(length, box.length);
33186 filterPattern(box, 4);
33192 Roo.each(boxes, function(box, k){
33198 if(box.length == 1){
33203 filterPattern(box, 4);
33207 this._processVerticalLayoutQueue( queue, isInstant );
33211 // _verticalAlternativeLayoutItems : function( items , isInstant )
33213 // if ( !items || !items.length ) {
33217 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33221 _horizontalLayoutItems : function ( items , isInstant)
33223 if ( !items || !items.length || items.length < 3) {
33229 var eItems = items.slice(0, 3);
33231 items = items.slice(3, items.length);
33234 ['xs', 'xs', 'xs', 'wide'],
33235 ['xs', 'xs', 'wide'],
33236 ['xs', 'xs', 'sm'],
33237 ['xs', 'xs', 'xs'],
33243 ['sm', 'xs', 'xs'],
33247 ['wide', 'xs', 'xs', 'xs'],
33248 ['wide', 'xs', 'xs'],
33261 Roo.each(items, function(item, k){
33263 switch (item.size) {
33274 boxes.push([item]);
33298 var filterPattern = function(box, length)
33306 var pattern = box.slice(0, length);
33310 Roo.each(pattern, function(i){
33311 format.push(i.size);
33314 Roo.each(standard, function(s){
33316 if(String(s) != String(format)){
33325 if(!match && length == 1){
33330 filterPattern(box, length - 1);
33334 queue.push(pattern);
33336 box = box.slice(length, box.length);
33338 filterPattern(box, 4);
33344 Roo.each(boxes, function(box, k){
33350 if(box.length == 1){
33355 filterPattern(box, 4);
33362 var pos = this.el.getBox(true);
33366 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33368 var hit_end = false;
33370 Roo.each(queue, function(box){
33374 Roo.each(box, function(b){
33376 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33386 Roo.each(box, function(b){
33388 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33391 mx = Math.max(mx, b.x);
33395 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33399 Roo.each(box, function(b){
33401 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33415 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33418 /** Sets position of item in DOM
33419 * @param {Element} item
33420 * @param {Number} x - horizontal position
33421 * @param {Number} y - vertical position
33422 * @param {Boolean} isInstant - disables transitions
33424 _processVerticalLayoutQueue : function( queue, isInstant )
33426 var pos = this.el.getBox(true);
33431 for (var i = 0; i < this.cols; i++){
33435 Roo.each(queue, function(box, k){
33437 var col = k % this.cols;
33439 Roo.each(box, function(b,kk){
33441 b.el.position('absolute');
33443 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33444 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33446 if(b.size == 'md-left' || b.size == 'md-right'){
33447 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33448 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33451 b.el.setWidth(width);
33452 b.el.setHeight(height);
33454 b.el.select('iframe',true).setSize(width,height);
33458 for (var i = 0; i < this.cols; i++){
33460 if(maxY[i] < maxY[col]){
33465 col = Math.min(col, i);
33469 x = pos.x + col * (this.colWidth + this.padWidth);
33473 var positions = [];
33475 switch (box.length){
33477 positions = this.getVerticalOneBoxColPositions(x, y, box);
33480 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33483 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33486 positions = this.getVerticalFourBoxColPositions(x, y, box);
33492 Roo.each(box, function(b,kk){
33494 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33496 var sz = b.el.getSize();
33498 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33506 for (var i = 0; i < this.cols; i++){
33507 mY = Math.max(mY, maxY[i]);
33510 this.el.setHeight(mY - pos.y);
33514 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33516 // var pos = this.el.getBox(true);
33519 // var maxX = pos.right;
33521 // var maxHeight = 0;
33523 // Roo.each(items, function(item, k){
33527 // item.el.position('absolute');
33529 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33531 // item.el.setWidth(width);
33533 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33535 // item.el.setHeight(height);
33538 // item.el.setXY([x, y], isInstant ? false : true);
33540 // item.el.setXY([maxX - width, y], isInstant ? false : true);
33543 // y = y + height + this.alternativePadWidth;
33545 // maxHeight = maxHeight + height + this.alternativePadWidth;
33549 // this.el.setHeight(maxHeight);
33553 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33555 var pos = this.el.getBox(true);
33560 var maxX = pos.right;
33562 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33564 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33566 Roo.each(queue, function(box, k){
33568 Roo.each(box, function(b, kk){
33570 b.el.position('absolute');
33572 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33573 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33575 if(b.size == 'md-left' || b.size == 'md-right'){
33576 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33577 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33580 b.el.setWidth(width);
33581 b.el.setHeight(height);
33589 var positions = [];
33591 switch (box.length){
33593 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33596 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33599 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33602 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33608 Roo.each(box, function(b,kk){
33610 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33612 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33620 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33622 Roo.each(eItems, function(b,k){
33624 b.size = (k == 0) ? 'sm' : 'xs';
33625 b.x = (k == 0) ? 2 : 1;
33626 b.y = (k == 0) ? 2 : 1;
33628 b.el.position('absolute');
33630 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33632 b.el.setWidth(width);
33634 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33636 b.el.setHeight(height);
33640 var positions = [];
33643 x : maxX - this.unitWidth * 2 - this.gutter,
33648 x : maxX - this.unitWidth,
33649 y : minY + (this.unitWidth + this.gutter) * 2
33653 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33657 Roo.each(eItems, function(b,k){
33659 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33665 getVerticalOneBoxColPositions : function(x, y, box)
33669 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33671 if(box[0].size == 'md-left'){
33675 if(box[0].size == 'md-right'){
33680 x : x + (this.unitWidth + this.gutter) * rand,
33687 getVerticalTwoBoxColPositions : function(x, y, box)
33691 if(box[0].size == 'xs'){
33695 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33699 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33713 x : x + (this.unitWidth + this.gutter) * 2,
33714 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33721 getVerticalThreeBoxColPositions : function(x, y, box)
33725 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33733 x : x + (this.unitWidth + this.gutter) * 1,
33738 x : x + (this.unitWidth + this.gutter) * 2,
33746 if(box[0].size == 'xs' && box[1].size == 'xs'){
33755 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33759 x : x + (this.unitWidth + this.gutter) * 1,
33773 x : x + (this.unitWidth + this.gutter) * 2,
33778 x : x + (this.unitWidth + this.gutter) * 2,
33779 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33786 getVerticalFourBoxColPositions : function(x, y, box)
33790 if(box[0].size == 'xs'){
33799 y : y + (this.unitHeight + this.gutter) * 1
33804 y : y + (this.unitHeight + this.gutter) * 2
33808 x : x + (this.unitWidth + this.gutter) * 1,
33822 x : x + (this.unitWidth + this.gutter) * 2,
33827 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33828 y : y + (this.unitHeight + this.gutter) * 1
33832 x : x + (this.unitWidth + this.gutter) * 2,
33833 y : y + (this.unitWidth + this.gutter) * 2
33840 getHorizontalOneBoxColPositions : function(maxX, minY, box)
33844 if(box[0].size == 'md-left'){
33846 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33853 if(box[0].size == 'md-right'){
33855 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33856 y : minY + (this.unitWidth + this.gutter) * 1
33862 var rand = Math.floor(Math.random() * (4 - box[0].y));
33865 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33866 y : minY + (this.unitWidth + this.gutter) * rand
33873 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33877 if(box[0].size == 'xs'){
33880 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33885 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33886 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33894 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33899 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33900 y : minY + (this.unitWidth + this.gutter) * 2
33907 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33911 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33914 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33919 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33920 y : minY + (this.unitWidth + this.gutter) * 1
33924 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33925 y : minY + (this.unitWidth + this.gutter) * 2
33932 if(box[0].size == 'xs' && box[1].size == 'xs'){
33935 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33940 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33945 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33946 y : minY + (this.unitWidth + this.gutter) * 1
33954 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33959 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33960 y : minY + (this.unitWidth + this.gutter) * 2
33964 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33965 y : minY + (this.unitWidth + this.gutter) * 2
33972 getHorizontalFourBoxColPositions : function(maxX, minY, box)
33976 if(box[0].size == 'xs'){
33979 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33984 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33989 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),
33994 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33995 y : minY + (this.unitWidth + this.gutter) * 1
34003 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34008 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34009 y : minY + (this.unitWidth + this.gutter) * 2
34013 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34014 y : minY + (this.unitWidth + this.gutter) * 2
34018 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),
34019 y : minY + (this.unitWidth + this.gutter) * 2
34027 * remove a Masonry Brick
34028 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34030 removeBrick : function(brick_id)
34036 for (var i = 0; i<this.bricks.length; i++) {
34037 if (this.bricks[i].id == brick_id) {
34038 this.bricks.splice(i,1);
34039 this.el.dom.removeChild(Roo.get(brick_id).dom);
34046 * adds a Masonry Brick
34047 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34049 addBrick : function(cfg)
34051 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34052 //this.register(cn);
34053 cn.parentId = this.id;
34054 cn.render(this.el);
34059 * register a Masonry Brick
34060 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34063 register : function(brick)
34065 this.bricks.push(brick);
34066 brick.masonryId = this.id;
34070 * clear all the Masonry Brick
34072 clearAll : function()
34075 //this.getChildContainer().dom.innerHTML = "";
34076 this.el.dom.innerHTML = '';
34079 getSelected : function()
34081 if (!this.selectedBrick) {
34085 return this.selectedBrick;
34089 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34093 * register a Masonry Layout
34094 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34097 register : function(layout)
34099 this.groups[layout.id] = layout;
34102 * fetch a Masonry Layout based on the masonry layout ID
34103 * @param {string} the masonry layout to add
34104 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34107 get: function(layout_id) {
34108 if (typeof(this.groups[layout_id]) == 'undefined') {
34111 return this.groups[layout_id] ;
34123 * http://masonry.desandro.com
34125 * The idea is to render all the bricks based on vertical width...
34127 * The original code extends 'outlayer' - we might need to use that....
34133 * @class Roo.bootstrap.LayoutMasonryAuto
34134 * @extends Roo.bootstrap.Component
34135 * Bootstrap Layout Masonry class
34138 * Create a new Element
34139 * @param {Object} config The config object
34142 Roo.bootstrap.LayoutMasonryAuto = function(config){
34143 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34146 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34149 * @cfg {Boolean} isFitWidth - resize the width..
34151 isFitWidth : false, // options..
34153 * @cfg {Boolean} isOriginLeft = left align?
34155 isOriginLeft : true,
34157 * @cfg {Boolean} isOriginTop = top align?
34159 isOriginTop : false,
34161 * @cfg {Boolean} isLayoutInstant = no animation?
34163 isLayoutInstant : false, // needed?
34165 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34167 isResizingContainer : true,
34169 * @cfg {Number} columnWidth width of the columns
34175 * @cfg {Number} maxCols maximum number of columns
34180 * @cfg {Number} padHeight padding below box..
34186 * @cfg {Boolean} isAutoInitial defalut true
34189 isAutoInitial : true,
34195 initialColumnWidth : 0,
34196 currentSize : null,
34198 colYs : null, // array.
34205 bricks: null, //CompositeElement
34206 cols : 0, // array?
34207 // element : null, // wrapped now this.el
34208 _isLayoutInited : null,
34211 getAutoCreate : function(){
34215 cls: 'blog-masonary-wrapper ' + this.cls,
34217 cls : 'mas-boxes masonary'
34224 getChildContainer: function( )
34226 if (this.boxesEl) {
34227 return this.boxesEl;
34230 this.boxesEl = this.el.select('.mas-boxes').first();
34232 return this.boxesEl;
34236 initEvents : function()
34240 if(this.isAutoInitial){
34241 Roo.log('hook children rendered');
34242 this.on('childrenrendered', function() {
34243 Roo.log('children rendered');
34250 initial : function()
34252 this.reloadItems();
34254 this.currentSize = this.el.getBox(true);
34256 /// was window resize... - let's see if this works..
34257 Roo.EventManager.onWindowResize(this.resize, this);
34259 if(!this.isAutoInitial){
34264 this.layout.defer(500,this);
34267 reloadItems: function()
34269 this.bricks = this.el.select('.masonry-brick', true);
34271 this.bricks.each(function(b) {
34272 //Roo.log(b.getSize());
34273 if (!b.attr('originalwidth')) {
34274 b.attr('originalwidth', b.getSize().width);
34279 Roo.log(this.bricks.elements.length);
34282 resize : function()
34285 var cs = this.el.getBox(true);
34287 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34288 Roo.log("no change in with or X");
34291 this.currentSize = cs;
34295 layout : function()
34298 this._resetLayout();
34299 //this._manageStamps();
34301 // don't animate first layout
34302 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34303 this.layoutItems( isInstant );
34305 // flag for initalized
34306 this._isLayoutInited = true;
34309 layoutItems : function( isInstant )
34311 //var items = this._getItemsForLayout( this.items );
34312 // original code supports filtering layout items.. we just ignore it..
34314 this._layoutItems( this.bricks , isInstant );
34316 this._postLayout();
34318 _layoutItems : function ( items , isInstant)
34320 //this.fireEvent( 'layout', this, items );
34323 if ( !items || !items.elements.length ) {
34324 // no items, emit event with empty array
34329 items.each(function(item) {
34330 Roo.log("layout item");
34332 // get x/y object from method
34333 var position = this._getItemLayoutPosition( item );
34335 position.item = item;
34336 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34337 queue.push( position );
34340 this._processLayoutQueue( queue );
34342 /** Sets position of item in DOM
34343 * @param {Element} item
34344 * @param {Number} x - horizontal position
34345 * @param {Number} y - vertical position
34346 * @param {Boolean} isInstant - disables transitions
34348 _processLayoutQueue : function( queue )
34350 for ( var i=0, len = queue.length; i < len; i++ ) {
34351 var obj = queue[i];
34352 obj.item.position('absolute');
34353 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34359 * Any logic you want to do after each layout,
34360 * i.e. size the container
34362 _postLayout : function()
34364 this.resizeContainer();
34367 resizeContainer : function()
34369 if ( !this.isResizingContainer ) {
34372 var size = this._getContainerSize();
34374 this.el.setSize(size.width,size.height);
34375 this.boxesEl.setSize(size.width,size.height);
34381 _resetLayout : function()
34383 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34384 this.colWidth = this.el.getWidth();
34385 //this.gutter = this.el.getWidth();
34387 this.measureColumns();
34393 this.colYs.push( 0 );
34399 measureColumns : function()
34401 this.getContainerWidth();
34402 // if columnWidth is 0, default to outerWidth of first item
34403 if ( !this.columnWidth ) {
34404 var firstItem = this.bricks.first();
34405 Roo.log(firstItem);
34406 this.columnWidth = this.containerWidth;
34407 if (firstItem && firstItem.attr('originalwidth') ) {
34408 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34410 // columnWidth fall back to item of first element
34411 Roo.log("set column width?");
34412 this.initialColumnWidth = this.columnWidth ;
34414 // if first elem has no width, default to size of container
34419 if (this.initialColumnWidth) {
34420 this.columnWidth = this.initialColumnWidth;
34425 // column width is fixed at the top - however if container width get's smaller we should
34428 // this bit calcs how man columns..
34430 var columnWidth = this.columnWidth += this.gutter;
34432 // calculate columns
34433 var containerWidth = this.containerWidth + this.gutter;
34435 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34436 // fix rounding errors, typically with gutters
34437 var excess = columnWidth - containerWidth % columnWidth;
34440 // if overshoot is less than a pixel, round up, otherwise floor it
34441 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34442 cols = Math[ mathMethod ]( cols );
34443 this.cols = Math.max( cols, 1 );
34444 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34446 // padding positioning..
34447 var totalColWidth = this.cols * this.columnWidth;
34448 var padavail = this.containerWidth - totalColWidth;
34449 // so for 2 columns - we need 3 'pads'
34451 var padNeeded = (1+this.cols) * this.padWidth;
34453 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34455 this.columnWidth += padExtra
34456 //this.padWidth = Math.floor(padavail / ( this.cols));
34458 // adjust colum width so that padding is fixed??
34460 // we have 3 columns ... total = width * 3
34461 // we have X left over... that should be used by
34463 //if (this.expandC) {
34471 getContainerWidth : function()
34473 /* // container is parent if fit width
34474 var container = this.isFitWidth ? this.element.parentNode : this.element;
34475 // check that this.size and size are there
34476 // IE8 triggers resize on body size change, so they might not be
34478 var size = getSize( container ); //FIXME
34479 this.containerWidth = size && size.innerWidth; //FIXME
34482 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34486 _getItemLayoutPosition : function( item ) // what is item?
34488 // we resize the item to our columnWidth..
34490 item.setWidth(this.columnWidth);
34491 item.autoBoxAdjust = false;
34493 var sz = item.getSize();
34495 // how many columns does this brick span
34496 var remainder = this.containerWidth % this.columnWidth;
34498 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34499 // round if off by 1 pixel, otherwise use ceil
34500 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34501 colSpan = Math.min( colSpan, this.cols );
34503 // normally this should be '1' as we dont' currently allow multi width columns..
34505 var colGroup = this._getColGroup( colSpan );
34506 // get the minimum Y value from the columns
34507 var minimumY = Math.min.apply( Math, colGroup );
34508 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34510 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
34512 // position the brick
34514 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34515 y: this.currentSize.y + minimumY + this.padHeight
34519 // apply setHeight to necessary columns
34520 var setHeight = minimumY + sz.height + this.padHeight;
34521 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34523 var setSpan = this.cols + 1 - colGroup.length;
34524 for ( var i = 0; i < setSpan; i++ ) {
34525 this.colYs[ shortColIndex + i ] = setHeight ;
34532 * @param {Number} colSpan - number of columns the element spans
34533 * @returns {Array} colGroup
34535 _getColGroup : function( colSpan )
34537 if ( colSpan < 2 ) {
34538 // if brick spans only one column, use all the column Ys
34543 // how many different places could this brick fit horizontally
34544 var groupCount = this.cols + 1 - colSpan;
34545 // for each group potential horizontal position
34546 for ( var i = 0; i < groupCount; i++ ) {
34547 // make an array of colY values for that one group
34548 var groupColYs = this.colYs.slice( i, i + colSpan );
34549 // and get the max value of the array
34550 colGroup[i] = Math.max.apply( Math, groupColYs );
34555 _manageStamp : function( stamp )
34557 var stampSize = stamp.getSize();
34558 var offset = stamp.getBox();
34559 // get the columns that this stamp affects
34560 var firstX = this.isOriginLeft ? offset.x : offset.right;
34561 var lastX = firstX + stampSize.width;
34562 var firstCol = Math.floor( firstX / this.columnWidth );
34563 firstCol = Math.max( 0, firstCol );
34565 var lastCol = Math.floor( lastX / this.columnWidth );
34566 // lastCol should not go over if multiple of columnWidth #425
34567 lastCol -= lastX % this.columnWidth ? 0 : 1;
34568 lastCol = Math.min( this.cols - 1, lastCol );
34570 // set colYs to bottom of the stamp
34571 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34574 for ( var i = firstCol; i <= lastCol; i++ ) {
34575 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34580 _getContainerSize : function()
34582 this.maxY = Math.max.apply( Math, this.colYs );
34587 if ( this.isFitWidth ) {
34588 size.width = this._getContainerFitWidth();
34594 _getContainerFitWidth : function()
34596 var unusedCols = 0;
34597 // count unused columns
34600 if ( this.colYs[i] !== 0 ) {
34605 // fit container to columns that have been used
34606 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34609 needsResizeLayout : function()
34611 var previousWidth = this.containerWidth;
34612 this.getContainerWidth();
34613 return previousWidth !== this.containerWidth;
34628 * @class Roo.bootstrap.MasonryBrick
34629 * @extends Roo.bootstrap.Component
34630 * Bootstrap MasonryBrick class
34633 * Create a new MasonryBrick
34634 * @param {Object} config The config object
34637 Roo.bootstrap.MasonryBrick = function(config){
34639 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34641 Roo.bootstrap.MasonryBrick.register(this);
34647 * When a MasonryBrick is clcik
34648 * @param {Roo.bootstrap.MasonryBrick} this
34649 * @param {Roo.EventObject} e
34655 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34658 * @cfg {String} title
34662 * @cfg {String} html
34666 * @cfg {String} bgimage
34670 * @cfg {String} videourl
34674 * @cfg {String} cls
34678 * @cfg {String} href
34682 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34687 * @cfg {String} placetitle (center|bottom)
34692 * @cfg {Boolean} isFitContainer defalut true
34694 isFitContainer : true,
34697 * @cfg {Boolean} preventDefault defalut false
34699 preventDefault : false,
34702 * @cfg {Boolean} inverse defalut false
34704 maskInverse : false,
34706 getAutoCreate : function()
34708 if(!this.isFitContainer){
34709 return this.getSplitAutoCreate();
34712 var cls = 'masonry-brick masonry-brick-full';
34714 if(this.href.length){
34715 cls += ' masonry-brick-link';
34718 if(this.bgimage.length){
34719 cls += ' masonry-brick-image';
34722 if(this.maskInverse){
34723 cls += ' mask-inverse';
34726 if(!this.html.length && !this.maskInverse && !this.videourl.length){
34727 cls += ' enable-mask';
34731 cls += ' masonry-' + this.size + '-brick';
34734 if(this.placetitle.length){
34736 switch (this.placetitle) {
34738 cls += ' masonry-center-title';
34741 cls += ' masonry-bottom-title';
34748 if(!this.html.length && !this.bgimage.length){
34749 cls += ' masonry-center-title';
34752 if(!this.html.length && this.bgimage.length){
34753 cls += ' masonry-bottom-title';
34758 cls += ' ' + this.cls;
34762 tag: (this.href.length) ? 'a' : 'div',
34767 cls: 'masonry-brick-mask'
34771 cls: 'masonry-brick-paragraph',
34777 if(this.href.length){
34778 cfg.href = this.href;
34781 var cn = cfg.cn[1].cn;
34783 if(this.title.length){
34786 cls: 'masonry-brick-title',
34791 if(this.html.length){
34794 cls: 'masonry-brick-text',
34799 if (!this.title.length && !this.html.length) {
34800 cfg.cn[1].cls += ' hide';
34803 if(this.bgimage.length){
34806 cls: 'masonry-brick-image-view',
34811 if(this.videourl.length){
34812 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34813 // youtube support only?
34816 cls: 'masonry-brick-image-view',
34819 allowfullscreen : true
34827 getSplitAutoCreate : function()
34829 var cls = 'masonry-brick masonry-brick-split';
34831 if(this.href.length){
34832 cls += ' masonry-brick-link';
34835 if(this.bgimage.length){
34836 cls += ' masonry-brick-image';
34840 cls += ' masonry-' + this.size + '-brick';
34843 switch (this.placetitle) {
34845 cls += ' masonry-center-title';
34848 cls += ' masonry-bottom-title';
34851 if(!this.bgimage.length){
34852 cls += ' masonry-center-title';
34855 if(this.bgimage.length){
34856 cls += ' masonry-bottom-title';
34862 cls += ' ' + this.cls;
34866 tag: (this.href.length) ? 'a' : 'div',
34871 cls: 'masonry-brick-split-head',
34875 cls: 'masonry-brick-paragraph',
34882 cls: 'masonry-brick-split-body',
34888 if(this.href.length){
34889 cfg.href = this.href;
34892 if(this.title.length){
34893 cfg.cn[0].cn[0].cn.push({
34895 cls: 'masonry-brick-title',
34900 if(this.html.length){
34901 cfg.cn[1].cn.push({
34903 cls: 'masonry-brick-text',
34908 if(this.bgimage.length){
34909 cfg.cn[0].cn.push({
34911 cls: 'masonry-brick-image-view',
34916 if(this.videourl.length){
34917 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34918 // youtube support only?
34919 cfg.cn[0].cn.cn.push({
34921 cls: 'masonry-brick-image-view',
34924 allowfullscreen : true
34931 initEvents: function()
34933 switch (this.size) {
34966 this.el.on('touchstart', this.onTouchStart, this);
34967 this.el.on('touchmove', this.onTouchMove, this);
34968 this.el.on('touchend', this.onTouchEnd, this);
34969 this.el.on('contextmenu', this.onContextMenu, this);
34971 this.el.on('mouseenter' ,this.enter, this);
34972 this.el.on('mouseleave', this.leave, this);
34973 this.el.on('click', this.onClick, this);
34976 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34977 this.parent().bricks.push(this);
34982 onClick: function(e, el)
34984 var time = this.endTimer - this.startTimer;
34985 // Roo.log(e.preventDefault());
34988 e.preventDefault();
34993 if(!this.preventDefault){
34997 e.preventDefault();
34999 if (this.activeClass != '') {
35000 this.selectBrick();
35003 this.fireEvent('click', this, e);
35006 enter: function(e, el)
35008 e.preventDefault();
35010 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35014 if(this.bgimage.length && this.html.length){
35015 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35019 leave: function(e, el)
35021 e.preventDefault();
35023 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35027 if(this.bgimage.length && this.html.length){
35028 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35032 onTouchStart: function(e, el)
35034 // e.preventDefault();
35036 this.touchmoved = false;
35038 if(!this.isFitContainer){
35042 if(!this.bgimage.length || !this.html.length){
35046 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35048 this.timer = new Date().getTime();
35052 onTouchMove: function(e, el)
35054 this.touchmoved = true;
35057 onContextMenu : function(e,el)
35059 e.preventDefault();
35060 e.stopPropagation();
35064 onTouchEnd: function(e, el)
35066 // e.preventDefault();
35068 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35075 if(!this.bgimage.length || !this.html.length){
35077 if(this.href.length){
35078 window.location.href = this.href;
35084 if(!this.isFitContainer){
35088 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35090 window.location.href = this.href;
35093 //selection on single brick only
35094 selectBrick : function() {
35096 if (!this.parentId) {
35100 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35101 var index = m.selectedBrick.indexOf(this.id);
35104 m.selectedBrick.splice(index,1);
35105 this.el.removeClass(this.activeClass);
35109 for(var i = 0; i < m.selectedBrick.length; i++) {
35110 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35111 b.el.removeClass(b.activeClass);
35114 m.selectedBrick = [];
35116 m.selectedBrick.push(this.id);
35117 this.el.addClass(this.activeClass);
35121 isSelected : function(){
35122 return this.el.hasClass(this.activeClass);
35127 Roo.apply(Roo.bootstrap.MasonryBrick, {
35130 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35132 * register a Masonry Brick
35133 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35136 register : function(brick)
35138 //this.groups[brick.id] = brick;
35139 this.groups.add(brick.id, brick);
35142 * fetch a masonry brick based on the masonry brick ID
35143 * @param {string} the masonry brick to add
35144 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35147 get: function(brick_id)
35149 // if (typeof(this.groups[brick_id]) == 'undefined') {
35152 // return this.groups[brick_id] ;
35154 if(this.groups.key(brick_id)) {
35155 return this.groups.key(brick_id);
35173 * @class Roo.bootstrap.Brick
35174 * @extends Roo.bootstrap.Component
35175 * Bootstrap Brick class
35178 * Create a new Brick
35179 * @param {Object} config The config object
35182 Roo.bootstrap.Brick = function(config){
35183 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35189 * When a Brick is click
35190 * @param {Roo.bootstrap.Brick} this
35191 * @param {Roo.EventObject} e
35197 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35200 * @cfg {String} title
35204 * @cfg {String} html
35208 * @cfg {String} bgimage
35212 * @cfg {String} cls
35216 * @cfg {String} href
35220 * @cfg {String} video
35224 * @cfg {Boolean} square
35228 getAutoCreate : function()
35230 var cls = 'roo-brick';
35232 if(this.href.length){
35233 cls += ' roo-brick-link';
35236 if(this.bgimage.length){
35237 cls += ' roo-brick-image';
35240 if(!this.html.length && !this.bgimage.length){
35241 cls += ' roo-brick-center-title';
35244 if(!this.html.length && this.bgimage.length){
35245 cls += ' roo-brick-bottom-title';
35249 cls += ' ' + this.cls;
35253 tag: (this.href.length) ? 'a' : 'div',
35258 cls: 'roo-brick-paragraph',
35264 if(this.href.length){
35265 cfg.href = this.href;
35268 var cn = cfg.cn[0].cn;
35270 if(this.title.length){
35273 cls: 'roo-brick-title',
35278 if(this.html.length){
35281 cls: 'roo-brick-text',
35288 if(this.bgimage.length){
35291 cls: 'roo-brick-image-view',
35299 initEvents: function()
35301 if(this.title.length || this.html.length){
35302 this.el.on('mouseenter' ,this.enter, this);
35303 this.el.on('mouseleave', this.leave, this);
35306 Roo.EventManager.onWindowResize(this.resize, this);
35308 if(this.bgimage.length){
35309 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35310 this.imageEl.on('load', this.onImageLoad, this);
35317 onImageLoad : function()
35322 resize : function()
35324 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35326 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35328 if(this.bgimage.length){
35329 var image = this.el.select('.roo-brick-image-view', true).first();
35331 image.setWidth(paragraph.getWidth());
35334 image.setHeight(paragraph.getWidth());
35337 this.el.setHeight(image.getHeight());
35338 paragraph.setHeight(image.getHeight());
35344 enter: function(e, el)
35346 e.preventDefault();
35348 if(this.bgimage.length){
35349 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35350 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35354 leave: function(e, el)
35356 e.preventDefault();
35358 if(this.bgimage.length){
35359 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35360 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35375 * @class Roo.bootstrap.NumberField
35376 * @extends Roo.bootstrap.Input
35377 * Bootstrap NumberField class
35383 * Create a new NumberField
35384 * @param {Object} config The config object
35387 Roo.bootstrap.NumberField = function(config){
35388 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35391 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35394 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35396 allowDecimals : true,
35398 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35400 decimalSeparator : ".",
35402 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35404 decimalPrecision : 2,
35406 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35408 allowNegative : true,
35411 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35415 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35417 minValue : Number.NEGATIVE_INFINITY,
35419 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35421 maxValue : Number.MAX_VALUE,
35423 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35425 minText : "The minimum value for this field is {0}",
35427 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35429 maxText : "The maximum value for this field is {0}",
35431 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35432 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35434 nanText : "{0} is not a valid number",
35436 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35438 thousandsDelimiter : false,
35440 * @cfg {String} valueAlign alignment of value
35442 valueAlign : "left",
35444 getAutoCreate : function()
35446 var hiddenInput = {
35450 cls: 'hidden-number-input'
35454 hiddenInput.name = this.name;
35459 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35461 this.name = hiddenInput.name;
35463 if(cfg.cn.length > 0) {
35464 cfg.cn.push(hiddenInput);
35471 initEvents : function()
35473 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35475 var allowed = "0123456789";
35477 if(this.allowDecimals){
35478 allowed += this.decimalSeparator;
35481 if(this.allowNegative){
35485 if(this.thousandsDelimiter) {
35489 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35491 var keyPress = function(e){
35493 var k = e.getKey();
35495 var c = e.getCharCode();
35498 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35499 allowed.indexOf(String.fromCharCode(c)) === -1
35505 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35509 if(allowed.indexOf(String.fromCharCode(c)) === -1){
35514 this.el.on("keypress", keyPress, this);
35517 validateValue : function(value)
35520 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35524 var num = this.parseValue(value);
35527 this.markInvalid(String.format(this.nanText, value));
35531 if(num < this.minValue){
35532 this.markInvalid(String.format(this.minText, this.minValue));
35536 if(num > this.maxValue){
35537 this.markInvalid(String.format(this.maxText, this.maxValue));
35544 getValue : function()
35546 var v = this.hiddenEl().getValue();
35548 return this.fixPrecision(this.parseValue(v));
35551 parseValue : function(value)
35553 if(this.thousandsDelimiter) {
35555 r = new RegExp(",", "g");
35556 value = value.replace(r, "");
35559 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35560 return isNaN(value) ? '' : value;
35563 fixPrecision : function(value)
35565 if(this.thousandsDelimiter) {
35567 r = new RegExp(",", "g");
35568 value = value.replace(r, "");
35571 var nan = isNaN(value);
35573 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35574 return nan ? '' : value;
35576 return parseFloat(value).toFixed(this.decimalPrecision);
35579 setValue : function(v)
35581 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35587 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35589 this.inputEl().dom.value = (v == '') ? '' :
35590 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35592 if(!this.allowZero && v === '0') {
35593 this.hiddenEl().dom.value = '';
35594 this.inputEl().dom.value = '';
35601 decimalPrecisionFcn : function(v)
35603 return Math.floor(v);
35606 beforeBlur : function()
35608 var v = this.parseValue(this.getRawValue());
35610 if(v || v === 0 || v === ''){
35615 hiddenEl : function()
35617 return this.el.select('input.hidden-number-input',true).first();
35629 * @class Roo.bootstrap.DocumentSlider
35630 * @extends Roo.bootstrap.Component
35631 * Bootstrap DocumentSlider class
35634 * Create a new DocumentViewer
35635 * @param {Object} config The config object
35638 Roo.bootstrap.DocumentSlider = function(config){
35639 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35646 * Fire after initEvent
35647 * @param {Roo.bootstrap.DocumentSlider} this
35652 * Fire after update
35653 * @param {Roo.bootstrap.DocumentSlider} this
35659 * @param {Roo.bootstrap.DocumentSlider} this
35665 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35671 getAutoCreate : function()
35675 cls : 'roo-document-slider',
35679 cls : 'roo-document-slider-header',
35683 cls : 'roo-document-slider-header-title'
35689 cls : 'roo-document-slider-body',
35693 cls : 'roo-document-slider-prev',
35697 cls : 'fa fa-chevron-left'
35703 cls : 'roo-document-slider-thumb',
35707 cls : 'roo-document-slider-image'
35713 cls : 'roo-document-slider-next',
35717 cls : 'fa fa-chevron-right'
35729 initEvents : function()
35731 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35732 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35734 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35735 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35737 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35738 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35740 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35741 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35743 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35744 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35746 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35747 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35749 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35750 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35752 this.thumbEl.on('click', this.onClick, this);
35754 this.prevIndicator.on('click', this.prev, this);
35756 this.nextIndicator.on('click', this.next, this);
35760 initial : function()
35762 if(this.files.length){
35763 this.indicator = 1;
35767 this.fireEvent('initial', this);
35770 update : function()
35772 this.imageEl.attr('src', this.files[this.indicator - 1]);
35774 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35776 this.prevIndicator.show();
35778 if(this.indicator == 1){
35779 this.prevIndicator.hide();
35782 this.nextIndicator.show();
35784 if(this.indicator == this.files.length){
35785 this.nextIndicator.hide();
35788 this.thumbEl.scrollTo('top');
35790 this.fireEvent('update', this);
35793 onClick : function(e)
35795 e.preventDefault();
35797 this.fireEvent('click', this);
35802 e.preventDefault();
35804 this.indicator = Math.max(1, this.indicator - 1);
35811 e.preventDefault();
35813 this.indicator = Math.min(this.files.length, this.indicator + 1);
35827 * @class Roo.bootstrap.RadioSet
35828 * @extends Roo.bootstrap.Input
35829 * Bootstrap RadioSet class
35830 * @cfg {String} indicatorpos (left|right) default left
35831 * @cfg {Boolean} inline (true|false) inline the element (default true)
35832 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35834 * Create a new RadioSet
35835 * @param {Object} config The config object
35838 Roo.bootstrap.RadioSet = function(config){
35840 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35844 Roo.bootstrap.RadioSet.register(this);
35849 * Fires when the element is checked or unchecked.
35850 * @param {Roo.bootstrap.RadioSet} this This radio
35851 * @param {Roo.bootstrap.Radio} item The checked item
35856 * Fires when the element is click.
35857 * @param {Roo.bootstrap.RadioSet} this This radio set
35858 * @param {Roo.bootstrap.Radio} item The checked item
35859 * @param {Roo.EventObject} e The event object
35866 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
35874 indicatorpos : 'left',
35876 getAutoCreate : function()
35880 cls : 'roo-radio-set-label',
35884 html : this.fieldLabel
35888 if (Roo.bootstrap.version == 3) {
35891 if(this.indicatorpos == 'left'){
35894 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35895 tooltip : 'This field is required'
35900 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35901 tooltip : 'This field is required'
35907 cls : 'roo-radio-set-items'
35910 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35912 if (align === 'left' && this.fieldLabel.length) {
35915 cls : "roo-radio-set-right",
35921 if(this.labelWidth > 12){
35922 label.style = "width: " + this.labelWidth + 'px';
35925 if(this.labelWidth < 13 && this.labelmd == 0){
35926 this.labelmd = this.labelWidth;
35929 if(this.labellg > 0){
35930 label.cls += ' col-lg-' + this.labellg;
35931 items.cls += ' col-lg-' + (12 - this.labellg);
35934 if(this.labelmd > 0){
35935 label.cls += ' col-md-' + this.labelmd;
35936 items.cls += ' col-md-' + (12 - this.labelmd);
35939 if(this.labelsm > 0){
35940 label.cls += ' col-sm-' + this.labelsm;
35941 items.cls += ' col-sm-' + (12 - this.labelsm);
35944 if(this.labelxs > 0){
35945 label.cls += ' col-xs-' + this.labelxs;
35946 items.cls += ' col-xs-' + (12 - this.labelxs);
35952 cls : 'roo-radio-set',
35956 cls : 'roo-radio-set-input',
35959 value : this.value ? this.value : ''
35966 if(this.weight.length){
35967 cfg.cls += ' roo-radio-' + this.weight;
35971 cfg.cls += ' roo-radio-set-inline';
35975 ['xs','sm','md','lg'].map(function(size){
35976 if (settings[size]) {
35977 cfg.cls += ' col-' + size + '-' + settings[size];
35985 initEvents : function()
35987 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35988 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35990 if(!this.fieldLabel.length){
35991 this.labelEl.hide();
35994 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35995 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35997 this.indicator = this.indicatorEl();
35999 if(this.indicator){
36000 this.indicator.addClass('invisible');
36003 this.originalValue = this.getValue();
36007 inputEl: function ()
36009 return this.el.select('.roo-radio-set-input', true).first();
36012 getChildContainer : function()
36014 return this.itemsEl;
36017 register : function(item)
36019 this.radioes.push(item);
36023 validate : function()
36025 if(this.getVisibilityEl().hasClass('hidden')){
36031 Roo.each(this.radioes, function(i){
36040 if(this.allowBlank) {
36044 if(this.disabled || valid){
36049 this.markInvalid();
36054 markValid : function()
36056 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36057 this.indicatorEl().removeClass('visible');
36058 this.indicatorEl().addClass('invisible');
36062 if (Roo.bootstrap.version == 3) {
36063 this.el.removeClass([this.invalidClass, this.validClass]);
36064 this.el.addClass(this.validClass);
36066 this.el.removeClass(['is-invalid','is-valid']);
36067 this.el.addClass(['is-valid']);
36069 this.fireEvent('valid', this);
36072 markInvalid : function(msg)
36074 if(this.allowBlank || this.disabled){
36078 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36079 this.indicatorEl().removeClass('invisible');
36080 this.indicatorEl().addClass('visible');
36082 if (Roo.bootstrap.version == 3) {
36083 this.el.removeClass([this.invalidClass, this.validClass]);
36084 this.el.addClass(this.invalidClass);
36086 this.el.removeClass(['is-invalid','is-valid']);
36087 this.el.addClass(['is-invalid']);
36090 this.fireEvent('invalid', this, msg);
36094 setValue : function(v, suppressEvent)
36096 if(this.value === v){
36103 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36106 Roo.each(this.radioes, function(i){
36108 i.el.removeClass('checked');
36111 Roo.each(this.radioes, function(i){
36113 if(i.value === v || i.value.toString() === v.toString()){
36115 i.el.addClass('checked');
36117 if(suppressEvent !== true){
36118 this.fireEvent('check', this, i);
36129 clearInvalid : function(){
36131 if(!this.el || this.preventMark){
36135 this.el.removeClass([this.invalidClass]);
36137 this.fireEvent('valid', this);
36142 Roo.apply(Roo.bootstrap.RadioSet, {
36146 register : function(set)
36148 this.groups[set.name] = set;
36151 get: function(name)
36153 if (typeof(this.groups[name]) == 'undefined') {
36157 return this.groups[name] ;
36163 * Ext JS Library 1.1.1
36164 * Copyright(c) 2006-2007, Ext JS, LLC.
36166 * Originally Released Under LGPL - original licence link has changed is not relivant.
36169 * <script type="text/javascript">
36174 * @class Roo.bootstrap.SplitBar
36175 * @extends Roo.util.Observable
36176 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36180 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36181 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36182 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36183 split.minSize = 100;
36184 split.maxSize = 600;
36185 split.animate = true;
36186 split.on('moved', splitterMoved);
36189 * Create a new SplitBar
36190 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36191 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36192 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36193 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36194 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36195 position of the SplitBar).
36197 Roo.bootstrap.SplitBar = function(cfg){
36202 // dragElement : elm
36203 // resizingElement: el,
36205 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36206 // placement : Roo.bootstrap.SplitBar.LEFT ,
36207 // existingProxy ???
36210 this.el = Roo.get(cfg.dragElement, true);
36211 this.el.dom.unselectable = "on";
36213 this.resizingEl = Roo.get(cfg.resizingElement, true);
36217 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36218 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36221 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36224 * The minimum size of the resizing element. (Defaults to 0)
36230 * The maximum size of the resizing element. (Defaults to 2000)
36233 this.maxSize = 2000;
36236 * Whether to animate the transition to the new size
36239 this.animate = false;
36242 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36245 this.useShim = false;
36250 if(!cfg.existingProxy){
36252 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36254 this.proxy = Roo.get(cfg.existingProxy).dom;
36257 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36260 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36263 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36266 this.dragSpecs = {};
36269 * @private The adapter to use to positon and resize elements
36271 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36272 this.adapter.init(this);
36274 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36276 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36277 this.el.addClass("roo-splitbar-h");
36280 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36281 this.el.addClass("roo-splitbar-v");
36287 * Fires when the splitter is moved (alias for {@link #event-moved})
36288 * @param {Roo.bootstrap.SplitBar} this
36289 * @param {Number} newSize the new width or height
36294 * Fires when the splitter is moved
36295 * @param {Roo.bootstrap.SplitBar} this
36296 * @param {Number} newSize the new width or height
36300 * @event beforeresize
36301 * Fires before the splitter is dragged
36302 * @param {Roo.bootstrap.SplitBar} this
36304 "beforeresize" : true,
36306 "beforeapply" : true
36309 Roo.util.Observable.call(this);
36312 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36313 onStartProxyDrag : function(x, y){
36314 this.fireEvent("beforeresize", this);
36316 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36318 o.enableDisplayMode("block");
36319 // all splitbars share the same overlay
36320 Roo.bootstrap.SplitBar.prototype.overlay = o;
36322 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36323 this.overlay.show();
36324 Roo.get(this.proxy).setDisplayed("block");
36325 var size = this.adapter.getElementSize(this);
36326 this.activeMinSize = this.getMinimumSize();;
36327 this.activeMaxSize = this.getMaximumSize();;
36328 var c1 = size - this.activeMinSize;
36329 var c2 = Math.max(this.activeMaxSize - size, 0);
36330 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36331 this.dd.resetConstraints();
36332 this.dd.setXConstraint(
36333 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36334 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36336 this.dd.setYConstraint(0, 0);
36338 this.dd.resetConstraints();
36339 this.dd.setXConstraint(0, 0);
36340 this.dd.setYConstraint(
36341 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36342 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36345 this.dragSpecs.startSize = size;
36346 this.dragSpecs.startPoint = [x, y];
36347 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36351 * @private Called after the drag operation by the DDProxy
36353 onEndProxyDrag : function(e){
36354 Roo.get(this.proxy).setDisplayed(false);
36355 var endPoint = Roo.lib.Event.getXY(e);
36357 this.overlay.hide();
36360 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36361 newSize = this.dragSpecs.startSize +
36362 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36363 endPoint[0] - this.dragSpecs.startPoint[0] :
36364 this.dragSpecs.startPoint[0] - endPoint[0]
36367 newSize = this.dragSpecs.startSize +
36368 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36369 endPoint[1] - this.dragSpecs.startPoint[1] :
36370 this.dragSpecs.startPoint[1] - endPoint[1]
36373 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36374 if(newSize != this.dragSpecs.startSize){
36375 if(this.fireEvent('beforeapply', this, newSize) !== false){
36376 this.adapter.setElementSize(this, newSize);
36377 this.fireEvent("moved", this, newSize);
36378 this.fireEvent("resize", this, newSize);
36384 * Get the adapter this SplitBar uses
36385 * @return The adapter object
36387 getAdapter : function(){
36388 return this.adapter;
36392 * Set the adapter this SplitBar uses
36393 * @param {Object} adapter A SplitBar adapter object
36395 setAdapter : function(adapter){
36396 this.adapter = adapter;
36397 this.adapter.init(this);
36401 * Gets the minimum size for the resizing element
36402 * @return {Number} The minimum size
36404 getMinimumSize : function(){
36405 return this.minSize;
36409 * Sets the minimum size for the resizing element
36410 * @param {Number} minSize The minimum size
36412 setMinimumSize : function(minSize){
36413 this.minSize = minSize;
36417 * Gets the maximum size for the resizing element
36418 * @return {Number} The maximum size
36420 getMaximumSize : function(){
36421 return this.maxSize;
36425 * Sets the maximum size for the resizing element
36426 * @param {Number} maxSize The maximum size
36428 setMaximumSize : function(maxSize){
36429 this.maxSize = maxSize;
36433 * Sets the initialize size for the resizing element
36434 * @param {Number} size The initial size
36436 setCurrentSize : function(size){
36437 var oldAnimate = this.animate;
36438 this.animate = false;
36439 this.adapter.setElementSize(this, size);
36440 this.animate = oldAnimate;
36444 * Destroy this splitbar.
36445 * @param {Boolean} removeEl True to remove the element
36447 destroy : function(removeEl){
36449 this.shim.remove();
36452 this.proxy.parentNode.removeChild(this.proxy);
36460 * @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.
36462 Roo.bootstrap.SplitBar.createProxy = function(dir){
36463 var proxy = new Roo.Element(document.createElement("div"));
36464 proxy.unselectable();
36465 var cls = 'roo-splitbar-proxy';
36466 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36467 document.body.appendChild(proxy.dom);
36472 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36473 * Default Adapter. It assumes the splitter and resizing element are not positioned
36474 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36476 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36479 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36480 // do nothing for now
36481 init : function(s){
36485 * Called before drag operations to get the current size of the resizing element.
36486 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36488 getElementSize : function(s){
36489 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36490 return s.resizingEl.getWidth();
36492 return s.resizingEl.getHeight();
36497 * Called after drag operations to set the size of the resizing element.
36498 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36499 * @param {Number} newSize The new size to set
36500 * @param {Function} onComplete A function to be invoked when resizing is complete
36502 setElementSize : function(s, newSize, onComplete){
36503 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36505 s.resizingEl.setWidth(newSize);
36507 onComplete(s, newSize);
36510 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36515 s.resizingEl.setHeight(newSize);
36517 onComplete(s, newSize);
36520 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36527 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36528 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36529 * Adapter that moves the splitter element to align with the resized sizing element.
36530 * Used with an absolute positioned SplitBar.
36531 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36532 * document.body, make sure you assign an id to the body element.
36534 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36535 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36536 this.container = Roo.get(container);
36539 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36540 init : function(s){
36541 this.basic.init(s);
36544 getElementSize : function(s){
36545 return this.basic.getElementSize(s);
36548 setElementSize : function(s, newSize, onComplete){
36549 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36552 moveSplitter : function(s){
36553 var yes = Roo.bootstrap.SplitBar;
36554 switch(s.placement){
36556 s.el.setX(s.resizingEl.getRight());
36559 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36562 s.el.setY(s.resizingEl.getBottom());
36565 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36572 * Orientation constant - Create a vertical SplitBar
36576 Roo.bootstrap.SplitBar.VERTICAL = 1;
36579 * Orientation constant - Create a horizontal SplitBar
36583 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36586 * Placement constant - The resizing element is to the left of the splitter element
36590 Roo.bootstrap.SplitBar.LEFT = 1;
36593 * Placement constant - The resizing element is to the right of the splitter element
36597 Roo.bootstrap.SplitBar.RIGHT = 2;
36600 * Placement constant - The resizing element is positioned above the splitter element
36604 Roo.bootstrap.SplitBar.TOP = 3;
36607 * Placement constant - The resizing element is positioned under splitter element
36611 Roo.bootstrap.SplitBar.BOTTOM = 4;
36612 Roo.namespace("Roo.bootstrap.layout");/*
36614 * Ext JS Library 1.1.1
36615 * Copyright(c) 2006-2007, Ext JS, LLC.
36617 * Originally Released Under LGPL - original licence link has changed is not relivant.
36620 * <script type="text/javascript">
36624 * @class Roo.bootstrap.layout.Manager
36625 * @extends Roo.bootstrap.Component
36626 * Base class for layout managers.
36628 Roo.bootstrap.layout.Manager = function(config)
36630 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36636 /** false to disable window resize monitoring @type Boolean */
36637 this.monitorWindowResize = true;
36642 * Fires when a layout is performed.
36643 * @param {Roo.LayoutManager} this
36647 * @event regionresized
36648 * Fires when the user resizes a region.
36649 * @param {Roo.LayoutRegion} region The resized region
36650 * @param {Number} newSize The new size (width for east/west, height for north/south)
36652 "regionresized" : true,
36654 * @event regioncollapsed
36655 * Fires when a region is collapsed.
36656 * @param {Roo.LayoutRegion} region The collapsed region
36658 "regioncollapsed" : true,
36660 * @event regionexpanded
36661 * Fires when a region is expanded.
36662 * @param {Roo.LayoutRegion} region The expanded region
36664 "regionexpanded" : true
36666 this.updating = false;
36669 this.el = Roo.get(config.el);
36675 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36680 monitorWindowResize : true,
36686 onRender : function(ct, position)
36689 this.el = Roo.get(ct);
36692 //this.fireEvent('render',this);
36696 initEvents: function()
36700 // ie scrollbar fix
36701 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36702 document.body.scroll = "no";
36703 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36704 this.el.position('relative');
36706 this.id = this.el.id;
36707 this.el.addClass("roo-layout-container");
36708 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36709 if(this.el.dom != document.body ) {
36710 this.el.on('resize', this.layout,this);
36711 this.el.on('show', this.layout,this);
36717 * Returns true if this layout is currently being updated
36718 * @return {Boolean}
36720 isUpdating : function(){
36721 return this.updating;
36725 * Suspend the LayoutManager from doing auto-layouts while
36726 * making multiple add or remove calls
36728 beginUpdate : function(){
36729 this.updating = true;
36733 * Restore auto-layouts and optionally disable the manager from performing a layout
36734 * @param {Boolean} noLayout true to disable a layout update
36736 endUpdate : function(noLayout){
36737 this.updating = false;
36743 layout: function(){
36747 onRegionResized : function(region, newSize){
36748 this.fireEvent("regionresized", region, newSize);
36752 onRegionCollapsed : function(region){
36753 this.fireEvent("regioncollapsed", region);
36756 onRegionExpanded : function(region){
36757 this.fireEvent("regionexpanded", region);
36761 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36762 * performs box-model adjustments.
36763 * @return {Object} The size as an object {width: (the width), height: (the height)}
36765 getViewSize : function()
36768 if(this.el.dom != document.body){
36769 size = this.el.getSize();
36771 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36773 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36774 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36779 * Returns the Element this layout is bound to.
36780 * @return {Roo.Element}
36782 getEl : function(){
36787 * Returns the specified region.
36788 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36789 * @return {Roo.LayoutRegion}
36791 getRegion : function(target){
36792 return this.regions[target.toLowerCase()];
36795 onWindowResize : function(){
36796 if(this.monitorWindowResize){
36803 * Ext JS Library 1.1.1
36804 * Copyright(c) 2006-2007, Ext JS, LLC.
36806 * Originally Released Under LGPL - original licence link has changed is not relivant.
36809 * <script type="text/javascript">
36812 * @class Roo.bootstrap.layout.Border
36813 * @extends Roo.bootstrap.layout.Manager
36814 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36815 * please see: examples/bootstrap/nested.html<br><br>
36817 <b>The container the layout is rendered into can be either the body element or any other element.
36818 If it is not the body element, the container needs to either be an absolute positioned element,
36819 or you will need to add "position:relative" to the css of the container. You will also need to specify
36820 the container size if it is not the body element.</b>
36823 * Create a new Border
36824 * @param {Object} config Configuration options
36826 Roo.bootstrap.layout.Border = function(config){
36827 config = config || {};
36828 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36832 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36833 if(config[region]){
36834 config[region].region = region;
36835 this.addRegion(config[region]);
36841 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
36843 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36845 parent : false, // this might point to a 'nest' or a ???
36848 * Creates and adds a new region if it doesn't already exist.
36849 * @param {String} target The target region key (north, south, east, west or center).
36850 * @param {Object} config The regions config object
36851 * @return {BorderLayoutRegion} The new region
36853 addRegion : function(config)
36855 if(!this.regions[config.region]){
36856 var r = this.factory(config);
36857 this.bindRegion(r);
36859 return this.regions[config.region];
36863 bindRegion : function(r){
36864 this.regions[r.config.region] = r;
36866 r.on("visibilitychange", this.layout, this);
36867 r.on("paneladded", this.layout, this);
36868 r.on("panelremoved", this.layout, this);
36869 r.on("invalidated", this.layout, this);
36870 r.on("resized", this.onRegionResized, this);
36871 r.on("collapsed", this.onRegionCollapsed, this);
36872 r.on("expanded", this.onRegionExpanded, this);
36876 * Performs a layout update.
36878 layout : function()
36880 if(this.updating) {
36884 // render all the rebions if they have not been done alreayd?
36885 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36886 if(this.regions[region] && !this.regions[region].bodyEl){
36887 this.regions[region].onRender(this.el)
36891 var size = this.getViewSize();
36892 var w = size.width;
36893 var h = size.height;
36898 //var x = 0, y = 0;
36900 var rs = this.regions;
36901 var north = rs["north"];
36902 var south = rs["south"];
36903 var west = rs["west"];
36904 var east = rs["east"];
36905 var center = rs["center"];
36906 //if(this.hideOnLayout){ // not supported anymore
36907 //c.el.setStyle("display", "none");
36909 if(north && north.isVisible()){
36910 var b = north.getBox();
36911 var m = north.getMargins();
36912 b.width = w - (m.left+m.right);
36915 centerY = b.height + b.y + m.bottom;
36916 centerH -= centerY;
36917 north.updateBox(this.safeBox(b));
36919 if(south && south.isVisible()){
36920 var b = south.getBox();
36921 var m = south.getMargins();
36922 b.width = w - (m.left+m.right);
36924 var totalHeight = (b.height + m.top + m.bottom);
36925 b.y = h - totalHeight + m.top;
36926 centerH -= totalHeight;
36927 south.updateBox(this.safeBox(b));
36929 if(west && west.isVisible()){
36930 var b = west.getBox();
36931 var m = west.getMargins();
36932 b.height = centerH - (m.top+m.bottom);
36934 b.y = centerY + m.top;
36935 var totalWidth = (b.width + m.left + m.right);
36936 centerX += totalWidth;
36937 centerW -= totalWidth;
36938 west.updateBox(this.safeBox(b));
36940 if(east && east.isVisible()){
36941 var b = east.getBox();
36942 var m = east.getMargins();
36943 b.height = centerH - (m.top+m.bottom);
36944 var totalWidth = (b.width + m.left + m.right);
36945 b.x = w - totalWidth + m.left;
36946 b.y = centerY + m.top;
36947 centerW -= totalWidth;
36948 east.updateBox(this.safeBox(b));
36951 var m = center.getMargins();
36953 x: centerX + m.left,
36954 y: centerY + m.top,
36955 width: centerW - (m.left+m.right),
36956 height: centerH - (m.top+m.bottom)
36958 //if(this.hideOnLayout){
36959 //center.el.setStyle("display", "block");
36961 center.updateBox(this.safeBox(centerBox));
36964 this.fireEvent("layout", this);
36968 safeBox : function(box){
36969 box.width = Math.max(0, box.width);
36970 box.height = Math.max(0, box.height);
36975 * Adds a ContentPanel (or subclass) to this layout.
36976 * @param {String} target The target region key (north, south, east, west or center).
36977 * @param {Roo.ContentPanel} panel The panel to add
36978 * @return {Roo.ContentPanel} The added panel
36980 add : function(target, panel){
36982 target = target.toLowerCase();
36983 return this.regions[target].add(panel);
36987 * Remove a ContentPanel (or subclass) to this layout.
36988 * @param {String} target The target region key (north, south, east, west or center).
36989 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36990 * @return {Roo.ContentPanel} The removed panel
36992 remove : function(target, panel){
36993 target = target.toLowerCase();
36994 return this.regions[target].remove(panel);
36998 * Searches all regions for a panel with the specified id
36999 * @param {String} panelId
37000 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37002 findPanel : function(panelId){
37003 var rs = this.regions;
37004 for(var target in rs){
37005 if(typeof rs[target] != "function"){
37006 var p = rs[target].getPanel(panelId);
37016 * Searches all regions for a panel with the specified id and activates (shows) it.
37017 * @param {String/ContentPanel} panelId The panels id or the panel itself
37018 * @return {Roo.ContentPanel} The shown panel or null
37020 showPanel : function(panelId) {
37021 var rs = this.regions;
37022 for(var target in rs){
37023 var r = rs[target];
37024 if(typeof r != "function"){
37025 if(r.hasPanel(panelId)){
37026 return r.showPanel(panelId);
37034 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37035 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37038 restoreState : function(provider){
37040 provider = Roo.state.Manager;
37042 var sm = new Roo.LayoutStateManager();
37043 sm.init(this, provider);
37049 * Adds a xtype elements to the layout.
37053 xtype : 'ContentPanel',
37060 xtype : 'NestedLayoutPanel',
37066 items : [ ... list of content panels or nested layout panels.. ]
37070 * @param {Object} cfg Xtype definition of item to add.
37072 addxtype : function(cfg)
37074 // basically accepts a pannel...
37075 // can accept a layout region..!?!?
37076 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37079 // theory? children can only be panels??
37081 //if (!cfg.xtype.match(/Panel$/)) {
37086 if (typeof(cfg.region) == 'undefined') {
37087 Roo.log("Failed to add Panel, region was not set");
37091 var region = cfg.region;
37097 xitems = cfg.items;
37102 if ( region == 'center') {
37103 Roo.log("Center: " + cfg.title);
37109 case 'Content': // ContentPanel (el, cfg)
37110 case 'Scroll': // ContentPanel (el, cfg)
37112 cfg.autoCreate = cfg.autoCreate || true;
37113 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37115 // var el = this.el.createChild();
37116 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37119 this.add(region, ret);
37123 case 'TreePanel': // our new panel!
37124 cfg.el = this.el.createChild();
37125 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37126 this.add(region, ret);
37131 // create a new Layout (which is a Border Layout...
37133 var clayout = cfg.layout;
37134 clayout.el = this.el.createChild();
37135 clayout.items = clayout.items || [];
37139 // replace this exitems with the clayout ones..
37140 xitems = clayout.items;
37142 // force background off if it's in center...
37143 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37144 cfg.background = false;
37146 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37149 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37150 //console.log('adding nested layout panel ' + cfg.toSource());
37151 this.add(region, ret);
37152 nb = {}; /// find first...
37157 // needs grid and region
37159 //var el = this.getRegion(region).el.createChild();
37161 *var el = this.el.createChild();
37162 // create the grid first...
37163 cfg.grid.container = el;
37164 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37167 if (region == 'center' && this.active ) {
37168 cfg.background = false;
37171 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37173 this.add(region, ret);
37175 if (cfg.background) {
37176 // render grid on panel activation (if panel background)
37177 ret.on('activate', function(gp) {
37178 if (!gp.grid.rendered) {
37179 // gp.grid.render(el);
37183 // cfg.grid.render(el);
37189 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37190 // it was the old xcomponent building that caused this before.
37191 // espeically if border is the top element in the tree.
37201 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37203 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37204 this.add(region, ret);
37208 throw "Can not add '" + cfg.xtype + "' to Border";
37214 this.beginUpdate();
37218 Roo.each(xitems, function(i) {
37219 region = nb && i.region ? i.region : false;
37221 var add = ret.addxtype(i);
37224 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37225 if (!i.background) {
37226 abn[region] = nb[region] ;
37233 // make the last non-background panel active..
37234 //if (nb) { Roo.log(abn); }
37237 for(var r in abn) {
37238 region = this.getRegion(r);
37240 // tried using nb[r], but it does not work..
37242 region.showPanel(abn[r]);
37253 factory : function(cfg)
37256 var validRegions = Roo.bootstrap.layout.Border.regions;
37258 var target = cfg.region;
37261 var r = Roo.bootstrap.layout;
37265 return new r.North(cfg);
37267 return new r.South(cfg);
37269 return new r.East(cfg);
37271 return new r.West(cfg);
37273 return new r.Center(cfg);
37275 throw 'Layout region "'+target+'" not supported.';
37282 * Ext JS Library 1.1.1
37283 * Copyright(c) 2006-2007, Ext JS, LLC.
37285 * Originally Released Under LGPL - original licence link has changed is not relivant.
37288 * <script type="text/javascript">
37292 * @class Roo.bootstrap.layout.Basic
37293 * @extends Roo.util.Observable
37294 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37295 * and does not have a titlebar, tabs or any other features. All it does is size and position
37296 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37297 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37298 * @cfg {string} region the region that it inhabits..
37299 * @cfg {bool} skipConfig skip config?
37303 Roo.bootstrap.layout.Basic = function(config){
37305 this.mgr = config.mgr;
37307 this.position = config.region;
37309 var skipConfig = config.skipConfig;
37313 * @scope Roo.BasicLayoutRegion
37317 * @event beforeremove
37318 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37319 * @param {Roo.LayoutRegion} this
37320 * @param {Roo.ContentPanel} panel The panel
37321 * @param {Object} e The cancel event object
37323 "beforeremove" : true,
37325 * @event invalidated
37326 * Fires when the layout for this region is changed.
37327 * @param {Roo.LayoutRegion} this
37329 "invalidated" : true,
37331 * @event visibilitychange
37332 * Fires when this region is shown or hidden
37333 * @param {Roo.LayoutRegion} this
37334 * @param {Boolean} visibility true or false
37336 "visibilitychange" : true,
37338 * @event paneladded
37339 * Fires when a panel is added.
37340 * @param {Roo.LayoutRegion} this
37341 * @param {Roo.ContentPanel} panel The panel
37343 "paneladded" : true,
37345 * @event panelremoved
37346 * Fires when a panel is removed.
37347 * @param {Roo.LayoutRegion} this
37348 * @param {Roo.ContentPanel} panel The panel
37350 "panelremoved" : true,
37352 * @event beforecollapse
37353 * Fires when this region before collapse.
37354 * @param {Roo.LayoutRegion} this
37356 "beforecollapse" : true,
37359 * Fires when this region is collapsed.
37360 * @param {Roo.LayoutRegion} this
37362 "collapsed" : true,
37365 * Fires when this region is expanded.
37366 * @param {Roo.LayoutRegion} this
37371 * Fires when this region is slid into view.
37372 * @param {Roo.LayoutRegion} this
37374 "slideshow" : true,
37377 * Fires when this region slides out of view.
37378 * @param {Roo.LayoutRegion} this
37380 "slidehide" : true,
37382 * @event panelactivated
37383 * Fires when a panel is activated.
37384 * @param {Roo.LayoutRegion} this
37385 * @param {Roo.ContentPanel} panel The activated panel
37387 "panelactivated" : true,
37390 * Fires when the user resizes this region.
37391 * @param {Roo.LayoutRegion} this
37392 * @param {Number} newSize The new size (width for east/west, height for north/south)
37396 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37397 this.panels = new Roo.util.MixedCollection();
37398 this.panels.getKey = this.getPanelId.createDelegate(this);
37400 this.activePanel = null;
37401 // ensure listeners are added...
37403 if (config.listeners || config.events) {
37404 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37405 listeners : config.listeners || {},
37406 events : config.events || {}
37410 if(skipConfig !== true){
37411 this.applyConfig(config);
37415 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37417 getPanelId : function(p){
37421 applyConfig : function(config){
37422 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37423 this.config = config;
37428 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37429 * the width, for horizontal (north, south) the height.
37430 * @param {Number} newSize The new width or height
37432 resizeTo : function(newSize){
37433 var el = this.el ? this.el :
37434 (this.activePanel ? this.activePanel.getEl() : null);
37436 switch(this.position){
37439 el.setWidth(newSize);
37440 this.fireEvent("resized", this, newSize);
37444 el.setHeight(newSize);
37445 this.fireEvent("resized", this, newSize);
37451 getBox : function(){
37452 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37455 getMargins : function(){
37456 return this.margins;
37459 updateBox : function(box){
37461 var el = this.activePanel.getEl();
37462 el.dom.style.left = box.x + "px";
37463 el.dom.style.top = box.y + "px";
37464 this.activePanel.setSize(box.width, box.height);
37468 * Returns the container element for this region.
37469 * @return {Roo.Element}
37471 getEl : function(){
37472 return this.activePanel;
37476 * Returns true if this region is currently visible.
37477 * @return {Boolean}
37479 isVisible : function(){
37480 return this.activePanel ? true : false;
37483 setActivePanel : function(panel){
37484 panel = this.getPanel(panel);
37485 if(this.activePanel && this.activePanel != panel){
37486 this.activePanel.setActiveState(false);
37487 this.activePanel.getEl().setLeftTop(-10000,-10000);
37489 this.activePanel = panel;
37490 panel.setActiveState(true);
37492 panel.setSize(this.box.width, this.box.height);
37494 this.fireEvent("panelactivated", this, panel);
37495 this.fireEvent("invalidated");
37499 * Show the specified panel.
37500 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37501 * @return {Roo.ContentPanel} The shown panel or null
37503 showPanel : function(panel){
37504 panel = this.getPanel(panel);
37506 this.setActivePanel(panel);
37512 * Get the active panel for this region.
37513 * @return {Roo.ContentPanel} The active panel or null
37515 getActivePanel : function(){
37516 return this.activePanel;
37520 * Add the passed ContentPanel(s)
37521 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37522 * @return {Roo.ContentPanel} The panel added (if only one was added)
37524 add : function(panel){
37525 if(arguments.length > 1){
37526 for(var i = 0, len = arguments.length; i < len; i++) {
37527 this.add(arguments[i]);
37531 if(this.hasPanel(panel)){
37532 this.showPanel(panel);
37535 var el = panel.getEl();
37536 if(el.dom.parentNode != this.mgr.el.dom){
37537 this.mgr.el.dom.appendChild(el.dom);
37539 if(panel.setRegion){
37540 panel.setRegion(this);
37542 this.panels.add(panel);
37543 el.setStyle("position", "absolute");
37544 if(!panel.background){
37545 this.setActivePanel(panel);
37546 if(this.config.initialSize && this.panels.getCount()==1){
37547 this.resizeTo(this.config.initialSize);
37550 this.fireEvent("paneladded", this, panel);
37555 * Returns true if the panel is in this region.
37556 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37557 * @return {Boolean}
37559 hasPanel : function(panel){
37560 if(typeof panel == "object"){ // must be panel obj
37561 panel = panel.getId();
37563 return this.getPanel(panel) ? true : false;
37567 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37568 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37569 * @param {Boolean} preservePanel Overrides the config preservePanel option
37570 * @return {Roo.ContentPanel} The panel that was removed
37572 remove : function(panel, preservePanel){
37573 panel = this.getPanel(panel);
37578 this.fireEvent("beforeremove", this, panel, e);
37579 if(e.cancel === true){
37582 var panelId = panel.getId();
37583 this.panels.removeKey(panelId);
37588 * Returns the panel specified or null if it's not in this region.
37589 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37590 * @return {Roo.ContentPanel}
37592 getPanel : function(id){
37593 if(typeof id == "object"){ // must be panel obj
37596 return this.panels.get(id);
37600 * Returns this regions position (north/south/east/west/center).
37603 getPosition: function(){
37604 return this.position;
37608 * Ext JS Library 1.1.1
37609 * Copyright(c) 2006-2007, Ext JS, LLC.
37611 * Originally Released Under LGPL - original licence link has changed is not relivant.
37614 * <script type="text/javascript">
37618 * @class Roo.bootstrap.layout.Region
37619 * @extends Roo.bootstrap.layout.Basic
37620 * This class represents a region in a layout manager.
37622 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37623 * @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})
37624 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
37625 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
37626 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
37627 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
37628 * @cfg {String} title The title for the region (overrides panel titles)
37629 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
37630 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37631 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
37632 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37633 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37634 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37635 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37636 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37637 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37638 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37640 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37641 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37642 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37643 * @cfg {Number} width For East/West panels
37644 * @cfg {Number} height For North/South panels
37645 * @cfg {Boolean} split To show the splitter
37646 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37648 * @cfg {string} cls Extra CSS classes to add to region
37650 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37651 * @cfg {string} region the region that it inhabits..
37654 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37655 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37657 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37658 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37659 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37661 Roo.bootstrap.layout.Region = function(config)
37663 this.applyConfig(config);
37665 var mgr = config.mgr;
37666 var pos = config.region;
37667 config.skipConfig = true;
37668 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37671 this.onRender(mgr.el);
37674 this.visible = true;
37675 this.collapsed = false;
37676 this.unrendered_panels = [];
37679 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37681 position: '', // set by wrapper (eg. north/south etc..)
37682 unrendered_panels : null, // unrendered panels.
37684 tabPosition : false,
37686 mgr: false, // points to 'Border'
37689 createBody : function(){
37690 /** This region's body element
37691 * @type Roo.Element */
37692 this.bodyEl = this.el.createChild({
37694 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37698 onRender: function(ctr, pos)
37700 var dh = Roo.DomHelper;
37701 /** This region's container element
37702 * @type Roo.Element */
37703 this.el = dh.append(ctr.dom, {
37705 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37707 /** This region's title element
37708 * @type Roo.Element */
37710 this.titleEl = dh.append(this.el.dom, {
37712 unselectable: "on",
37713 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37715 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
37716 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37720 this.titleEl.enableDisplayMode();
37721 /** This region's title text element
37722 * @type HTMLElement */
37723 this.titleTextEl = this.titleEl.dom.firstChild;
37724 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37726 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37727 this.closeBtn.enableDisplayMode();
37728 this.closeBtn.on("click", this.closeClicked, this);
37729 this.closeBtn.hide();
37731 this.createBody(this.config);
37732 if(this.config.hideWhenEmpty){
37734 this.on("paneladded", this.validateVisibility, this);
37735 this.on("panelremoved", this.validateVisibility, this);
37737 if(this.autoScroll){
37738 this.bodyEl.setStyle("overflow", "auto");
37740 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37742 //if(c.titlebar !== false){
37743 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37744 this.titleEl.hide();
37746 this.titleEl.show();
37747 if(this.config.title){
37748 this.titleTextEl.innerHTML = this.config.title;
37752 if(this.config.collapsed){
37753 this.collapse(true);
37755 if(this.config.hidden){
37759 if (this.unrendered_panels && this.unrendered_panels.length) {
37760 for (var i =0;i< this.unrendered_panels.length; i++) {
37761 this.add(this.unrendered_panels[i]);
37763 this.unrendered_panels = null;
37769 applyConfig : function(c)
37772 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37773 var dh = Roo.DomHelper;
37774 if(c.titlebar !== false){
37775 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37776 this.collapseBtn.on("click", this.collapse, this);
37777 this.collapseBtn.enableDisplayMode();
37779 if(c.showPin === true || this.showPin){
37780 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37781 this.stickBtn.enableDisplayMode();
37782 this.stickBtn.on("click", this.expand, this);
37783 this.stickBtn.hide();
37788 /** This region's collapsed element
37789 * @type Roo.Element */
37792 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37793 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37796 if(c.floatable !== false){
37797 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37798 this.collapsedEl.on("click", this.collapseClick, this);
37801 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37802 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37803 id: "message", unselectable: "on", style:{"float":"left"}});
37804 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37806 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37807 this.expandBtn.on("click", this.expand, this);
37811 if(this.collapseBtn){
37812 this.collapseBtn.setVisible(c.collapsible == true);
37815 this.cmargins = c.cmargins || this.cmargins ||
37816 (this.position == "west" || this.position == "east" ?
37817 {top: 0, left: 2, right:2, bottom: 0} :
37818 {top: 2, left: 0, right:0, bottom: 2});
37820 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37823 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37825 this.autoScroll = c.autoScroll || false;
37830 this.duration = c.duration || .30;
37831 this.slideDuration = c.slideDuration || .45;
37836 * Returns true if this region is currently visible.
37837 * @return {Boolean}
37839 isVisible : function(){
37840 return this.visible;
37844 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37845 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
37847 //setCollapsedTitle : function(title){
37848 // title = title || " ";
37849 // if(this.collapsedTitleTextEl){
37850 // this.collapsedTitleTextEl.innerHTML = title;
37854 getBox : function(){
37856 // if(!this.collapsed){
37857 b = this.el.getBox(false, true);
37859 // b = this.collapsedEl.getBox(false, true);
37864 getMargins : function(){
37865 return this.margins;
37866 //return this.collapsed ? this.cmargins : this.margins;
37869 highlight : function(){
37870 this.el.addClass("x-layout-panel-dragover");
37873 unhighlight : function(){
37874 this.el.removeClass("x-layout-panel-dragover");
37877 updateBox : function(box)
37879 if (!this.bodyEl) {
37880 return; // not rendered yet..
37884 if(!this.collapsed){
37885 this.el.dom.style.left = box.x + "px";
37886 this.el.dom.style.top = box.y + "px";
37887 this.updateBody(box.width, box.height);
37889 this.collapsedEl.dom.style.left = box.x + "px";
37890 this.collapsedEl.dom.style.top = box.y + "px";
37891 this.collapsedEl.setSize(box.width, box.height);
37894 this.tabs.autoSizeTabs();
37898 updateBody : function(w, h)
37901 this.el.setWidth(w);
37902 w -= this.el.getBorderWidth("rl");
37903 if(this.config.adjustments){
37904 w += this.config.adjustments[0];
37907 if(h !== null && h > 0){
37908 this.el.setHeight(h);
37909 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37910 h -= this.el.getBorderWidth("tb");
37911 if(this.config.adjustments){
37912 h += this.config.adjustments[1];
37914 this.bodyEl.setHeight(h);
37916 h = this.tabs.syncHeight(h);
37919 if(this.panelSize){
37920 w = w !== null ? w : this.panelSize.width;
37921 h = h !== null ? h : this.panelSize.height;
37923 if(this.activePanel){
37924 var el = this.activePanel.getEl();
37925 w = w !== null ? w : el.getWidth();
37926 h = h !== null ? h : el.getHeight();
37927 this.panelSize = {width: w, height: h};
37928 this.activePanel.setSize(w, h);
37930 if(Roo.isIE && this.tabs){
37931 this.tabs.el.repaint();
37936 * Returns the container element for this region.
37937 * @return {Roo.Element}
37939 getEl : function(){
37944 * Hides this region.
37947 //if(!this.collapsed){
37948 this.el.dom.style.left = "-2000px";
37951 // this.collapsedEl.dom.style.left = "-2000px";
37952 // this.collapsedEl.hide();
37954 this.visible = false;
37955 this.fireEvent("visibilitychange", this, false);
37959 * Shows this region if it was previously hidden.
37962 //if(!this.collapsed){
37965 // this.collapsedEl.show();
37967 this.visible = true;
37968 this.fireEvent("visibilitychange", this, true);
37971 closeClicked : function(){
37972 if(this.activePanel){
37973 this.remove(this.activePanel);
37977 collapseClick : function(e){
37979 e.stopPropagation();
37982 e.stopPropagation();
37988 * Collapses this region.
37989 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37992 collapse : function(skipAnim, skipCheck = false){
37993 if(this.collapsed) {
37997 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37999 this.collapsed = true;
38001 this.split.el.hide();
38003 if(this.config.animate && skipAnim !== true){
38004 this.fireEvent("invalidated", this);
38005 this.animateCollapse();
38007 this.el.setLocation(-20000,-20000);
38009 this.collapsedEl.show();
38010 this.fireEvent("collapsed", this);
38011 this.fireEvent("invalidated", this);
38017 animateCollapse : function(){
38022 * Expands this region if it was previously collapsed.
38023 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38024 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38027 expand : function(e, skipAnim){
38029 e.stopPropagation();
38031 if(!this.collapsed || this.el.hasActiveFx()) {
38035 this.afterSlideIn();
38038 this.collapsed = false;
38039 if(this.config.animate && skipAnim !== true){
38040 this.animateExpand();
38044 this.split.el.show();
38046 this.collapsedEl.setLocation(-2000,-2000);
38047 this.collapsedEl.hide();
38048 this.fireEvent("invalidated", this);
38049 this.fireEvent("expanded", this);
38053 animateExpand : function(){
38057 initTabs : function()
38059 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38061 var ts = new Roo.bootstrap.panel.Tabs({
38062 el: this.bodyEl.dom,
38064 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38065 disableTooltips: this.config.disableTabTips,
38066 toolbar : this.config.toolbar
38069 if(this.config.hideTabs){
38070 ts.stripWrap.setDisplayed(false);
38073 ts.resizeTabs = this.config.resizeTabs === true;
38074 ts.minTabWidth = this.config.minTabWidth || 40;
38075 ts.maxTabWidth = this.config.maxTabWidth || 250;
38076 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38077 ts.monitorResize = false;
38078 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38079 ts.bodyEl.addClass('roo-layout-tabs-body');
38080 this.panels.each(this.initPanelAsTab, this);
38083 initPanelAsTab : function(panel){
38084 var ti = this.tabs.addTab(
38088 this.config.closeOnTab && panel.isClosable(),
38091 if(panel.tabTip !== undefined){
38092 ti.setTooltip(panel.tabTip);
38094 ti.on("activate", function(){
38095 this.setActivePanel(panel);
38098 if(this.config.closeOnTab){
38099 ti.on("beforeclose", function(t, e){
38101 this.remove(panel);
38105 panel.tabItem = ti;
38110 updatePanelTitle : function(panel, title)
38112 if(this.activePanel == panel){
38113 this.updateTitle(title);
38116 var ti = this.tabs.getTab(panel.getEl().id);
38118 if(panel.tabTip !== undefined){
38119 ti.setTooltip(panel.tabTip);
38124 updateTitle : function(title){
38125 if(this.titleTextEl && !this.config.title){
38126 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38130 setActivePanel : function(panel)
38132 panel = this.getPanel(panel);
38133 if(this.activePanel && this.activePanel != panel){
38134 if(this.activePanel.setActiveState(false) === false){
38138 this.activePanel = panel;
38139 panel.setActiveState(true);
38140 if(this.panelSize){
38141 panel.setSize(this.panelSize.width, this.panelSize.height);
38144 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38146 this.updateTitle(panel.getTitle());
38148 this.fireEvent("invalidated", this);
38150 this.fireEvent("panelactivated", this, panel);
38154 * Shows the specified panel.
38155 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38156 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38158 showPanel : function(panel)
38160 panel = this.getPanel(panel);
38163 var tab = this.tabs.getTab(panel.getEl().id);
38164 if(tab.isHidden()){
38165 this.tabs.unhideTab(tab.id);
38169 this.setActivePanel(panel);
38176 * Get the active panel for this region.
38177 * @return {Roo.ContentPanel} The active panel or null
38179 getActivePanel : function(){
38180 return this.activePanel;
38183 validateVisibility : function(){
38184 if(this.panels.getCount() < 1){
38185 this.updateTitle(" ");
38186 this.closeBtn.hide();
38189 if(!this.isVisible()){
38196 * Adds the passed ContentPanel(s) to this region.
38197 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38198 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38200 add : function(panel)
38202 if(arguments.length > 1){
38203 for(var i = 0, len = arguments.length; i < len; i++) {
38204 this.add(arguments[i]);
38209 // if we have not been rendered yet, then we can not really do much of this..
38210 if (!this.bodyEl) {
38211 this.unrendered_panels.push(panel);
38218 if(this.hasPanel(panel)){
38219 this.showPanel(panel);
38222 panel.setRegion(this);
38223 this.panels.add(panel);
38224 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38225 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38226 // and hide them... ???
38227 this.bodyEl.dom.appendChild(panel.getEl().dom);
38228 if(panel.background !== true){
38229 this.setActivePanel(panel);
38231 this.fireEvent("paneladded", this, panel);
38238 this.initPanelAsTab(panel);
38242 if(panel.background !== true){
38243 this.tabs.activate(panel.getEl().id);
38245 this.fireEvent("paneladded", this, panel);
38250 * Hides the tab for the specified panel.
38251 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38253 hidePanel : function(panel){
38254 if(this.tabs && (panel = this.getPanel(panel))){
38255 this.tabs.hideTab(panel.getEl().id);
38260 * Unhides the tab for a previously hidden panel.
38261 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38263 unhidePanel : function(panel){
38264 if(this.tabs && (panel = this.getPanel(panel))){
38265 this.tabs.unhideTab(panel.getEl().id);
38269 clearPanels : function(){
38270 while(this.panels.getCount() > 0){
38271 this.remove(this.panels.first());
38276 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38277 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38278 * @param {Boolean} preservePanel Overrides the config preservePanel option
38279 * @return {Roo.ContentPanel} The panel that was removed
38281 remove : function(panel, preservePanel)
38283 panel = this.getPanel(panel);
38288 this.fireEvent("beforeremove", this, panel, e);
38289 if(e.cancel === true){
38292 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38293 var panelId = panel.getId();
38294 this.panels.removeKey(panelId);
38296 document.body.appendChild(panel.getEl().dom);
38299 this.tabs.removeTab(panel.getEl().id);
38300 }else if (!preservePanel){
38301 this.bodyEl.dom.removeChild(panel.getEl().dom);
38303 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38304 var p = this.panels.first();
38305 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38306 tempEl.appendChild(p.getEl().dom);
38307 this.bodyEl.update("");
38308 this.bodyEl.dom.appendChild(p.getEl().dom);
38310 this.updateTitle(p.getTitle());
38312 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38313 this.setActivePanel(p);
38315 panel.setRegion(null);
38316 if(this.activePanel == panel){
38317 this.activePanel = null;
38319 if(this.config.autoDestroy !== false && preservePanel !== true){
38320 try{panel.destroy();}catch(e){}
38322 this.fireEvent("panelremoved", this, panel);
38327 * Returns the TabPanel component used by this region
38328 * @return {Roo.TabPanel}
38330 getTabs : function(){
38334 createTool : function(parentEl, className){
38335 var btn = Roo.DomHelper.append(parentEl, {
38337 cls: "x-layout-tools-button",
38340 cls: "roo-layout-tools-button-inner " + className,
38344 btn.addClassOnOver("roo-layout-tools-button-over");
38349 * Ext JS Library 1.1.1
38350 * Copyright(c) 2006-2007, Ext JS, LLC.
38352 * Originally Released Under LGPL - original licence link has changed is not relivant.
38355 * <script type="text/javascript">
38361 * @class Roo.SplitLayoutRegion
38362 * @extends Roo.LayoutRegion
38363 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38365 Roo.bootstrap.layout.Split = function(config){
38366 this.cursor = config.cursor;
38367 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38370 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38372 splitTip : "Drag to resize.",
38373 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38374 useSplitTips : false,
38376 applyConfig : function(config){
38377 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38380 onRender : function(ctr,pos) {
38382 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38383 if(!this.config.split){
38388 var splitEl = Roo.DomHelper.append(ctr.dom, {
38390 id: this.el.id + "-split",
38391 cls: "roo-layout-split roo-layout-split-"+this.position,
38394 /** The SplitBar for this region
38395 * @type Roo.SplitBar */
38396 // does not exist yet...
38397 Roo.log([this.position, this.orientation]);
38399 this.split = new Roo.bootstrap.SplitBar({
38400 dragElement : splitEl,
38401 resizingElement: this.el,
38402 orientation : this.orientation
38405 this.split.on("moved", this.onSplitMove, this);
38406 this.split.useShim = this.config.useShim === true;
38407 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38408 if(this.useSplitTips){
38409 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38411 //if(config.collapsible){
38412 // this.split.el.on("dblclick", this.collapse, this);
38415 if(typeof this.config.minSize != "undefined"){
38416 this.split.minSize = this.config.minSize;
38418 if(typeof this.config.maxSize != "undefined"){
38419 this.split.maxSize = this.config.maxSize;
38421 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38422 this.hideSplitter();
38427 getHMaxSize : function(){
38428 var cmax = this.config.maxSize || 10000;
38429 var center = this.mgr.getRegion("center");
38430 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38433 getVMaxSize : function(){
38434 var cmax = this.config.maxSize || 10000;
38435 var center = this.mgr.getRegion("center");
38436 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38439 onSplitMove : function(split, newSize){
38440 this.fireEvent("resized", this, newSize);
38444 * Returns the {@link Roo.SplitBar} for this region.
38445 * @return {Roo.SplitBar}
38447 getSplitBar : function(){
38452 this.hideSplitter();
38453 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38456 hideSplitter : function(){
38458 this.split.el.setLocation(-2000,-2000);
38459 this.split.el.hide();
38465 this.split.el.show();
38467 Roo.bootstrap.layout.Split.superclass.show.call(this);
38470 beforeSlide: function(){
38471 if(Roo.isGecko){// firefox overflow auto bug workaround
38472 this.bodyEl.clip();
38474 this.tabs.bodyEl.clip();
38476 if(this.activePanel){
38477 this.activePanel.getEl().clip();
38479 if(this.activePanel.beforeSlide){
38480 this.activePanel.beforeSlide();
38486 afterSlide : function(){
38487 if(Roo.isGecko){// firefox overflow auto bug workaround
38488 this.bodyEl.unclip();
38490 this.tabs.bodyEl.unclip();
38492 if(this.activePanel){
38493 this.activePanel.getEl().unclip();
38494 if(this.activePanel.afterSlide){
38495 this.activePanel.afterSlide();
38501 initAutoHide : function(){
38502 if(this.autoHide !== false){
38503 if(!this.autoHideHd){
38504 var st = new Roo.util.DelayedTask(this.slideIn, this);
38505 this.autoHideHd = {
38506 "mouseout": function(e){
38507 if(!e.within(this.el, true)){
38511 "mouseover" : function(e){
38517 this.el.on(this.autoHideHd);
38521 clearAutoHide : function(){
38522 if(this.autoHide !== false){
38523 this.el.un("mouseout", this.autoHideHd.mouseout);
38524 this.el.un("mouseover", this.autoHideHd.mouseover);
38528 clearMonitor : function(){
38529 Roo.get(document).un("click", this.slideInIf, this);
38532 // these names are backwards but not changed for compat
38533 slideOut : function(){
38534 if(this.isSlid || this.el.hasActiveFx()){
38537 this.isSlid = true;
38538 if(this.collapseBtn){
38539 this.collapseBtn.hide();
38541 this.closeBtnState = this.closeBtn.getStyle('display');
38542 this.closeBtn.hide();
38544 this.stickBtn.show();
38547 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38548 this.beforeSlide();
38549 this.el.setStyle("z-index", 10001);
38550 this.el.slideIn(this.getSlideAnchor(), {
38551 callback: function(){
38553 this.initAutoHide();
38554 Roo.get(document).on("click", this.slideInIf, this);
38555 this.fireEvent("slideshow", this);
38562 afterSlideIn : function(){
38563 this.clearAutoHide();
38564 this.isSlid = false;
38565 this.clearMonitor();
38566 this.el.setStyle("z-index", "");
38567 if(this.collapseBtn){
38568 this.collapseBtn.show();
38570 this.closeBtn.setStyle('display', this.closeBtnState);
38572 this.stickBtn.hide();
38574 this.fireEvent("slidehide", this);
38577 slideIn : function(cb){
38578 if(!this.isSlid || this.el.hasActiveFx()){
38582 this.isSlid = false;
38583 this.beforeSlide();
38584 this.el.slideOut(this.getSlideAnchor(), {
38585 callback: function(){
38586 this.el.setLeftTop(-10000, -10000);
38588 this.afterSlideIn();
38596 slideInIf : function(e){
38597 if(!e.within(this.el)){
38602 animateCollapse : function(){
38603 this.beforeSlide();
38604 this.el.setStyle("z-index", 20000);
38605 var anchor = this.getSlideAnchor();
38606 this.el.slideOut(anchor, {
38607 callback : function(){
38608 this.el.setStyle("z-index", "");
38609 this.collapsedEl.slideIn(anchor, {duration:.3});
38611 this.el.setLocation(-10000,-10000);
38613 this.fireEvent("collapsed", this);
38620 animateExpand : function(){
38621 this.beforeSlide();
38622 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38623 this.el.setStyle("z-index", 20000);
38624 this.collapsedEl.hide({
38627 this.el.slideIn(this.getSlideAnchor(), {
38628 callback : function(){
38629 this.el.setStyle("z-index", "");
38632 this.split.el.show();
38634 this.fireEvent("invalidated", this);
38635 this.fireEvent("expanded", this);
38663 getAnchor : function(){
38664 return this.anchors[this.position];
38667 getCollapseAnchor : function(){
38668 return this.canchors[this.position];
38671 getSlideAnchor : function(){
38672 return this.sanchors[this.position];
38675 getAlignAdj : function(){
38676 var cm = this.cmargins;
38677 switch(this.position){
38693 getExpandAdj : function(){
38694 var c = this.collapsedEl, cm = this.cmargins;
38695 switch(this.position){
38697 return [-(cm.right+c.getWidth()+cm.left), 0];
38700 return [cm.right+c.getWidth()+cm.left, 0];
38703 return [0, -(cm.top+cm.bottom+c.getHeight())];
38706 return [0, cm.top+cm.bottom+c.getHeight()];
38712 * Ext JS Library 1.1.1
38713 * Copyright(c) 2006-2007, Ext JS, LLC.
38715 * Originally Released Under LGPL - original licence link has changed is not relivant.
38718 * <script type="text/javascript">
38721 * These classes are private internal classes
38723 Roo.bootstrap.layout.Center = function(config){
38724 config.region = "center";
38725 Roo.bootstrap.layout.Region.call(this, config);
38726 this.visible = true;
38727 this.minWidth = config.minWidth || 20;
38728 this.minHeight = config.minHeight || 20;
38731 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38733 // center panel can't be hidden
38737 // center panel can't be hidden
38740 getMinWidth: function(){
38741 return this.minWidth;
38744 getMinHeight: function(){
38745 return this.minHeight;
38759 Roo.bootstrap.layout.North = function(config)
38761 config.region = 'north';
38762 config.cursor = 'n-resize';
38764 Roo.bootstrap.layout.Split.call(this, config);
38768 this.split.placement = Roo.bootstrap.SplitBar.TOP;
38769 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38770 this.split.el.addClass("roo-layout-split-v");
38772 var size = config.initialSize || config.height;
38773 if(typeof size != "undefined"){
38774 this.el.setHeight(size);
38777 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38779 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38783 getBox : function(){
38784 if(this.collapsed){
38785 return this.collapsedEl.getBox();
38787 var box = this.el.getBox();
38789 box.height += this.split.el.getHeight();
38794 updateBox : function(box){
38795 if(this.split && !this.collapsed){
38796 box.height -= this.split.el.getHeight();
38797 this.split.el.setLeft(box.x);
38798 this.split.el.setTop(box.y+box.height);
38799 this.split.el.setWidth(box.width);
38801 if(this.collapsed){
38802 this.updateBody(box.width, null);
38804 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38812 Roo.bootstrap.layout.South = function(config){
38813 config.region = 'south';
38814 config.cursor = 's-resize';
38815 Roo.bootstrap.layout.Split.call(this, config);
38817 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38818 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38819 this.split.el.addClass("roo-layout-split-v");
38821 var size = config.initialSize || config.height;
38822 if(typeof size != "undefined"){
38823 this.el.setHeight(size);
38827 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38828 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38829 getBox : function(){
38830 if(this.collapsed){
38831 return this.collapsedEl.getBox();
38833 var box = this.el.getBox();
38835 var sh = this.split.el.getHeight();
38842 updateBox : function(box){
38843 if(this.split && !this.collapsed){
38844 var sh = this.split.el.getHeight();
38847 this.split.el.setLeft(box.x);
38848 this.split.el.setTop(box.y-sh);
38849 this.split.el.setWidth(box.width);
38851 if(this.collapsed){
38852 this.updateBody(box.width, null);
38854 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38858 Roo.bootstrap.layout.East = function(config){
38859 config.region = "east";
38860 config.cursor = "e-resize";
38861 Roo.bootstrap.layout.Split.call(this, config);
38863 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38864 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38865 this.split.el.addClass("roo-layout-split-h");
38867 var size = config.initialSize || config.width;
38868 if(typeof size != "undefined"){
38869 this.el.setWidth(size);
38872 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38873 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38874 getBox : function(){
38875 if(this.collapsed){
38876 return this.collapsedEl.getBox();
38878 var box = this.el.getBox();
38880 var sw = this.split.el.getWidth();
38887 updateBox : function(box){
38888 if(this.split && !this.collapsed){
38889 var sw = this.split.el.getWidth();
38891 this.split.el.setLeft(box.x);
38892 this.split.el.setTop(box.y);
38893 this.split.el.setHeight(box.height);
38896 if(this.collapsed){
38897 this.updateBody(null, box.height);
38899 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38903 Roo.bootstrap.layout.West = function(config){
38904 config.region = "west";
38905 config.cursor = "w-resize";
38907 Roo.bootstrap.layout.Split.call(this, config);
38909 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38910 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38911 this.split.el.addClass("roo-layout-split-h");
38915 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38916 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38918 onRender: function(ctr, pos)
38920 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38921 var size = this.config.initialSize || this.config.width;
38922 if(typeof size != "undefined"){
38923 this.el.setWidth(size);
38927 getBox : function(){
38928 if(this.collapsed){
38929 return this.collapsedEl.getBox();
38931 var box = this.el.getBox();
38933 box.width += this.split.el.getWidth();
38938 updateBox : function(box){
38939 if(this.split && !this.collapsed){
38940 var sw = this.split.el.getWidth();
38942 this.split.el.setLeft(box.x+box.width);
38943 this.split.el.setTop(box.y);
38944 this.split.el.setHeight(box.height);
38946 if(this.collapsed){
38947 this.updateBody(null, box.height);
38949 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38951 });Roo.namespace("Roo.bootstrap.panel");/*
38953 * Ext JS Library 1.1.1
38954 * Copyright(c) 2006-2007, Ext JS, LLC.
38956 * Originally Released Under LGPL - original licence link has changed is not relivant.
38959 * <script type="text/javascript">
38962 * @class Roo.ContentPanel
38963 * @extends Roo.util.Observable
38964 * A basic ContentPanel element.
38965 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
38966 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
38967 * @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
38968 * @cfg {Boolean} closable True if the panel can be closed/removed
38969 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
38970 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38971 * @cfg {Toolbar} toolbar A toolbar for this panel
38972 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
38973 * @cfg {String} title The title for this panel
38974 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38975 * @cfg {String} url Calls {@link #setUrl} with this value
38976 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38977 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
38978 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
38979 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
38980 * @cfg {Boolean} badges render the badges
38983 * Create a new ContentPanel.
38984 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38985 * @param {String/Object} config A string to set only the title or a config object
38986 * @param {String} content (optional) Set the HTML content for this panel
38987 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38989 Roo.bootstrap.panel.Content = function( config){
38991 this.tpl = config.tpl || false;
38993 var el = config.el;
38994 var content = config.content;
38996 if(config.autoCreate){ // xtype is available if this is called from factory
38999 this.el = Roo.get(el);
39000 if(!this.el && config && config.autoCreate){
39001 if(typeof config.autoCreate == "object"){
39002 if(!config.autoCreate.id){
39003 config.autoCreate.id = config.id||el;
39005 this.el = Roo.DomHelper.append(document.body,
39006 config.autoCreate, true);
39008 var elcfg = { tag: "div",
39009 cls: "roo-layout-inactive-content",
39013 elcfg.html = config.html;
39017 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39020 this.closable = false;
39021 this.loaded = false;
39022 this.active = false;
39025 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39027 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39029 this.wrapEl = this.el; //this.el.wrap();
39031 if (config.toolbar.items) {
39032 ti = config.toolbar.items ;
39033 delete config.toolbar.items ;
39037 this.toolbar.render(this.wrapEl, 'before');
39038 for(var i =0;i < ti.length;i++) {
39039 // Roo.log(['add child', items[i]]);
39040 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39042 this.toolbar.items = nitems;
39043 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39044 delete config.toolbar;
39048 // xtype created footer. - not sure if will work as we normally have to render first..
39049 if (this.footer && !this.footer.el && this.footer.xtype) {
39050 if (!this.wrapEl) {
39051 this.wrapEl = this.el.wrap();
39054 this.footer.container = this.wrapEl.createChild();
39056 this.footer = Roo.factory(this.footer, Roo);
39061 if(typeof config == "string"){
39062 this.title = config;
39064 Roo.apply(this, config);
39068 this.resizeEl = Roo.get(this.resizeEl, true);
39070 this.resizeEl = this.el;
39072 // handle view.xtype
39080 * Fires when this panel is activated.
39081 * @param {Roo.ContentPanel} this
39085 * @event deactivate
39086 * Fires when this panel is activated.
39087 * @param {Roo.ContentPanel} this
39089 "deactivate" : true,
39093 * Fires when this panel is resized if fitToFrame is true.
39094 * @param {Roo.ContentPanel} this
39095 * @param {Number} width The width after any component adjustments
39096 * @param {Number} height The height after any component adjustments
39102 * Fires when this tab is created
39103 * @param {Roo.ContentPanel} this
39114 if(this.autoScroll){
39115 this.resizeEl.setStyle("overflow", "auto");
39117 // fix randome scrolling
39118 //this.el.on('scroll', function() {
39119 // Roo.log('fix random scolling');
39120 // this.scrollTo('top',0);
39123 content = content || this.content;
39125 this.setContent(content);
39127 if(config && config.url){
39128 this.setUrl(this.url, this.params, this.loadOnce);
39133 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39135 if (this.view && typeof(this.view.xtype) != 'undefined') {
39136 this.view.el = this.el.appendChild(document.createElement("div"));
39137 this.view = Roo.factory(this.view);
39138 this.view.render && this.view.render(false, '');
39142 this.fireEvent('render', this);
39145 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39149 setRegion : function(region){
39150 this.region = region;
39151 this.setActiveClass(region && !this.background);
39155 setActiveClass: function(state)
39158 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39159 this.el.setStyle('position','relative');
39161 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39162 this.el.setStyle('position', 'absolute');
39167 * Returns the toolbar for this Panel if one was configured.
39168 * @return {Roo.Toolbar}
39170 getToolbar : function(){
39171 return this.toolbar;
39174 setActiveState : function(active)
39176 this.active = active;
39177 this.setActiveClass(active);
39179 if(this.fireEvent("deactivate", this) === false){
39184 this.fireEvent("activate", this);
39188 * Updates this panel's element
39189 * @param {String} content The new content
39190 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39192 setContent : function(content, loadScripts){
39193 this.el.update(content, loadScripts);
39196 ignoreResize : function(w, h){
39197 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39200 this.lastSize = {width: w, height: h};
39205 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39206 * @return {Roo.UpdateManager} The UpdateManager
39208 getUpdateManager : function(){
39209 return this.el.getUpdateManager();
39212 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39213 * @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:
39216 url: "your-url.php",
39217 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39218 callback: yourFunction,
39219 scope: yourObject, //(optional scope)
39222 text: "Loading...",
39227 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39228 * 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.
39229 * @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}
39230 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39231 * @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.
39232 * @return {Roo.ContentPanel} this
39235 var um = this.el.getUpdateManager();
39236 um.update.apply(um, arguments);
39242 * 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.
39243 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39244 * @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)
39245 * @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)
39246 * @return {Roo.UpdateManager} The UpdateManager
39248 setUrl : function(url, params, loadOnce){
39249 if(this.refreshDelegate){
39250 this.removeListener("activate", this.refreshDelegate);
39252 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39253 this.on("activate", this.refreshDelegate);
39254 return this.el.getUpdateManager();
39257 _handleRefresh : function(url, params, loadOnce){
39258 if(!loadOnce || !this.loaded){
39259 var updater = this.el.getUpdateManager();
39260 updater.update(url, params, this._setLoaded.createDelegate(this));
39264 _setLoaded : function(){
39265 this.loaded = true;
39269 * Returns this panel's id
39272 getId : function(){
39277 * Returns this panel's element - used by regiosn to add.
39278 * @return {Roo.Element}
39280 getEl : function(){
39281 return this.wrapEl || this.el;
39286 adjustForComponents : function(width, height)
39288 //Roo.log('adjustForComponents ');
39289 if(this.resizeEl != this.el){
39290 width -= this.el.getFrameWidth('lr');
39291 height -= this.el.getFrameWidth('tb');
39294 var te = this.toolbar.getEl();
39295 te.setWidth(width);
39296 height -= te.getHeight();
39299 var te = this.footer.getEl();
39300 te.setWidth(width);
39301 height -= te.getHeight();
39305 if(this.adjustments){
39306 width += this.adjustments[0];
39307 height += this.adjustments[1];
39309 return {"width": width, "height": height};
39312 setSize : function(width, height){
39313 if(this.fitToFrame && !this.ignoreResize(width, height)){
39314 if(this.fitContainer && this.resizeEl != this.el){
39315 this.el.setSize(width, height);
39317 var size = this.adjustForComponents(width, height);
39318 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39319 this.fireEvent('resize', this, size.width, size.height);
39324 * Returns this panel's title
39327 getTitle : function(){
39329 if (typeof(this.title) != 'object') {
39334 for (var k in this.title) {
39335 if (!this.title.hasOwnProperty(k)) {
39339 if (k.indexOf('-') >= 0) {
39340 var s = k.split('-');
39341 for (var i = 0; i<s.length; i++) {
39342 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39345 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39352 * Set this panel's title
39353 * @param {String} title
39355 setTitle : function(title){
39356 this.title = title;
39358 this.region.updatePanelTitle(this, title);
39363 * Returns true is this panel was configured to be closable
39364 * @return {Boolean}
39366 isClosable : function(){
39367 return this.closable;
39370 beforeSlide : function(){
39372 this.resizeEl.clip();
39375 afterSlide : function(){
39377 this.resizeEl.unclip();
39381 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39382 * Will fail silently if the {@link #setUrl} method has not been called.
39383 * This does not activate the panel, just updates its content.
39385 refresh : function(){
39386 if(this.refreshDelegate){
39387 this.loaded = false;
39388 this.refreshDelegate();
39393 * Destroys this panel
39395 destroy : function(){
39396 this.el.removeAllListeners();
39397 var tempEl = document.createElement("span");
39398 tempEl.appendChild(this.el.dom);
39399 tempEl.innerHTML = "";
39405 * form - if the content panel contains a form - this is a reference to it.
39406 * @type {Roo.form.Form}
39410 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39411 * This contains a reference to it.
39417 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39427 * @param {Object} cfg Xtype definition of item to add.
39431 getChildContainer: function () {
39432 return this.getEl();
39437 var ret = new Roo.factory(cfg);
39442 if (cfg.xtype.match(/^Form$/)) {
39445 //if (this.footer) {
39446 // el = this.footer.container.insertSibling(false, 'before');
39448 el = this.el.createChild();
39451 this.form = new Roo.form.Form(cfg);
39454 if ( this.form.allItems.length) {
39455 this.form.render(el.dom);
39459 // should only have one of theses..
39460 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39461 // views.. should not be just added - used named prop 'view''
39463 cfg.el = this.el.appendChild(document.createElement("div"));
39466 var ret = new Roo.factory(cfg);
39468 ret.render && ret.render(false, ''); // render blank..
39478 * @class Roo.bootstrap.panel.Grid
39479 * @extends Roo.bootstrap.panel.Content
39481 * Create a new GridPanel.
39482 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39483 * @param {Object} config A the config object
39489 Roo.bootstrap.panel.Grid = function(config)
39493 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39494 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39496 config.el = this.wrapper;
39497 //this.el = this.wrapper;
39499 if (config.container) {
39500 // ctor'ed from a Border/panel.grid
39503 this.wrapper.setStyle("overflow", "hidden");
39504 this.wrapper.addClass('roo-grid-container');
39509 if(config.toolbar){
39510 var tool_el = this.wrapper.createChild();
39511 this.toolbar = Roo.factory(config.toolbar);
39513 if (config.toolbar.items) {
39514 ti = config.toolbar.items ;
39515 delete config.toolbar.items ;
39519 this.toolbar.render(tool_el);
39520 for(var i =0;i < ti.length;i++) {
39521 // Roo.log(['add child', items[i]]);
39522 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39524 this.toolbar.items = nitems;
39526 delete config.toolbar;
39529 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39530 config.grid.scrollBody = true;;
39531 config.grid.monitorWindowResize = false; // turn off autosizing
39532 config.grid.autoHeight = false;
39533 config.grid.autoWidth = false;
39535 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39537 if (config.background) {
39538 // render grid on panel activation (if panel background)
39539 this.on('activate', function(gp) {
39540 if (!gp.grid.rendered) {
39541 gp.grid.render(this.wrapper);
39542 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39547 this.grid.render(this.wrapper);
39548 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39551 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39552 // ??? needed ??? config.el = this.wrapper;
39557 // xtype created footer. - not sure if will work as we normally have to render first..
39558 if (this.footer && !this.footer.el && this.footer.xtype) {
39560 var ctr = this.grid.getView().getFooterPanel(true);
39561 this.footer.dataSource = this.grid.dataSource;
39562 this.footer = Roo.factory(this.footer, Roo);
39563 this.footer.render(ctr);
39573 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39574 getId : function(){
39575 return this.grid.id;
39579 * Returns the grid for this panel
39580 * @return {Roo.bootstrap.Table}
39582 getGrid : function(){
39586 setSize : function(width, height){
39587 if(!this.ignoreResize(width, height)){
39588 var grid = this.grid;
39589 var size = this.adjustForComponents(width, height);
39590 // tfoot is not a footer?
39593 var gridel = grid.getGridEl();
39594 gridel.setSize(size.width, size.height);
39596 var tbd = grid.getGridEl().select('tbody', true).first();
39597 var thd = grid.getGridEl().select('thead',true).first();
39598 var tbf= grid.getGridEl().select('tfoot', true).first();
39601 size.height -= thd.getHeight();
39604 size.height -= thd.getHeight();
39607 tbd.setSize(size.width, size.height );
39608 // this is for the account management tab -seems to work there.
39609 var thd = grid.getGridEl().select('thead',true).first();
39611 // tbd.setSize(size.width, size.height - thd.getHeight());
39620 beforeSlide : function(){
39621 this.grid.getView().scroller.clip();
39624 afterSlide : function(){
39625 this.grid.getView().scroller.unclip();
39628 destroy : function(){
39629 this.grid.destroy();
39631 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
39636 * @class Roo.bootstrap.panel.Nest
39637 * @extends Roo.bootstrap.panel.Content
39639 * Create a new Panel, that can contain a layout.Border.
39642 * @param {Roo.BorderLayout} layout The layout for this panel
39643 * @param {String/Object} config A string to set only the title or a config object
39645 Roo.bootstrap.panel.Nest = function(config)
39647 // construct with only one argument..
39648 /* FIXME - implement nicer consturctors
39649 if (layout.layout) {
39651 layout = config.layout;
39652 delete config.layout;
39654 if (layout.xtype && !layout.getEl) {
39655 // then layout needs constructing..
39656 layout = Roo.factory(layout, Roo);
39660 config.el = config.layout.getEl();
39662 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39664 config.layout.monitorWindowResize = false; // turn off autosizing
39665 this.layout = config.layout;
39666 this.layout.getEl().addClass("roo-layout-nested-layout");
39667 this.layout.parent = this;
39674 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39676 setSize : function(width, height){
39677 if(!this.ignoreResize(width, height)){
39678 var size = this.adjustForComponents(width, height);
39679 var el = this.layout.getEl();
39680 if (size.height < 1) {
39681 el.setWidth(size.width);
39683 el.setSize(size.width, size.height);
39685 var touch = el.dom.offsetWidth;
39686 this.layout.layout();
39687 // ie requires a double layout on the first pass
39688 if(Roo.isIE && !this.initialized){
39689 this.initialized = true;
39690 this.layout.layout();
39695 // activate all subpanels if not currently active..
39697 setActiveState : function(active){
39698 this.active = active;
39699 this.setActiveClass(active);
39702 this.fireEvent("deactivate", this);
39706 this.fireEvent("activate", this);
39707 // not sure if this should happen before or after..
39708 if (!this.layout) {
39709 return; // should not happen..
39712 for (var r in this.layout.regions) {
39713 reg = this.layout.getRegion(r);
39714 if (reg.getActivePanel()) {
39715 //reg.showPanel(reg.getActivePanel()); // force it to activate..
39716 reg.setActivePanel(reg.getActivePanel());
39719 if (!reg.panels.length) {
39722 reg.showPanel(reg.getPanel(0));
39731 * Returns the nested BorderLayout for this panel
39732 * @return {Roo.BorderLayout}
39734 getLayout : function(){
39735 return this.layout;
39739 * Adds a xtype elements to the layout of the nested panel
39743 xtype : 'ContentPanel',
39750 xtype : 'NestedLayoutPanel',
39756 items : [ ... list of content panels or nested layout panels.. ]
39760 * @param {Object} cfg Xtype definition of item to add.
39762 addxtype : function(cfg) {
39763 return this.layout.addxtype(cfg);
39768 * Ext JS Library 1.1.1
39769 * Copyright(c) 2006-2007, Ext JS, LLC.
39771 * Originally Released Under LGPL - original licence link has changed is not relivant.
39774 * <script type="text/javascript">
39777 * @class Roo.TabPanel
39778 * @extends Roo.util.Observable
39779 * A lightweight tab container.
39783 // basic tabs 1, built from existing content
39784 var tabs = new Roo.TabPanel("tabs1");
39785 tabs.addTab("script", "View Script");
39786 tabs.addTab("markup", "View Markup");
39787 tabs.activate("script");
39789 // more advanced tabs, built from javascript
39790 var jtabs = new Roo.TabPanel("jtabs");
39791 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39793 // set up the UpdateManager
39794 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39795 var updater = tab2.getUpdateManager();
39796 updater.setDefaultUrl("ajax1.htm");
39797 tab2.on('activate', updater.refresh, updater, true);
39799 // Use setUrl for Ajax loading
39800 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39801 tab3.setUrl("ajax2.htm", null, true);
39804 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39807 jtabs.activate("jtabs-1");
39810 * Create a new TabPanel.
39811 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39812 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39814 Roo.bootstrap.panel.Tabs = function(config){
39816 * The container element for this TabPanel.
39817 * @type Roo.Element
39819 this.el = Roo.get(config.el);
39822 if(typeof config == "boolean"){
39823 this.tabPosition = config ? "bottom" : "top";
39825 Roo.apply(this, config);
39829 if(this.tabPosition == "bottom"){
39830 // if tabs are at the bottom = create the body first.
39831 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39832 this.el.addClass("roo-tabs-bottom");
39834 // next create the tabs holders
39836 if (this.tabPosition == "west"){
39838 var reg = this.region; // fake it..
39840 if (!reg.mgr.parent) {
39843 reg = reg.mgr.parent.region;
39845 Roo.log("got nest?");
39847 if (reg.mgr.getRegion('west')) {
39848 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39849 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39850 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39851 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39852 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39860 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39861 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39862 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39863 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39868 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39871 // finally - if tabs are at the top, then create the body last..
39872 if(this.tabPosition != "bottom"){
39873 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39874 * @type Roo.Element
39876 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39877 this.el.addClass("roo-tabs-top");
39881 this.bodyEl.setStyle("position", "relative");
39883 this.active = null;
39884 this.activateDelegate = this.activate.createDelegate(this);
39889 * Fires when the active tab changes
39890 * @param {Roo.TabPanel} this
39891 * @param {Roo.TabPanelItem} activePanel The new active tab
39895 * @event beforetabchange
39896 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39897 * @param {Roo.TabPanel} this
39898 * @param {Object} e Set cancel to true on this object to cancel the tab change
39899 * @param {Roo.TabPanelItem} tab The tab being changed to
39901 "beforetabchange" : true
39904 Roo.EventManager.onWindowResize(this.onResize, this);
39905 this.cpad = this.el.getPadding("lr");
39906 this.hiddenCount = 0;
39909 // toolbar on the tabbar support...
39910 if (this.toolbar) {
39911 alert("no toolbar support yet");
39912 this.toolbar = false;
39914 var tcfg = this.toolbar;
39915 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
39916 this.toolbar = new Roo.Toolbar(tcfg);
39917 if (Roo.isSafari) {
39918 var tbl = tcfg.container.child('table', true);
39919 tbl.setAttribute('width', '100%');
39927 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39930 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39932 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39934 tabPosition : "top",
39936 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39938 currentTabWidth : 0,
39940 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39944 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39948 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39950 preferredTabWidth : 175,
39952 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39954 resizeTabs : false,
39956 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39958 monitorResize : true,
39960 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
39962 toolbar : false, // set by caller..
39964 region : false, /// set by caller
39966 disableTooltips : true, // not used yet...
39969 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39970 * @param {String} id The id of the div to use <b>or create</b>
39971 * @param {String} text The text for the tab
39972 * @param {String} content (optional) Content to put in the TabPanelItem body
39973 * @param {Boolean} closable (optional) True to create a close icon on the tab
39974 * @return {Roo.TabPanelItem} The created TabPanelItem
39976 addTab : function(id, text, content, closable, tpl)
39978 var item = new Roo.bootstrap.panel.TabItem({
39982 closable : closable,
39985 this.addTabItem(item);
39987 item.setContent(content);
39993 * Returns the {@link Roo.TabPanelItem} with the specified id/index
39994 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39995 * @return {Roo.TabPanelItem}
39997 getTab : function(id){
39998 return this.items[id];
40002 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40003 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40005 hideTab : function(id){
40006 var t = this.items[id];
40009 this.hiddenCount++;
40010 this.autoSizeTabs();
40015 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40016 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40018 unhideTab : function(id){
40019 var t = this.items[id];
40021 t.setHidden(false);
40022 this.hiddenCount--;
40023 this.autoSizeTabs();
40028 * Adds an existing {@link Roo.TabPanelItem}.
40029 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40031 addTabItem : function(item)
40033 this.items[item.id] = item;
40034 this.items.push(item);
40035 this.autoSizeTabs();
40036 // if(this.resizeTabs){
40037 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40038 // this.autoSizeTabs();
40040 // item.autoSize();
40045 * Removes a {@link Roo.TabPanelItem}.
40046 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40048 removeTab : function(id){
40049 var items = this.items;
40050 var tab = items[id];
40051 if(!tab) { return; }
40052 var index = items.indexOf(tab);
40053 if(this.active == tab && items.length > 1){
40054 var newTab = this.getNextAvailable(index);
40059 this.stripEl.dom.removeChild(tab.pnode.dom);
40060 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40061 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40063 items.splice(index, 1);
40064 delete this.items[tab.id];
40065 tab.fireEvent("close", tab);
40066 tab.purgeListeners();
40067 this.autoSizeTabs();
40070 getNextAvailable : function(start){
40071 var items = this.items;
40073 // look for a next tab that will slide over to
40074 // replace the one being removed
40075 while(index < items.length){
40076 var item = items[++index];
40077 if(item && !item.isHidden()){
40081 // if one isn't found select the previous tab (on the left)
40084 var item = items[--index];
40085 if(item && !item.isHidden()){
40093 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40094 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40096 disableTab : function(id){
40097 var tab = this.items[id];
40098 if(tab && this.active != tab){
40104 * Enables a {@link Roo.TabPanelItem} that is disabled.
40105 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40107 enableTab : function(id){
40108 var tab = this.items[id];
40113 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40114 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40115 * @return {Roo.TabPanelItem} The TabPanelItem.
40117 activate : function(id)
40119 //Roo.log('activite:' + id);
40121 var tab = this.items[id];
40125 if(tab == this.active || tab.disabled){
40129 this.fireEvent("beforetabchange", this, e, tab);
40130 if(e.cancel !== true && !tab.disabled){
40132 this.active.hide();
40134 this.active = this.items[id];
40135 this.active.show();
40136 this.fireEvent("tabchange", this, this.active);
40142 * Gets the active {@link Roo.TabPanelItem}.
40143 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40145 getActiveTab : function(){
40146 return this.active;
40150 * Updates the tab body element to fit the height of the container element
40151 * for overflow scrolling
40152 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40154 syncHeight : function(targetHeight){
40155 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40156 var bm = this.bodyEl.getMargins();
40157 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40158 this.bodyEl.setHeight(newHeight);
40162 onResize : function(){
40163 if(this.monitorResize){
40164 this.autoSizeTabs();
40169 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40171 beginUpdate : function(){
40172 this.updating = true;
40176 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40178 endUpdate : function(){
40179 this.updating = false;
40180 this.autoSizeTabs();
40184 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40186 autoSizeTabs : function()
40188 var count = this.items.length;
40189 var vcount = count - this.hiddenCount;
40192 this.stripEl.hide();
40194 this.stripEl.show();
40197 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40202 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40203 var availWidth = Math.floor(w / vcount);
40204 var b = this.stripBody;
40205 if(b.getWidth() > w){
40206 var tabs = this.items;
40207 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40208 if(availWidth < this.minTabWidth){
40209 /*if(!this.sleft){ // incomplete scrolling code
40210 this.createScrollButtons();
40213 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40216 if(this.currentTabWidth < this.preferredTabWidth){
40217 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40223 * Returns the number of tabs in this TabPanel.
40226 getCount : function(){
40227 return this.items.length;
40231 * Resizes all the tabs to the passed width
40232 * @param {Number} The new width
40234 setTabWidth : function(width){
40235 this.currentTabWidth = width;
40236 for(var i = 0, len = this.items.length; i < len; i++) {
40237 if(!this.items[i].isHidden()) {
40238 this.items[i].setWidth(width);
40244 * Destroys this TabPanel
40245 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40247 destroy : function(removeEl){
40248 Roo.EventManager.removeResizeListener(this.onResize, this);
40249 for(var i = 0, len = this.items.length; i < len; i++){
40250 this.items[i].purgeListeners();
40252 if(removeEl === true){
40253 this.el.update("");
40258 createStrip : function(container)
40260 var strip = document.createElement("nav");
40261 strip.className = Roo.bootstrap.version == 4 ?
40262 "navbar-light bg-light" :
40263 "navbar navbar-default"; //"x-tabs-wrap";
40264 container.appendChild(strip);
40268 createStripList : function(strip)
40270 // div wrapper for retard IE
40271 // returns the "tr" element.
40272 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40273 //'<div class="x-tabs-strip-wrap">'+
40274 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40275 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40276 return strip.firstChild; //.firstChild.firstChild.firstChild;
40278 createBody : function(container)
40280 var body = document.createElement("div");
40281 Roo.id(body, "tab-body");
40282 //Roo.fly(body).addClass("x-tabs-body");
40283 Roo.fly(body).addClass("tab-content");
40284 container.appendChild(body);
40287 createItemBody :function(bodyEl, id){
40288 var body = Roo.getDom(id);
40290 body = document.createElement("div");
40293 //Roo.fly(body).addClass("x-tabs-item-body");
40294 Roo.fly(body).addClass("tab-pane");
40295 bodyEl.insertBefore(body, bodyEl.firstChild);
40299 createStripElements : function(stripEl, text, closable, tpl)
40301 var td = document.createElement("li"); // was td..
40302 td.className = 'nav-item';
40304 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40307 stripEl.appendChild(td);
40309 td.className = "x-tabs-closable";
40310 if(!this.closeTpl){
40311 this.closeTpl = new Roo.Template(
40312 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40313 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40314 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40317 var el = this.closeTpl.overwrite(td, {"text": text});
40318 var close = el.getElementsByTagName("div")[0];
40319 var inner = el.getElementsByTagName("em")[0];
40320 return {"el": el, "close": close, "inner": inner};
40323 // not sure what this is..
40324 // if(!this.tabTpl){
40325 //this.tabTpl = new Roo.Template(
40326 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40327 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40329 // this.tabTpl = new Roo.Template(
40330 // '<a href="#">' +
40331 // '<span unselectable="on"' +
40332 // (this.disableTooltips ? '' : ' title="{text}"') +
40333 // ' >{text}</span></a>'
40339 var template = tpl || this.tabTpl || false;
40342 template = new Roo.Template(
40343 Roo.bootstrap.version == 4 ?
40345 '<a class="nav-link" href="#" unselectable="on"' +
40346 (this.disableTooltips ? '' : ' title="{text}"') +
40349 '<a class="nav-link" href="#">' +
40350 '<span unselectable="on"' +
40351 (this.disableTooltips ? '' : ' title="{text}"') +
40352 ' >{text}</span></a>'
40357 switch (typeof(template)) {
40361 template = new Roo.Template(template);
40367 var el = template.overwrite(td, {"text": text});
40369 var inner = el.getElementsByTagName("span")[0];
40371 return {"el": el, "inner": inner};
40379 * @class Roo.TabPanelItem
40380 * @extends Roo.util.Observable
40381 * Represents an individual item (tab plus body) in a TabPanel.
40382 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40383 * @param {String} id The id of this TabPanelItem
40384 * @param {String} text The text for the tab of this TabPanelItem
40385 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40387 Roo.bootstrap.panel.TabItem = function(config){
40389 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40390 * @type Roo.TabPanel
40392 this.tabPanel = config.panel;
40394 * The id for this TabPanelItem
40397 this.id = config.id;
40399 this.disabled = false;
40401 this.text = config.text;
40403 this.loaded = false;
40404 this.closable = config.closable;
40407 * The body element for this TabPanelItem.
40408 * @type Roo.Element
40410 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40411 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40412 this.bodyEl.setStyle("display", "block");
40413 this.bodyEl.setStyle("zoom", "1");
40414 //this.hideAction();
40416 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40418 this.el = Roo.get(els.el);
40419 this.inner = Roo.get(els.inner, true);
40420 this.textEl = Roo.bootstrap.version == 4 ?
40421 this.el : Roo.get(this.el.dom.firstChild, true);
40423 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40424 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40427 // this.el.on("mousedown", this.onTabMouseDown, this);
40428 this.el.on("click", this.onTabClick, this);
40430 if(config.closable){
40431 var c = Roo.get(els.close, true);
40432 c.dom.title = this.closeText;
40433 c.addClassOnOver("close-over");
40434 c.on("click", this.closeClick, this);
40440 * Fires when this tab becomes the active tab.
40441 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40442 * @param {Roo.TabPanelItem} this
40446 * @event beforeclose
40447 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40448 * @param {Roo.TabPanelItem} this
40449 * @param {Object} e Set cancel to true on this object to cancel the close.
40451 "beforeclose": true,
40454 * Fires when this tab is closed.
40455 * @param {Roo.TabPanelItem} this
40459 * @event deactivate
40460 * Fires when this tab is no longer the active tab.
40461 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40462 * @param {Roo.TabPanelItem} this
40464 "deactivate" : true
40466 this.hidden = false;
40468 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40471 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40473 purgeListeners : function(){
40474 Roo.util.Observable.prototype.purgeListeners.call(this);
40475 this.el.removeAllListeners();
40478 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40481 this.status_node.addClass("active");
40484 this.tabPanel.stripWrap.repaint();
40486 this.fireEvent("activate", this.tabPanel, this);
40490 * Returns true if this tab is the active tab.
40491 * @return {Boolean}
40493 isActive : function(){
40494 return this.tabPanel.getActiveTab() == this;
40498 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40501 this.status_node.removeClass("active");
40503 this.fireEvent("deactivate", this.tabPanel, this);
40506 hideAction : function(){
40507 this.bodyEl.hide();
40508 this.bodyEl.setStyle("position", "absolute");
40509 this.bodyEl.setLeft("-20000px");
40510 this.bodyEl.setTop("-20000px");
40513 showAction : function(){
40514 this.bodyEl.setStyle("position", "relative");
40515 this.bodyEl.setTop("");
40516 this.bodyEl.setLeft("");
40517 this.bodyEl.show();
40521 * Set the tooltip for the tab.
40522 * @param {String} tooltip The tab's tooltip
40524 setTooltip : function(text){
40525 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40526 this.textEl.dom.qtip = text;
40527 this.textEl.dom.removeAttribute('title');
40529 this.textEl.dom.title = text;
40533 onTabClick : function(e){
40534 e.preventDefault();
40535 this.tabPanel.activate(this.id);
40538 onTabMouseDown : function(e){
40539 e.preventDefault();
40540 this.tabPanel.activate(this.id);
40543 getWidth : function(){
40544 return this.inner.getWidth();
40547 setWidth : function(width){
40548 var iwidth = width - this.linode.getPadding("lr");
40549 this.inner.setWidth(iwidth);
40550 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40551 this.linode.setWidth(width);
40555 * Show or hide the tab
40556 * @param {Boolean} hidden True to hide or false to show.
40558 setHidden : function(hidden){
40559 this.hidden = hidden;
40560 this.linode.setStyle("display", hidden ? "none" : "");
40564 * Returns true if this tab is "hidden"
40565 * @return {Boolean}
40567 isHidden : function(){
40568 return this.hidden;
40572 * Returns the text for this tab
40575 getText : function(){
40579 autoSize : function(){
40580 //this.el.beginMeasure();
40581 this.textEl.setWidth(1);
40583 * #2804 [new] Tabs in Roojs
40584 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40586 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40587 //this.el.endMeasure();
40591 * Sets the text for the tab (Note: this also sets the tooltip text)
40592 * @param {String} text The tab's text and tooltip
40594 setText : function(text){
40596 this.textEl.update(text);
40597 this.setTooltip(text);
40598 //if(!this.tabPanel.resizeTabs){
40599 // this.autoSize();
40603 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40605 activate : function(){
40606 this.tabPanel.activate(this.id);
40610 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40612 disable : function(){
40613 if(this.tabPanel.active != this){
40614 this.disabled = true;
40615 this.status_node.addClass("disabled");
40620 * Enables this TabPanelItem if it was previously disabled.
40622 enable : function(){
40623 this.disabled = false;
40624 this.status_node.removeClass("disabled");
40628 * Sets the content for this TabPanelItem.
40629 * @param {String} content The content
40630 * @param {Boolean} loadScripts true to look for and load scripts
40632 setContent : function(content, loadScripts){
40633 this.bodyEl.update(content, loadScripts);
40637 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40638 * @return {Roo.UpdateManager} The UpdateManager
40640 getUpdateManager : function(){
40641 return this.bodyEl.getUpdateManager();
40645 * Set a URL to be used to load the content for this TabPanelItem.
40646 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40647 * @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)
40648 * @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)
40649 * @return {Roo.UpdateManager} The UpdateManager
40651 setUrl : function(url, params, loadOnce){
40652 if(this.refreshDelegate){
40653 this.un('activate', this.refreshDelegate);
40655 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40656 this.on("activate", this.refreshDelegate);
40657 return this.bodyEl.getUpdateManager();
40661 _handleRefresh : function(url, params, loadOnce){
40662 if(!loadOnce || !this.loaded){
40663 var updater = this.bodyEl.getUpdateManager();
40664 updater.update(url, params, this._setLoaded.createDelegate(this));
40669 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40670 * Will fail silently if the setUrl method has not been called.
40671 * This does not activate the panel, just updates its content.
40673 refresh : function(){
40674 if(this.refreshDelegate){
40675 this.loaded = false;
40676 this.refreshDelegate();
40681 _setLoaded : function(){
40682 this.loaded = true;
40686 closeClick : function(e){
40689 this.fireEvent("beforeclose", this, o);
40690 if(o.cancel !== true){
40691 this.tabPanel.removeTab(this.id);
40695 * The text displayed in the tooltip for the close icon.
40698 closeText : "Close this tab"
40701 * This script refer to:
40702 * Title: International Telephone Input
40703 * Author: Jack O'Connor
40704 * Code version: v12.1.12
40705 * Availability: https://github.com/jackocnr/intl-tel-input.git
40708 Roo.bootstrap.PhoneInputData = function() {
40711 "Afghanistan (افغانستان)",
40716 "Albania (Shqipëri)",
40721 "Algeria (الجزائر)",
40746 "Antigua and Barbuda",
40756 "Armenia (Հայաստան)",
40772 "Austria (Österreich)",
40777 "Azerbaijan (Azərbaycan)",
40787 "Bahrain (البحرين)",
40792 "Bangladesh (বাংলাদেশ)",
40802 "Belarus (Беларусь)",
40807 "Belgium (België)",
40837 "Bosnia and Herzegovina (Босна и Херцеговина)",
40852 "British Indian Ocean Territory",
40857 "British Virgin Islands",
40867 "Bulgaria (България)",
40877 "Burundi (Uburundi)",
40882 "Cambodia (កម្ពុជា)",
40887 "Cameroon (Cameroun)",
40896 ["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"]
40899 "Cape Verde (Kabu Verdi)",
40904 "Caribbean Netherlands",
40915 "Central African Republic (République centrafricaine)",
40935 "Christmas Island",
40941 "Cocos (Keeling) Islands",
40952 "Comoros (جزر القمر)",
40957 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40962 "Congo (Republic) (Congo-Brazzaville)",
40982 "Croatia (Hrvatska)",
41003 "Czech Republic (Česká republika)",
41008 "Denmark (Danmark)",
41023 "Dominican Republic (República Dominicana)",
41027 ["809", "829", "849"]
41045 "Equatorial Guinea (Guinea Ecuatorial)",
41065 "Falkland Islands (Islas Malvinas)",
41070 "Faroe Islands (Føroyar)",
41091 "French Guiana (Guyane française)",
41096 "French Polynesia (Polynésie française)",
41111 "Georgia (საქართველო)",
41116 "Germany (Deutschland)",
41136 "Greenland (Kalaallit Nunaat)",
41173 "Guinea-Bissau (Guiné Bissau)",
41198 "Hungary (Magyarország)",
41203 "Iceland (Ísland)",
41223 "Iraq (العراق)",
41239 "Israel (ישראל)",
41266 "Jordan (الأردن)",
41271 "Kazakhstan (Казахстан)",
41292 "Kuwait (الكويت)",
41297 "Kyrgyzstan (Кыргызстан)",
41307 "Latvia (Latvija)",
41312 "Lebanon (لبنان)",
41327 "Libya (ليبيا)",
41337 "Lithuania (Lietuva)",
41352 "Macedonia (FYROM) (Македонија)",
41357 "Madagascar (Madagasikara)",
41387 "Marshall Islands",
41397 "Mauritania (موريتانيا)",
41402 "Mauritius (Moris)",
41423 "Moldova (Republica Moldova)",
41433 "Mongolia (Монгол)",
41438 "Montenegro (Crna Gora)",
41448 "Morocco (المغرب)",
41454 "Mozambique (Moçambique)",
41459 "Myanmar (Burma) (မြန်မာ)",
41464 "Namibia (Namibië)",
41479 "Netherlands (Nederland)",
41484 "New Caledonia (Nouvelle-Calédonie)",
41519 "North Korea (조선 민주주의 인민 공화국)",
41524 "Northern Mariana Islands",
41540 "Pakistan (پاکستان)",
41550 "Palestine (فلسطين)",
41560 "Papua New Guinea",
41602 "Réunion (La Réunion)",
41608 "Romania (România)",
41624 "Saint Barthélemy",
41635 "Saint Kitts and Nevis",
41645 "Saint Martin (Saint-Martin (partie française))",
41651 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41656 "Saint Vincent and the Grenadines",
41671 "São Tomé and Príncipe (São Tomé e Príncipe)",
41676 "Saudi Arabia (المملكة العربية السعودية)",
41681 "Senegal (Sénégal)",
41711 "Slovakia (Slovensko)",
41716 "Slovenia (Slovenija)",
41726 "Somalia (Soomaaliya)",
41736 "South Korea (대한민국)",
41741 "South Sudan (جنوب السودان)",
41751 "Sri Lanka (ශ්රී ලංකාව)",
41756 "Sudan (السودان)",
41766 "Svalbard and Jan Mayen",
41777 "Sweden (Sverige)",
41782 "Switzerland (Schweiz)",
41787 "Syria (سوريا)",
41832 "Trinidad and Tobago",
41837 "Tunisia (تونس)",
41842 "Turkey (Türkiye)",
41852 "Turks and Caicos Islands",
41862 "U.S. Virgin Islands",
41872 "Ukraine (Україна)",
41877 "United Arab Emirates (الإمارات العربية المتحدة)",
41899 "Uzbekistan (Oʻzbekiston)",
41909 "Vatican City (Città del Vaticano)",
41920 "Vietnam (Việt Nam)",
41925 "Wallis and Futuna (Wallis-et-Futuna)",
41930 "Western Sahara (الصحراء الغربية)",
41936 "Yemen (اليمن)",
41960 * This script refer to:
41961 * Title: International Telephone Input
41962 * Author: Jack O'Connor
41963 * Code version: v12.1.12
41964 * Availability: https://github.com/jackocnr/intl-tel-input.git
41968 * @class Roo.bootstrap.PhoneInput
41969 * @extends Roo.bootstrap.TriggerField
41970 * An input with International dial-code selection
41972 * @cfg {String} defaultDialCode default '+852'
41973 * @cfg {Array} preferedCountries default []
41976 * Create a new PhoneInput.
41977 * @param {Object} config Configuration options
41980 Roo.bootstrap.PhoneInput = function(config) {
41981 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41984 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41986 listWidth: undefined,
41988 selectedClass: 'active',
41990 invalidClass : "has-warning",
41992 validClass: 'has-success',
41994 allowed: '0123456789',
41999 * @cfg {String} defaultDialCode The default dial code when initializing the input
42001 defaultDialCode: '+852',
42004 * @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
42006 preferedCountries: false,
42008 getAutoCreate : function()
42010 var data = Roo.bootstrap.PhoneInputData();
42011 var align = this.labelAlign || this.parentLabelAlign();
42014 this.allCountries = [];
42015 this.dialCodeMapping = [];
42017 for (var i = 0; i < data.length; i++) {
42019 this.allCountries[i] = {
42023 priority: c[3] || 0,
42024 areaCodes: c[4] || null
42026 this.dialCodeMapping[c[2]] = {
42029 priority: c[3] || 0,
42030 areaCodes: c[4] || null
42042 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42043 maxlength: this.max_length,
42044 cls : 'form-control tel-input',
42045 autocomplete: 'new-password'
42048 var hiddenInput = {
42051 cls: 'hidden-tel-input'
42055 hiddenInput.name = this.name;
42058 if (this.disabled) {
42059 input.disabled = true;
42062 var flag_container = {
42079 cls: this.hasFeedback ? 'has-feedback' : '',
42085 cls: 'dial-code-holder',
42092 cls: 'roo-select2-container input-group',
42099 if (this.fieldLabel.length) {
42102 tooltip: 'This field is required'
42108 cls: 'control-label',
42114 html: this.fieldLabel
42117 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42123 if(this.indicatorpos == 'right') {
42124 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42131 if(align == 'left') {
42139 if(this.labelWidth > 12){
42140 label.style = "width: " + this.labelWidth + 'px';
42142 if(this.labelWidth < 13 && this.labelmd == 0){
42143 this.labelmd = this.labelWidth;
42145 if(this.labellg > 0){
42146 label.cls += ' col-lg-' + this.labellg;
42147 input.cls += ' col-lg-' + (12 - this.labellg);
42149 if(this.labelmd > 0){
42150 label.cls += ' col-md-' + this.labelmd;
42151 container.cls += ' col-md-' + (12 - this.labelmd);
42153 if(this.labelsm > 0){
42154 label.cls += ' col-sm-' + this.labelsm;
42155 container.cls += ' col-sm-' + (12 - this.labelsm);
42157 if(this.labelxs > 0){
42158 label.cls += ' col-xs-' + this.labelxs;
42159 container.cls += ' col-xs-' + (12 - this.labelxs);
42169 var settings = this;
42171 ['xs','sm','md','lg'].map(function(size){
42172 if (settings[size]) {
42173 cfg.cls += ' col-' + size + '-' + settings[size];
42177 this.store = new Roo.data.Store({
42178 proxy : new Roo.data.MemoryProxy({}),
42179 reader : new Roo.data.JsonReader({
42190 'name' : 'dialCode',
42194 'name' : 'priority',
42198 'name' : 'areaCodes',
42205 if(!this.preferedCountries) {
42206 this.preferedCountries = [
42213 var p = this.preferedCountries.reverse();
42216 for (var i = 0; i < p.length; i++) {
42217 for (var j = 0; j < this.allCountries.length; j++) {
42218 if(this.allCountries[j].iso2 == p[i]) {
42219 var t = this.allCountries[j];
42220 this.allCountries.splice(j,1);
42221 this.allCountries.unshift(t);
42227 this.store.proxy.data = {
42229 data: this.allCountries
42235 initEvents : function()
42238 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42240 this.indicator = this.indicatorEl();
42241 this.flag = this.flagEl();
42242 this.dialCodeHolder = this.dialCodeHolderEl();
42244 this.trigger = this.el.select('div.flag-box',true).first();
42245 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42250 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42251 _this.list.setWidth(lw);
42254 this.list.on('mouseover', this.onViewOver, this);
42255 this.list.on('mousemove', this.onViewMove, this);
42256 this.inputEl().on("keyup", this.onKeyUp, this);
42257 this.inputEl().on("keypress", this.onKeyPress, this);
42259 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42261 this.view = new Roo.View(this.list, this.tpl, {
42262 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42265 this.view.on('click', this.onViewClick, this);
42266 this.setValue(this.defaultDialCode);
42269 onTriggerClick : function(e)
42271 Roo.log('trigger click');
42276 if(this.isExpanded()){
42278 this.hasFocus = false;
42280 this.store.load({});
42281 this.hasFocus = true;
42286 isExpanded : function()
42288 return this.list.isVisible();
42291 collapse : function()
42293 if(!this.isExpanded()){
42297 Roo.get(document).un('mousedown', this.collapseIf, this);
42298 Roo.get(document).un('mousewheel', this.collapseIf, this);
42299 this.fireEvent('collapse', this);
42303 expand : function()
42307 if(this.isExpanded() || !this.hasFocus){
42311 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42312 this.list.setWidth(lw);
42315 this.restrictHeight();
42317 Roo.get(document).on('mousedown', this.collapseIf, this);
42318 Roo.get(document).on('mousewheel', this.collapseIf, this);
42320 this.fireEvent('expand', this);
42323 restrictHeight : function()
42325 this.list.alignTo(this.inputEl(), this.listAlign);
42326 this.list.alignTo(this.inputEl(), this.listAlign);
42329 onViewOver : function(e, t)
42331 if(this.inKeyMode){
42334 var item = this.view.findItemFromChild(t);
42337 var index = this.view.indexOf(item);
42338 this.select(index, false);
42343 onViewClick : function(view, doFocus, el, e)
42345 var index = this.view.getSelectedIndexes()[0];
42347 var r = this.store.getAt(index);
42350 this.onSelect(r, index);
42352 if(doFocus !== false && !this.blockFocus){
42353 this.inputEl().focus();
42357 onViewMove : function(e, t)
42359 this.inKeyMode = false;
42362 select : function(index, scrollIntoView)
42364 this.selectedIndex = index;
42365 this.view.select(index);
42366 if(scrollIntoView !== false){
42367 var el = this.view.getNode(index);
42369 this.list.scrollChildIntoView(el, false);
42374 createList : function()
42376 this.list = Roo.get(document.body).createChild({
42378 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42379 style: 'display:none'
42382 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42385 collapseIf : function(e)
42387 var in_combo = e.within(this.el);
42388 var in_list = e.within(this.list);
42389 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42391 if (in_combo || in_list || is_list) {
42397 onSelect : function(record, index)
42399 if(this.fireEvent('beforeselect', this, record, index) !== false){
42401 this.setFlagClass(record.data.iso2);
42402 this.setDialCode(record.data.dialCode);
42403 this.hasFocus = false;
42405 this.fireEvent('select', this, record, index);
42409 flagEl : function()
42411 var flag = this.el.select('div.flag',true).first();
42418 dialCodeHolderEl : function()
42420 var d = this.el.select('input.dial-code-holder',true).first();
42427 setDialCode : function(v)
42429 this.dialCodeHolder.dom.value = '+'+v;
42432 setFlagClass : function(n)
42434 this.flag.dom.className = 'flag '+n;
42437 getValue : function()
42439 var v = this.inputEl().getValue();
42440 if(this.dialCodeHolder) {
42441 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42446 setValue : function(v)
42448 var d = this.getDialCode(v);
42450 //invalid dial code
42451 if(v.length == 0 || !d || d.length == 0) {
42453 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42454 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42460 this.setFlagClass(this.dialCodeMapping[d].iso2);
42461 this.setDialCode(d);
42462 this.inputEl().dom.value = v.replace('+'+d,'');
42463 this.hiddenEl().dom.value = this.getValue();
42468 getDialCode : function(v)
42472 if (v.length == 0) {
42473 return this.dialCodeHolder.dom.value;
42477 if (v.charAt(0) != "+") {
42480 var numericChars = "";
42481 for (var i = 1; i < v.length; i++) {
42482 var c = v.charAt(i);
42485 if (this.dialCodeMapping[numericChars]) {
42486 dialCode = v.substr(1, i);
42488 if (numericChars.length == 4) {
42498 this.setValue(this.defaultDialCode);
42502 hiddenEl : function()
42504 return this.el.select('input.hidden-tel-input',true).first();
42507 // after setting val
42508 onKeyUp : function(e){
42509 this.setValue(this.getValue());
42512 onKeyPress : function(e){
42513 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42520 * @class Roo.bootstrap.MoneyField
42521 * @extends Roo.bootstrap.ComboBox
42522 * Bootstrap MoneyField class
42525 * Create a new MoneyField.
42526 * @param {Object} config Configuration options
42529 Roo.bootstrap.MoneyField = function(config) {
42531 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42535 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42538 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42540 allowDecimals : true,
42542 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42544 decimalSeparator : ".",
42546 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42548 decimalPrecision : 0,
42550 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42552 allowNegative : true,
42554 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42558 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42560 minValue : Number.NEGATIVE_INFINITY,
42562 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42564 maxValue : Number.MAX_VALUE,
42566 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42568 minText : "The minimum value for this field is {0}",
42570 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42572 maxText : "The maximum value for this field is {0}",
42574 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
42575 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42577 nanText : "{0} is not a valid number",
42579 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42583 * @cfg {String} defaults currency of the MoneyField
42584 * value should be in lkey
42586 defaultCurrency : false,
42588 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42590 thousandsDelimiter : false,
42592 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42603 getAutoCreate : function()
42605 var align = this.labelAlign || this.parentLabelAlign();
42617 cls : 'form-control roo-money-amount-input',
42618 autocomplete: 'new-password'
42621 var hiddenInput = {
42625 cls: 'hidden-number-input'
42628 if(this.max_length) {
42629 input.maxlength = this.max_length;
42633 hiddenInput.name = this.name;
42636 if (this.disabled) {
42637 input.disabled = true;
42640 var clg = 12 - this.inputlg;
42641 var cmd = 12 - this.inputmd;
42642 var csm = 12 - this.inputsm;
42643 var cxs = 12 - this.inputxs;
42647 cls : 'row roo-money-field',
42651 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42655 cls: 'roo-select2-container input-group',
42659 cls : 'form-control roo-money-currency-input',
42660 autocomplete: 'new-password',
42662 name : this.currencyName
42666 cls : 'input-group-addon',
42680 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42684 cls: this.hasFeedback ? 'has-feedback' : '',
42695 if (this.fieldLabel.length) {
42698 tooltip: 'This field is required'
42704 cls: 'control-label',
42710 html: this.fieldLabel
42713 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42719 if(this.indicatorpos == 'right') {
42720 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42727 if(align == 'left') {
42735 if(this.labelWidth > 12){
42736 label.style = "width: " + this.labelWidth + 'px';
42738 if(this.labelWidth < 13 && this.labelmd == 0){
42739 this.labelmd = this.labelWidth;
42741 if(this.labellg > 0){
42742 label.cls += ' col-lg-' + this.labellg;
42743 input.cls += ' col-lg-' + (12 - this.labellg);
42745 if(this.labelmd > 0){
42746 label.cls += ' col-md-' + this.labelmd;
42747 container.cls += ' col-md-' + (12 - this.labelmd);
42749 if(this.labelsm > 0){
42750 label.cls += ' col-sm-' + this.labelsm;
42751 container.cls += ' col-sm-' + (12 - this.labelsm);
42753 if(this.labelxs > 0){
42754 label.cls += ' col-xs-' + this.labelxs;
42755 container.cls += ' col-xs-' + (12 - this.labelxs);
42766 var settings = this;
42768 ['xs','sm','md','lg'].map(function(size){
42769 if (settings[size]) {
42770 cfg.cls += ' col-' + size + '-' + settings[size];
42777 initEvents : function()
42779 this.indicator = this.indicatorEl();
42781 this.initCurrencyEvent();
42783 this.initNumberEvent();
42786 initCurrencyEvent : function()
42789 throw "can not find store for combo";
42792 this.store = Roo.factory(this.store, Roo.data);
42793 this.store.parent = this;
42797 this.triggerEl = this.el.select('.input-group-addon', true).first();
42799 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42804 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42805 _this.list.setWidth(lw);
42808 this.list.on('mouseover', this.onViewOver, this);
42809 this.list.on('mousemove', this.onViewMove, this);
42810 this.list.on('scroll', this.onViewScroll, this);
42813 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42816 this.view = new Roo.View(this.list, this.tpl, {
42817 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42820 this.view.on('click', this.onViewClick, this);
42822 this.store.on('beforeload', this.onBeforeLoad, this);
42823 this.store.on('load', this.onLoad, this);
42824 this.store.on('loadexception', this.onLoadException, this);
42826 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42827 "up" : function(e){
42828 this.inKeyMode = true;
42832 "down" : function(e){
42833 if(!this.isExpanded()){
42834 this.onTriggerClick();
42836 this.inKeyMode = true;
42841 "enter" : function(e){
42844 if(this.fireEvent("specialkey", this, e)){
42845 this.onViewClick(false);
42851 "esc" : function(e){
42855 "tab" : function(e){
42858 if(this.fireEvent("specialkey", this, e)){
42859 this.onViewClick(false);
42867 doRelay : function(foo, bar, hname){
42868 if(hname == 'down' || this.scope.isExpanded()){
42869 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42877 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42881 initNumberEvent : function(e)
42883 this.inputEl().on("keydown" , this.fireKey, this);
42884 this.inputEl().on("focus", this.onFocus, this);
42885 this.inputEl().on("blur", this.onBlur, this);
42887 this.inputEl().relayEvent('keyup', this);
42889 if(this.indicator){
42890 this.indicator.addClass('invisible');
42893 this.originalValue = this.getValue();
42895 if(this.validationEvent == 'keyup'){
42896 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42897 this.inputEl().on('keyup', this.filterValidation, this);
42899 else if(this.validationEvent !== false){
42900 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42903 if(this.selectOnFocus){
42904 this.on("focus", this.preFocus, this);
42907 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42908 this.inputEl().on("keypress", this.filterKeys, this);
42910 this.inputEl().relayEvent('keypress', this);
42913 var allowed = "0123456789";
42915 if(this.allowDecimals){
42916 allowed += this.decimalSeparator;
42919 if(this.allowNegative){
42923 if(this.thousandsDelimiter) {
42927 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42929 var keyPress = function(e){
42931 var k = e.getKey();
42933 var c = e.getCharCode();
42936 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42937 allowed.indexOf(String.fromCharCode(c)) === -1
42943 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42947 if(allowed.indexOf(String.fromCharCode(c)) === -1){
42952 this.inputEl().on("keypress", keyPress, this);
42956 onTriggerClick : function(e)
42963 this.loadNext = false;
42965 if(this.isExpanded()){
42970 this.hasFocus = true;
42972 if(this.triggerAction == 'all') {
42973 this.doQuery(this.allQuery, true);
42977 this.doQuery(this.getRawValue());
42980 getCurrency : function()
42982 var v = this.currencyEl().getValue();
42987 restrictHeight : function()
42989 this.list.alignTo(this.currencyEl(), this.listAlign);
42990 this.list.alignTo(this.currencyEl(), this.listAlign);
42993 onViewClick : function(view, doFocus, el, e)
42995 var index = this.view.getSelectedIndexes()[0];
42997 var r = this.store.getAt(index);
43000 this.onSelect(r, index);
43004 onSelect : function(record, index){
43006 if(this.fireEvent('beforeselect', this, record, index) !== false){
43008 this.setFromCurrencyData(index > -1 ? record.data : false);
43012 this.fireEvent('select', this, record, index);
43016 setFromCurrencyData : function(o)
43020 this.lastCurrency = o;
43022 if (this.currencyField) {
43023 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43025 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43028 this.lastSelectionText = currency;
43030 //setting default currency
43031 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43032 this.setCurrency(this.defaultCurrency);
43036 this.setCurrency(currency);
43039 setFromData : function(o)
43043 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43045 this.setFromCurrencyData(c);
43050 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43052 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43055 this.setValue(value);
43059 setCurrency : function(v)
43061 this.currencyValue = v;
43064 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43069 setValue : function(v)
43071 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43077 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43079 this.inputEl().dom.value = (v == '') ? '' :
43080 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43082 if(!this.allowZero && v === '0') {
43083 this.hiddenEl().dom.value = '';
43084 this.inputEl().dom.value = '';
43091 getRawValue : function()
43093 var v = this.inputEl().getValue();
43098 getValue : function()
43100 return this.fixPrecision(this.parseValue(this.getRawValue()));
43103 parseValue : function(value)
43105 if(this.thousandsDelimiter) {
43107 r = new RegExp(",", "g");
43108 value = value.replace(r, "");
43111 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43112 return isNaN(value) ? '' : value;
43116 fixPrecision : function(value)
43118 if(this.thousandsDelimiter) {
43120 r = new RegExp(",", "g");
43121 value = value.replace(r, "");
43124 var nan = isNaN(value);
43126 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43127 return nan ? '' : value;
43129 return parseFloat(value).toFixed(this.decimalPrecision);
43132 decimalPrecisionFcn : function(v)
43134 return Math.floor(v);
43137 validateValue : function(value)
43139 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43143 var num = this.parseValue(value);
43146 this.markInvalid(String.format(this.nanText, value));
43150 if(num < this.minValue){
43151 this.markInvalid(String.format(this.minText, this.minValue));
43155 if(num > this.maxValue){
43156 this.markInvalid(String.format(this.maxText, this.maxValue));
43163 validate : function()
43165 if(this.disabled || this.allowBlank){
43170 var currency = this.getCurrency();
43172 if(this.validateValue(this.getRawValue()) && currency.length){
43177 this.markInvalid();
43181 getName: function()
43186 beforeBlur : function()
43192 var v = this.parseValue(this.getRawValue());
43199 onBlur : function()
43203 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43204 //this.el.removeClass(this.focusClass);
43207 this.hasFocus = false;
43209 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43213 var v = this.getValue();
43215 if(String(v) !== String(this.startValue)){
43216 this.fireEvent('change', this, v, this.startValue);
43219 this.fireEvent("blur", this);
43222 inputEl : function()
43224 return this.el.select('.roo-money-amount-input', true).first();
43227 currencyEl : function()
43229 return this.el.select('.roo-money-currency-input', true).first();
43232 hiddenEl : function()
43234 return this.el.select('input.hidden-number-input',true).first();
43238 * @class Roo.bootstrap.BezierSignature
43239 * @extends Roo.bootstrap.Component
43240 * Bootstrap BezierSignature class
43241 * This script refer to:
43242 * Title: Signature Pad
43244 * Availability: https://github.com/szimek/signature_pad
43247 * Create a new BezierSignature
43248 * @param {Object} config The config object
43251 Roo.bootstrap.BezierSignature = function(config){
43252 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43258 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43265 mouse_btn_down: true,
43268 * @cfg {int} canvas height
43270 canvas_height: '200px',
43273 * @cfg {float|function} Radius of a single dot.
43278 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43283 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43288 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43293 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43298 * @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.
43300 bg_color: 'rgba(0, 0, 0, 0)',
43303 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43305 dot_color: 'black',
43308 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43310 velocity_filter_weight: 0.7,
43313 * @cfg {function} Callback when stroke begin.
43318 * @cfg {function} Callback when stroke end.
43322 getAutoCreate : function()
43324 var cls = 'roo-signature column';
43327 cls += ' ' + this.cls;
43337 for(var i = 0; i < col_sizes.length; i++) {
43338 if(this[col_sizes[i]]) {
43339 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43349 cls: 'roo-signature-body',
43353 cls: 'roo-signature-body-canvas',
43354 height: this.canvas_height,
43355 width: this.canvas_width
43362 style: 'display: none'
43370 initEvents: function()
43372 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43374 var canvas = this.canvasEl();
43376 // mouse && touch event swapping...
43377 canvas.dom.style.touchAction = 'none';
43378 canvas.dom.style.msTouchAction = 'none';
43380 this.mouse_btn_down = false;
43381 canvas.on('mousedown', this._handleMouseDown, this);
43382 canvas.on('mousemove', this._handleMouseMove, this);
43383 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43385 if (window.PointerEvent) {
43386 canvas.on('pointerdown', this._handleMouseDown, this);
43387 canvas.on('pointermove', this._handleMouseMove, this);
43388 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43391 if ('ontouchstart' in window) {
43392 canvas.on('touchstart', this._handleTouchStart, this);
43393 canvas.on('touchmove', this._handleTouchMove, this);
43394 canvas.on('touchend', this._handleTouchEnd, this);
43397 Roo.EventManager.onWindowResize(this.resize, this, true);
43399 // file input event
43400 this.fileEl().on('change', this.uploadImage, this);
43407 resize: function(){
43409 var canvas = this.canvasEl().dom;
43410 var ctx = this.canvasElCtx();
43411 var img_data = false;
43413 if(canvas.width > 0) {
43414 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43416 // setting canvas width will clean img data
43419 var style = window.getComputedStyle ?
43420 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43422 var padding_left = parseInt(style.paddingLeft) || 0;
43423 var padding_right = parseInt(style.paddingRight) || 0;
43425 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43428 ctx.putImageData(img_data, 0, 0);
43432 _handleMouseDown: function(e)
43434 if (e.browserEvent.which === 1) {
43435 this.mouse_btn_down = true;
43436 this.strokeBegin(e);
43440 _handleMouseMove: function (e)
43442 if (this.mouse_btn_down) {
43443 this.strokeMoveUpdate(e);
43447 _handleMouseUp: function (e)
43449 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43450 this.mouse_btn_down = false;
43455 _handleTouchStart: function (e) {
43457 e.preventDefault();
43458 if (e.browserEvent.targetTouches.length === 1) {
43459 // var touch = e.browserEvent.changedTouches[0];
43460 // this.strokeBegin(touch);
43462 this.strokeBegin(e); // assume e catching the correct xy...
43466 _handleTouchMove: function (e) {
43467 e.preventDefault();
43468 // var touch = event.targetTouches[0];
43469 // _this._strokeMoveUpdate(touch);
43470 this.strokeMoveUpdate(e);
43473 _handleTouchEnd: function (e) {
43474 var wasCanvasTouched = e.target === this.canvasEl().dom;
43475 if (wasCanvasTouched) {
43476 e.preventDefault();
43477 // var touch = event.changedTouches[0];
43478 // _this._strokeEnd(touch);
43483 reset: function () {
43484 this._lastPoints = [];
43485 this._lastVelocity = 0;
43486 this._lastWidth = (this.min_width + this.max_width) / 2;
43487 this.canvasElCtx().fillStyle = this.dot_color;
43490 strokeMoveUpdate: function(e)
43492 this.strokeUpdate(e);
43494 if (this.throttle) {
43495 this.throttleStroke(this.strokeUpdate, this.throttle);
43498 this.strokeUpdate(e);
43502 strokeBegin: function(e)
43504 var newPointGroup = {
43505 color: this.dot_color,
43509 if (typeof this.onBegin === 'function') {
43513 this.curve_data.push(newPointGroup);
43515 this.strokeUpdate(e);
43518 strokeUpdate: function(e)
43520 var rect = this.canvasEl().dom.getBoundingClientRect();
43521 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43522 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43523 var lastPoints = lastPointGroup.points;
43524 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43525 var isLastPointTooClose = lastPoint
43526 ? point.distanceTo(lastPoint) <= this.min_distance
43528 var color = lastPointGroup.color;
43529 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43530 var curve = this.addPoint(point);
43532 this.drawDot({color: color, point: point});
43535 this.drawCurve({color: color, curve: curve});
43545 strokeEnd: function(e)
43547 this.strokeUpdate(e);
43548 if (typeof this.onEnd === 'function') {
43553 addPoint: function (point) {
43554 var _lastPoints = this._lastPoints;
43555 _lastPoints.push(point);
43556 if (_lastPoints.length > 2) {
43557 if (_lastPoints.length === 3) {
43558 _lastPoints.unshift(_lastPoints[0]);
43560 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43561 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43562 _lastPoints.shift();
43568 calculateCurveWidths: function (startPoint, endPoint) {
43569 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43570 (1 - this.velocity_filter_weight) * this._lastVelocity;
43572 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43575 start: this._lastWidth
43578 this._lastVelocity = velocity;
43579 this._lastWidth = newWidth;
43583 drawDot: function (_a) {
43584 var color = _a.color, point = _a.point;
43585 var ctx = this.canvasElCtx();
43586 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43588 this.drawCurveSegment(point.x, point.y, width);
43590 ctx.fillStyle = color;
43594 drawCurve: function (_a) {
43595 var color = _a.color, curve = _a.curve;
43596 var ctx = this.canvasElCtx();
43597 var widthDelta = curve.endWidth - curve.startWidth;
43598 var drawSteps = Math.floor(curve.length()) * 2;
43600 ctx.fillStyle = color;
43601 for (var i = 0; i < drawSteps; i += 1) {
43602 var t = i / drawSteps;
43608 var x = uuu * curve.startPoint.x;
43609 x += 3 * uu * t * curve.control1.x;
43610 x += 3 * u * tt * curve.control2.x;
43611 x += ttt * curve.endPoint.x;
43612 var y = uuu * curve.startPoint.y;
43613 y += 3 * uu * t * curve.control1.y;
43614 y += 3 * u * tt * curve.control2.y;
43615 y += ttt * curve.endPoint.y;
43616 var width = curve.startWidth + ttt * widthDelta;
43617 this.drawCurveSegment(x, y, width);
43623 drawCurveSegment: function (x, y, width) {
43624 var ctx = this.canvasElCtx();
43626 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43627 this.is_empty = false;
43632 var ctx = this.canvasElCtx();
43633 var canvas = this.canvasEl().dom;
43634 ctx.fillStyle = this.bg_color;
43635 ctx.clearRect(0, 0, canvas.width, canvas.height);
43636 ctx.fillRect(0, 0, canvas.width, canvas.height);
43637 this.curve_data = [];
43639 this.is_empty = true;
43644 return this.el.select('input',true).first();
43647 canvasEl: function()
43649 return this.el.select('canvas',true).first();
43652 canvasElCtx: function()
43654 return this.el.select('canvas',true).first().dom.getContext('2d');
43657 getImage: function(type)
43659 if(this.is_empty) {
43664 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43667 drawFromImage: function(img_src)
43669 var img = new Image();
43671 img.onload = function(){
43672 this.canvasElCtx().drawImage(img, 0, 0);
43677 this.is_empty = false;
43680 selectImage: function()
43682 this.fileEl().dom.click();
43685 uploadImage: function(e)
43687 var reader = new FileReader();
43689 reader.onload = function(e){
43690 var img = new Image();
43691 img.onload = function(){
43693 this.canvasElCtx().drawImage(img, 0, 0);
43695 img.src = e.target.result;
43698 reader.readAsDataURL(e.target.files[0]);
43701 // Bezier Point Constructor
43702 Point: (function () {
43703 function Point(x, y, time) {
43706 this.time = time || Date.now();
43708 Point.prototype.distanceTo = function (start) {
43709 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43711 Point.prototype.equals = function (other) {
43712 return this.x === other.x && this.y === other.y && this.time === other.time;
43714 Point.prototype.velocityFrom = function (start) {
43715 return this.time !== start.time
43716 ? this.distanceTo(start) / (this.time - start.time)
43723 // Bezier Constructor
43724 Bezier: (function () {
43725 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43726 this.startPoint = startPoint;
43727 this.control2 = control2;
43728 this.control1 = control1;
43729 this.endPoint = endPoint;
43730 this.startWidth = startWidth;
43731 this.endWidth = endWidth;
43733 Bezier.fromPoints = function (points, widths, scope) {
43734 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43735 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43736 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43738 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43739 var dx1 = s1.x - s2.x;
43740 var dy1 = s1.y - s2.y;
43741 var dx2 = s2.x - s3.x;
43742 var dy2 = s2.y - s3.y;
43743 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43744 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43745 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43746 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43747 var dxm = m1.x - m2.x;
43748 var dym = m1.y - m2.y;
43749 var k = l2 / (l1 + l2);
43750 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43751 var tx = s2.x - cm.x;
43752 var ty = s2.y - cm.y;
43754 c1: new scope.Point(m1.x + tx, m1.y + ty),
43755 c2: new scope.Point(m2.x + tx, m2.y + ty)
43758 Bezier.prototype.length = function () {
43763 for (var i = 0; i <= steps; i += 1) {
43765 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43766 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43768 var xdiff = cx - px;
43769 var ydiff = cy - py;
43770 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43777 Bezier.prototype.point = function (t, start, c1, c2, end) {
43778 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43779 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43780 + (3.0 * c2 * (1.0 - t) * t * t)
43781 + (end * t * t * t);
43786 throttleStroke: function(fn, wait) {
43787 if (wait === void 0) { wait = 250; }
43789 var timeout = null;
43793 var later = function () {
43794 previous = Date.now();
43796 result = fn.apply(storedContext, storedArgs);
43798 storedContext = null;
43802 return function wrapper() {
43804 for (var _i = 0; _i < arguments.length; _i++) {
43805 args[_i] = arguments[_i];
43807 var now = Date.now();
43808 var remaining = wait - (now - previous);
43809 storedContext = this;
43811 if (remaining <= 0 || remaining > wait) {
43813 clearTimeout(timeout);
43817 result = fn.apply(storedContext, storedArgs);
43819 storedContext = null;
43823 else if (!timeout) {
43824 timeout = window.setTimeout(later, remaining);