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'
10480 if (this.inputType == 'file') {
10481 input.style = 'overflow:hidden'; // why not in CSS?
10484 if(this.capture.length){
10485 input.capture = this.capture;
10488 if(this.accept.length){
10489 input.accept = this.accept + "/*";
10493 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10496 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10497 input.maxLength = this.maxLength;
10500 if (this.disabled) {
10501 input.disabled=true;
10504 if (this.readOnly) {
10505 input.readonly=true;
10509 input.name = this.name;
10513 input.cls += ' input-' + this.size;
10517 ['xs','sm','md','lg'].map(function(size){
10518 if (settings[size]) {
10519 cfg.cls += ' col-' + size + '-' + settings[size];
10523 var inputblock = input;
10527 cls: 'glyphicon form-control-feedback'
10530 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10533 cls : 'has-feedback',
10541 if (this.before || this.after) {
10544 cls : 'input-group',
10548 if (this.before && typeof(this.before) == 'string') {
10550 inputblock.cn.push({
10552 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10556 if (this.before && typeof(this.before) == 'object') {
10557 this.before = Roo.factory(this.before);
10559 inputblock.cn.push({
10561 cls : 'roo-input-before input-group-prepend input-group-' +
10562 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10566 inputblock.cn.push(input);
10568 if (this.after && typeof(this.after) == 'string') {
10569 inputblock.cn.push({
10571 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10575 if (this.after && typeof(this.after) == 'object') {
10576 this.after = Roo.factory(this.after);
10578 inputblock.cn.push({
10580 cls : 'roo-input-after input-group-append input-group-' +
10581 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10585 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10586 inputblock.cls += ' has-feedback';
10587 inputblock.cn.push(feedback);
10592 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10593 tooltip : 'This field is required'
10595 if (this.allowBlank ) {
10596 indicator.style = this.allowBlank ? ' 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) {
10686 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10687 tooltip : 'This field is required',
10688 style : this.allowBlank ? ' display:none' : ''
10692 //cls : 'input-group-addon',
10693 html : this.fieldLabel
10701 if(this.indicatorpos == 'right'){
10706 //cls : 'input-group-addon',
10707 html : this.fieldLabel
10712 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10713 tooltip : 'This field is required',
10714 style : this.allowBlank ? ' display:none' : ''
10734 if (this.parentType === 'Navbar' && this.parent().bar) {
10735 cfg.cls += ' navbar-form';
10738 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10739 // on BS4 we do this only if not form
10740 cfg.cls += ' navbar-form';
10748 * return the real input element.
10750 inputEl: function ()
10752 return this.el.select('input.form-control',true).first();
10755 tooltipEl : function()
10757 return this.inputEl();
10760 indicatorEl : function()
10762 if (Roo.bootstrap.version == 4) {
10763 return false; // not enabled in v4 yet.
10766 var indicator = this.el.select('i.roo-required-indicator',true).first();
10776 setDisabled : function(v)
10778 var i = this.inputEl().dom;
10780 i.removeAttribute('disabled');
10784 i.setAttribute('disabled','true');
10786 initEvents : function()
10789 this.inputEl().on("keydown" , this.fireKey, this);
10790 this.inputEl().on("focus", this.onFocus, this);
10791 this.inputEl().on("blur", this.onBlur, this);
10793 this.inputEl().relayEvent('keyup', this);
10795 this.indicator = this.indicatorEl();
10797 if(this.indicator){
10798 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10801 // reference to original value for reset
10802 this.originalValue = this.getValue();
10803 //Roo.form.TextField.superclass.initEvents.call(this);
10804 if(this.validationEvent == 'keyup'){
10805 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10806 this.inputEl().on('keyup', this.filterValidation, this);
10808 else if(this.validationEvent !== false){
10809 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10812 if(this.selectOnFocus){
10813 this.on("focus", this.preFocus, this);
10816 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10817 this.inputEl().on("keypress", this.filterKeys, this);
10819 this.inputEl().relayEvent('keypress', this);
10822 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
10823 this.el.on("click", this.autoSize, this);
10826 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10827 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10830 if (typeof(this.before) == 'object') {
10831 this.before.render(this.el.select('.roo-input-before',true).first());
10833 if (typeof(this.after) == 'object') {
10834 this.after.render(this.el.select('.roo-input-after',true).first());
10837 this.inputEl().on('change', this.onChange, this);
10840 filterValidation : function(e){
10841 if(!e.isNavKeyPress()){
10842 this.validationTask.delay(this.validationDelay);
10846 * Validates the field value
10847 * @return {Boolean} True if the value is valid, else false
10849 validate : function(){
10850 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10851 if(this.disabled || this.validateValue(this.getRawValue())){
10856 this.markInvalid();
10862 * Validates a value according to the field's validation rules and marks the field as invalid
10863 * if the validation fails
10864 * @param {Mixed} value The value to validate
10865 * @return {Boolean} True if the value is valid, else false
10867 validateValue : function(value)
10869 if(this.getVisibilityEl().hasClass('hidden')){
10873 if(value.length < 1) { // if it's blank
10874 if(this.allowBlank){
10880 if(value.length < this.minLength){
10883 if(value.length > this.maxLength){
10887 var vt = Roo.form.VTypes;
10888 if(!vt[this.vtype](value, this)){
10892 if(typeof this.validator == "function"){
10893 var msg = this.validator(value);
10897 if (typeof(msg) == 'string') {
10898 this.invalidText = msg;
10902 if(this.regex && !this.regex.test(value)){
10910 fireKey : function(e){
10911 //Roo.log('field ' + e.getKey());
10912 if(e.isNavKeyPress()){
10913 this.fireEvent("specialkey", this, e);
10916 focus : function (selectText){
10918 this.inputEl().focus();
10919 if(selectText === true){
10920 this.inputEl().dom.select();
10926 onFocus : function(){
10927 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10928 // this.el.addClass(this.focusClass);
10930 if(!this.hasFocus){
10931 this.hasFocus = true;
10932 this.startValue = this.getValue();
10933 this.fireEvent("focus", this);
10937 beforeBlur : Roo.emptyFn,
10941 onBlur : function(){
10943 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10944 //this.el.removeClass(this.focusClass);
10946 this.hasFocus = false;
10947 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10950 var v = this.getValue();
10951 if(String(v) !== String(this.startValue)){
10952 this.fireEvent('change', this, v, this.startValue);
10954 this.fireEvent("blur", this);
10957 onChange : function(e)
10959 var v = this.getValue();
10960 if(String(v) !== String(this.startValue)){
10961 this.fireEvent('change', this, v, this.startValue);
10967 * Resets the current field value to the originally loaded value and clears any validation messages
10969 reset : function(){
10970 this.setValue(this.originalValue);
10974 * Returns the name of the field
10975 * @return {Mixed} name The name field
10977 getName: function(){
10981 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10982 * @return {Mixed} value The field value
10984 getValue : function(){
10986 var v = this.inputEl().getValue();
10991 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10992 * @return {Mixed} value The field value
10994 getRawValue : function(){
10995 var v = this.inputEl().getValue();
11001 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11002 * @param {Mixed} value The value to set
11004 setRawValue : function(v){
11005 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11008 selectText : function(start, end){
11009 var v = this.getRawValue();
11011 start = start === undefined ? 0 : start;
11012 end = end === undefined ? v.length : end;
11013 var d = this.inputEl().dom;
11014 if(d.setSelectionRange){
11015 d.setSelectionRange(start, end);
11016 }else if(d.createTextRange){
11017 var range = d.createTextRange();
11018 range.moveStart("character", start);
11019 range.moveEnd("character", v.length-end);
11026 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11027 * @param {Mixed} value The value to set
11029 setValue : function(v){
11032 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11038 processValue : function(value){
11039 if(this.stripCharsRe){
11040 var newValue = value.replace(this.stripCharsRe, '');
11041 if(newValue !== value){
11042 this.setRawValue(newValue);
11049 preFocus : function(){
11051 if(this.selectOnFocus){
11052 this.inputEl().dom.select();
11055 filterKeys : function(e){
11056 var k = e.getKey();
11057 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11060 var c = e.getCharCode(), cc = String.fromCharCode(c);
11061 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11064 if(!this.maskRe.test(cc)){
11069 * Clear any invalid styles/messages for this field
11071 clearInvalid : function(){
11073 if(!this.el || this.preventMark){ // not rendered
11078 this.el.removeClass([this.invalidClass, 'is-invalid']);
11080 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11082 var feedback = this.el.select('.form-control-feedback', true).first();
11085 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11090 if(this.indicator){
11091 this.indicator.removeClass('visible');
11092 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11095 this.fireEvent('valid', this);
11099 * Mark this field as valid
11101 markValid : function()
11103 if(!this.el || this.preventMark){ // not rendered...
11107 this.el.removeClass([this.invalidClass, this.validClass]);
11108 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11110 var feedback = this.el.select('.form-control-feedback', true).first();
11113 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11116 if(this.indicator){
11117 this.indicator.removeClass('visible');
11118 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11126 if(this.allowBlank && !this.getRawValue().length){
11129 if (Roo.bootstrap.version == 3) {
11130 this.el.addClass(this.validClass);
11132 this.inputEl().addClass('is-valid');
11135 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11137 var feedback = this.el.select('.form-control-feedback', true).first();
11140 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11141 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11146 this.fireEvent('valid', this);
11150 * Mark this field as invalid
11151 * @param {String} msg The validation message
11153 markInvalid : function(msg)
11155 if(!this.el || this.preventMark){ // not rendered
11159 this.el.removeClass([this.invalidClass, this.validClass]);
11160 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11162 var feedback = this.el.select('.form-control-feedback', true).first();
11165 this.el.select('.form-control-feedback', true).first().removeClass(
11166 [this.invalidFeedbackClass, this.validFeedbackClass]);
11173 if(this.allowBlank && !this.getRawValue().length){
11177 if(this.indicator){
11178 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11179 this.indicator.addClass('visible');
11181 if (Roo.bootstrap.version == 3) {
11182 this.el.addClass(this.invalidClass);
11184 this.inputEl().addClass('is-invalid');
11189 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11191 var feedback = this.el.select('.form-control-feedback', true).first();
11194 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11196 if(this.getValue().length || this.forceFeedback){
11197 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11204 this.fireEvent('invalid', this, msg);
11207 SafariOnKeyDown : function(event)
11209 // this is a workaround for a password hang bug on chrome/ webkit.
11210 if (this.inputEl().dom.type != 'password') {
11214 var isSelectAll = false;
11216 if(this.inputEl().dom.selectionEnd > 0){
11217 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11219 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11220 event.preventDefault();
11225 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11227 event.preventDefault();
11228 // this is very hacky as keydown always get's upper case.
11230 var cc = String.fromCharCode(event.getCharCode());
11231 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11235 adjustWidth : function(tag, w){
11236 tag = tag.toLowerCase();
11237 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11238 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11239 if(tag == 'input'){
11242 if(tag == 'textarea'){
11245 }else if(Roo.isOpera){
11246 if(tag == 'input'){
11249 if(tag == 'textarea'){
11257 setFieldLabel : function(v)
11259 if(!this.rendered){
11263 if(this.indicatorEl()){
11264 var ar = this.el.select('label > span',true);
11266 if (ar.elements.length) {
11267 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11268 this.fieldLabel = v;
11272 var br = this.el.select('label',true);
11274 if(br.elements.length) {
11275 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11276 this.fieldLabel = v;
11280 Roo.log('Cannot Found any of label > span || label in input');
11284 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11285 this.fieldLabel = v;
11300 * @class Roo.bootstrap.TextArea
11301 * @extends Roo.bootstrap.Input
11302 * Bootstrap TextArea class
11303 * @cfg {Number} cols Specifies the visible width of a text area
11304 * @cfg {Number} rows Specifies the visible number of lines in a text area
11305 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11306 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11307 * @cfg {string} html text
11310 * Create a new TextArea
11311 * @param {Object} config The config object
11314 Roo.bootstrap.TextArea = function(config){
11315 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11319 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11329 getAutoCreate : function(){
11331 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11337 if(this.inputType != 'hidden'){
11338 cfg.cls = 'form-group' //input-group
11346 value : this.value || '',
11347 html: this.html || '',
11348 cls : 'form-control',
11349 placeholder : this.placeholder || ''
11353 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11354 input.maxLength = this.maxLength;
11358 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11362 input.cols = this.cols;
11365 if (this.readOnly) {
11366 input.readonly = true;
11370 input.name = this.name;
11374 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11378 ['xs','sm','md','lg'].map(function(size){
11379 if (settings[size]) {
11380 cfg.cls += ' col-' + size + '-' + settings[size];
11384 var inputblock = input;
11386 if(this.hasFeedback && !this.allowBlank){
11390 cls: 'glyphicon form-control-feedback'
11394 cls : 'has-feedback',
11403 if (this.before || this.after) {
11406 cls : 'input-group',
11410 inputblock.cn.push({
11412 cls : 'input-group-addon',
11417 inputblock.cn.push(input);
11419 if(this.hasFeedback && !this.allowBlank){
11420 inputblock.cls += ' has-feedback';
11421 inputblock.cn.push(feedback);
11425 inputblock.cn.push({
11427 cls : 'input-group-addon',
11434 if (align ==='left' && this.fieldLabel.length) {
11439 cls : 'control-label',
11440 html : this.fieldLabel
11451 if(this.labelWidth > 12){
11452 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11455 if(this.labelWidth < 13 && this.labelmd == 0){
11456 this.labelmd = this.labelWidth;
11459 if(this.labellg > 0){
11460 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11461 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11464 if(this.labelmd > 0){
11465 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11466 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11469 if(this.labelsm > 0){
11470 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11471 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11474 if(this.labelxs > 0){
11475 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11476 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11479 } else if ( this.fieldLabel.length) {
11484 //cls : 'input-group-addon',
11485 html : this.fieldLabel
11503 if (this.disabled) {
11504 input.disabled=true;
11511 * return the real textarea element.
11513 inputEl: function ()
11515 return this.el.select('textarea.form-control',true).first();
11519 * Clear any invalid styles/messages for this field
11521 clearInvalid : function()
11524 if(!this.el || this.preventMark){ // not rendered
11528 var label = this.el.select('label', true).first();
11529 var icon = this.el.select('i.fa-star', true).first();
11534 this.el.removeClass( this.validClass);
11535 this.inputEl().removeClass('is-invalid');
11537 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11539 var feedback = this.el.select('.form-control-feedback', true).first();
11542 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11547 this.fireEvent('valid', this);
11551 * Mark this field as valid
11553 markValid : function()
11555 if(!this.el || this.preventMark){ // not rendered
11559 this.el.removeClass([this.invalidClass, this.validClass]);
11560 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11562 var feedback = this.el.select('.form-control-feedback', true).first();
11565 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11568 if(this.disabled || this.allowBlank){
11572 var label = this.el.select('label', true).first();
11573 var icon = this.el.select('i.fa-star', true).first();
11578 if (Roo.bootstrap.version == 3) {
11579 this.el.addClass(this.validClass);
11581 this.inputEl().addClass('is-valid');
11585 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11587 var feedback = this.el.select('.form-control-feedback', true).first();
11590 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11591 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11596 this.fireEvent('valid', this);
11600 * Mark this field as invalid
11601 * @param {String} msg The validation message
11603 markInvalid : function(msg)
11605 if(!this.el || this.preventMark){ // not rendered
11609 this.el.removeClass([this.invalidClass, this.validClass]);
11610 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11612 var feedback = this.el.select('.form-control-feedback', true).first();
11615 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11618 if(this.disabled || this.allowBlank){
11622 var label = this.el.select('label', true).first();
11623 var icon = this.el.select('i.fa-star', true).first();
11625 if(!this.getValue().length && label && !icon){
11626 this.el.createChild({
11628 cls : 'text-danger fa fa-lg fa-star',
11629 tooltip : 'This field is required',
11630 style : 'margin-right:5px;'
11634 if (Roo.bootstrap.version == 3) {
11635 this.el.addClass(this.invalidClass);
11637 this.inputEl().addClass('is-invalid');
11640 // fixme ... this may be depricated need to test..
11641 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11643 var feedback = this.el.select('.form-control-feedback', true).first();
11646 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11648 if(this.getValue().length || this.forceFeedback){
11649 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11656 this.fireEvent('invalid', this, msg);
11664 * trigger field - base class for combo..
11669 * @class Roo.bootstrap.TriggerField
11670 * @extends Roo.bootstrap.Input
11671 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11672 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11673 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11674 * for which you can provide a custom implementation. For example:
11676 var trigger = new Roo.bootstrap.TriggerField();
11677 trigger.onTriggerClick = myTriggerFn;
11678 trigger.applyTo('my-field');
11681 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11682 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11683 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11684 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11685 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11688 * Create a new TriggerField.
11689 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11690 * to the base TextField)
11692 Roo.bootstrap.TriggerField = function(config){
11693 this.mimicing = false;
11694 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11697 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11699 * @cfg {String} triggerClass A CSS class to apply to the trigger
11702 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11707 * @cfg {Boolean} removable (true|false) special filter default false
11711 /** @cfg {Boolean} grow @hide */
11712 /** @cfg {Number} growMin @hide */
11713 /** @cfg {Number} growMax @hide */
11719 autoSize: Roo.emptyFn,
11723 deferHeight : true,
11726 actionMode : 'wrap',
11731 getAutoCreate : function(){
11733 var align = this.labelAlign || this.parentLabelAlign();
11738 cls: 'form-group' //input-group
11745 type : this.inputType,
11746 cls : 'form-control',
11747 autocomplete: 'new-password',
11748 placeholder : this.placeholder || ''
11752 input.name = this.name;
11755 input.cls += ' input-' + this.size;
11758 if (this.disabled) {
11759 input.disabled=true;
11762 var inputblock = input;
11764 if(this.hasFeedback && !this.allowBlank){
11768 cls: 'glyphicon form-control-feedback'
11771 if(this.removable && !this.editable ){
11773 cls : 'has-feedback',
11779 cls : 'roo-combo-removable-btn close'
11786 cls : 'has-feedback',
11795 if(this.removable && !this.editable ){
11797 cls : 'roo-removable',
11803 cls : 'roo-combo-removable-btn close'
11810 if (this.before || this.after) {
11813 cls : 'input-group',
11817 inputblock.cn.push({
11819 cls : 'input-group-addon input-group-prepend input-group-text',
11824 inputblock.cn.push(input);
11826 if(this.hasFeedback && !this.allowBlank){
11827 inputblock.cls += ' has-feedback';
11828 inputblock.cn.push(feedback);
11832 inputblock.cn.push({
11834 cls : 'input-group-addon input-group-append input-group-text',
11843 var ibwrap = inputblock;
11848 cls: 'roo-select2-choices',
11852 cls: 'roo-select2-search-field',
11864 cls: 'roo-select2-container input-group',
11869 cls: 'form-hidden-field'
11875 if(!this.multiple && this.showToggleBtn){
11881 if (this.caret != false) {
11884 cls: 'fa fa-' + this.caret
11891 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11893 Roo.bootstrap.version == 3 ? caret : '',
11896 cls: 'combobox-clear',
11910 combobox.cls += ' roo-select2-container-multi';
11914 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11915 tooltip : 'This field is required'
11917 if (Roo.bootstrap.version == 4) {
11920 style : 'display:none'
11925 if (align ==='left' && this.fieldLabel.length) {
11927 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11934 cls : 'control-label',
11935 html : this.fieldLabel
11947 var labelCfg = cfg.cn[1];
11948 var contentCfg = cfg.cn[2];
11950 if(this.indicatorpos == 'right'){
11955 cls : 'control-label',
11959 html : this.fieldLabel
11973 labelCfg = cfg.cn[0];
11974 contentCfg = cfg.cn[1];
11977 if(this.labelWidth > 12){
11978 labelCfg.style = "width: " + this.labelWidth + 'px';
11981 if(this.labelWidth < 13 && this.labelmd == 0){
11982 this.labelmd = this.labelWidth;
11985 if(this.labellg > 0){
11986 labelCfg.cls += ' col-lg-' + this.labellg;
11987 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11990 if(this.labelmd > 0){
11991 labelCfg.cls += ' col-md-' + this.labelmd;
11992 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11995 if(this.labelsm > 0){
11996 labelCfg.cls += ' col-sm-' + this.labelsm;
11997 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12000 if(this.labelxs > 0){
12001 labelCfg.cls += ' col-xs-' + this.labelxs;
12002 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12005 } else if ( this.fieldLabel.length) {
12006 // Roo.log(" label");
12011 //cls : 'input-group-addon',
12012 html : this.fieldLabel
12020 if(this.indicatorpos == 'right'){
12028 html : this.fieldLabel
12042 // Roo.log(" no label && no align");
12049 ['xs','sm','md','lg'].map(function(size){
12050 if (settings[size]) {
12051 cfg.cls += ' col-' + size + '-' + settings[size];
12062 onResize : function(w, h){
12063 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12064 // if(typeof w == 'number'){
12065 // var x = w - this.trigger.getWidth();
12066 // this.inputEl().setWidth(this.adjustWidth('input', x));
12067 // this.trigger.setStyle('left', x+'px');
12072 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12075 getResizeEl : function(){
12076 return this.inputEl();
12080 getPositionEl : function(){
12081 return this.inputEl();
12085 alignErrorIcon : function(){
12086 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12090 initEvents : function(){
12094 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12095 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12096 if(!this.multiple && this.showToggleBtn){
12097 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12098 if(this.hideTrigger){
12099 this.trigger.setDisplayed(false);
12101 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12105 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12108 if(this.removable && !this.editable && !this.tickable){
12109 var close = this.closeTriggerEl();
12112 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12113 close.on('click', this.removeBtnClick, this, close);
12117 //this.trigger.addClassOnOver('x-form-trigger-over');
12118 //this.trigger.addClassOnClick('x-form-trigger-click');
12121 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12125 closeTriggerEl : function()
12127 var close = this.el.select('.roo-combo-removable-btn', true).first();
12128 return close ? close : false;
12131 removeBtnClick : function(e, h, el)
12133 e.preventDefault();
12135 if(this.fireEvent("remove", this) !== false){
12137 this.fireEvent("afterremove", this)
12141 createList : function()
12143 this.list = Roo.get(document.body).createChild({
12144 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12145 cls: 'typeahead typeahead-long dropdown-menu',
12146 style: 'display:none'
12149 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12154 initTrigger : function(){
12159 onDestroy : function(){
12161 this.trigger.removeAllListeners();
12162 // this.trigger.remove();
12165 // this.wrap.remove();
12167 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12171 onFocus : function(){
12172 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12174 if(!this.mimicing){
12175 this.wrap.addClass('x-trigger-wrap-focus');
12176 this.mimicing = true;
12177 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12178 if(this.monitorTab){
12179 this.el.on("keydown", this.checkTab, this);
12186 checkTab : function(e){
12187 if(e.getKey() == e.TAB){
12188 this.triggerBlur();
12193 onBlur : function(){
12198 mimicBlur : function(e, t){
12200 if(!this.wrap.contains(t) && this.validateBlur()){
12201 this.triggerBlur();
12207 triggerBlur : function(){
12208 this.mimicing = false;
12209 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12210 if(this.monitorTab){
12211 this.el.un("keydown", this.checkTab, this);
12213 //this.wrap.removeClass('x-trigger-wrap-focus');
12214 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12218 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12219 validateBlur : function(e, t){
12224 onDisable : function(){
12225 this.inputEl().dom.disabled = true;
12226 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12228 // this.wrap.addClass('x-item-disabled');
12233 onEnable : function(){
12234 this.inputEl().dom.disabled = false;
12235 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12237 // this.el.removeClass('x-item-disabled');
12242 onShow : function(){
12243 var ae = this.getActionEl();
12246 ae.dom.style.display = '';
12247 ae.dom.style.visibility = 'visible';
12253 onHide : function(){
12254 var ae = this.getActionEl();
12255 ae.dom.style.display = 'none';
12259 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12260 * by an implementing function.
12262 * @param {EventObject} e
12264 onTriggerClick : Roo.emptyFn
12272 * @class Roo.bootstrap.CardUploader
12273 * @extends Roo.bootstrap.Button
12274 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12275 * @cfg {Number} errorTimeout default 3000
12276 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12277 * @cfg {Array} html The button text.
12281 * Create a new CardUploader
12282 * @param {Object} config The config object
12285 Roo.bootstrap.CardUploader = function(config){
12289 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12292 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12299 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12302 errorTimeout : 3000,
12306 fileCollection : false,
12309 getAutoCreate : function()
12313 cls :'form-group' ,
12318 //cls : 'input-group-addon',
12319 html : this.fieldLabel
12326 value : this.value,
12327 cls : 'd-none form-control'
12332 multiple : 'multiple',
12334 cls : 'd-none roo-card-upload-selector'
12338 cls : 'roo-card-uploader-button-container w-100 mb-2'
12341 cls : 'card-columns roo-card-uploader-container'
12351 getChildContainer : function() /// what children are added to.
12353 return this.containerEl;
12356 getButtonContainer : function() /// what children are added to.
12358 return this.el.select(".roo-card-uploader-button-container").first();
12361 initEvents : function()
12364 Roo.bootstrap.Input.prototype.initEvents.call(this);
12368 xns: Roo.bootstrap,
12371 container_method : 'getButtonContainer' ,
12372 html : this.html, // fix changable?
12375 'click' : function(btn, e) {
12384 this.urlAPI = (window.createObjectURL && window) ||
12385 (window.URL && URL.revokeObjectURL && URL) ||
12386 (window.webkitURL && webkitURL);
12391 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12393 this.selectorEl.on('change', this.onFileSelected, this);
12396 this.images.forEach(function(img) {
12399 this.images = false;
12401 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12407 onClick : function(e)
12409 e.preventDefault();
12411 this.selectorEl.dom.click();
12415 onFileSelected : function(e)
12417 e.preventDefault();
12419 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12423 Roo.each(this.selectorEl.dom.files, function(file){
12424 this.addFile(file);
12433 addFile : function(file)
12436 if(typeof(file) === 'string'){
12437 throw "Add file by name?"; // should not happen
12441 if(!file || !this.urlAPI){
12451 var url = _this.urlAPI.createObjectURL( file);
12454 id : Roo.bootstrap.CardUploader.ID--,
12455 is_uploaded : false,
12458 mimetype : file.type,
12465 addCard : function (data)
12467 // hidden input element?
12468 // if the file is not an image...
12469 //then we need to use something other that and header_image
12474 xns : Roo.bootstrap,
12475 xtype : 'CardFooter',
12478 xns : Roo.bootstrap,
12484 xns : Roo.bootstrap,
12486 html : String.format("<small>{0}</small>", data.title),
12487 cls : 'col-11 text-left',
12492 click : function() {
12493 this.downloadCard(data.id)
12499 xns : Roo.bootstrap,
12507 click : function() {
12508 t.removeCard(data.id)
12520 var cn = this.addxtype(
12523 xns : Roo.bootstrap,
12526 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12527 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12528 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12533 initEvents : function() {
12534 Roo.bootstrap.Card.prototype.initEvents.call(this);
12535 this.imgEl = this.el.select('.card-img-top').first();
12537 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12538 this.imgEl.set({ 'pointer' : 'cursor' });
12547 // dont' really need ot update items.
12548 // this.items.push(cn);
12549 this.fileCollection.add(cn);
12550 this.updateInput();
12553 removeCard : function(id)
12556 var card = this.fileCollection.get(id);
12557 card.data.is_deleted = 1;
12558 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12559 this.fileCollection.remove(card);
12560 //this.items = this.items.filter(function(e) { return e != card });
12561 // dont' really need ot update items.
12562 card.el.dom.parentNode.removeChild(card.el.dom);
12567 this.fileCollection.each(function(card) {
12568 card.el.dom.parentNode.removeChild(card.el.dom);
12570 this.fileCollection.clear();
12571 this.updateInput();
12574 updateInput : function()
12577 this.fileCollection.each(function(e) {
12581 this.inputEl().dom.value = JSON.stringify(data);
12588 Roo.bootstrap.CardUploader.ID = -1;/*
12590 * Ext JS Library 1.1.1
12591 * Copyright(c) 2006-2007, Ext JS, LLC.
12593 * Originally Released Under LGPL - original licence link has changed is not relivant.
12596 * <script type="text/javascript">
12601 * @class Roo.data.SortTypes
12603 * Defines the default sorting (casting?) comparison functions used when sorting data.
12605 Roo.data.SortTypes = {
12607 * Default sort that does nothing
12608 * @param {Mixed} s The value being converted
12609 * @return {Mixed} The comparison value
12611 none : function(s){
12616 * The regular expression used to strip tags
12620 stripTagsRE : /<\/?[^>]+>/gi,
12623 * Strips all HTML tags to sort on text only
12624 * @param {Mixed} s The value being converted
12625 * @return {String} The comparison value
12627 asText : function(s){
12628 return String(s).replace(this.stripTagsRE, "");
12632 * Strips all HTML tags to sort on text only - Case insensitive
12633 * @param {Mixed} s The value being converted
12634 * @return {String} The comparison value
12636 asUCText : function(s){
12637 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12641 * Case insensitive string
12642 * @param {Mixed} s The value being converted
12643 * @return {String} The comparison value
12645 asUCString : function(s) {
12646 return String(s).toUpperCase();
12651 * @param {Mixed} s The value being converted
12652 * @return {Number} The comparison value
12654 asDate : function(s) {
12658 if(s instanceof Date){
12659 return s.getTime();
12661 return Date.parse(String(s));
12666 * @param {Mixed} s The value being converted
12667 * @return {Float} The comparison value
12669 asFloat : function(s) {
12670 var val = parseFloat(String(s).replace(/,/g, ""));
12679 * @param {Mixed} s The value being converted
12680 * @return {Number} The comparison value
12682 asInt : function(s) {
12683 var val = parseInt(String(s).replace(/,/g, ""));
12691 * Ext JS Library 1.1.1
12692 * Copyright(c) 2006-2007, Ext JS, LLC.
12694 * Originally Released Under LGPL - original licence link has changed is not relivant.
12697 * <script type="text/javascript">
12701 * @class Roo.data.Record
12702 * Instances of this class encapsulate both record <em>definition</em> information, and record
12703 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12704 * to access Records cached in an {@link Roo.data.Store} object.<br>
12706 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12707 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12710 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12712 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12713 * {@link #create}. The parameters are the same.
12714 * @param {Array} data An associative Array of data values keyed by the field name.
12715 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12716 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12717 * not specified an integer id is generated.
12719 Roo.data.Record = function(data, id){
12720 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12725 * Generate a constructor for a specific record layout.
12726 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12727 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12728 * Each field definition object may contain the following properties: <ul>
12729 * <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,
12730 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12731 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12732 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12733 * is being used, then this is a string containing the javascript expression to reference the data relative to
12734 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12735 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12736 * this may be omitted.</p></li>
12737 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12738 * <ul><li>auto (Default, implies no conversion)</li>
12743 * <li>date</li></ul></p></li>
12744 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12745 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12746 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12747 * by the Reader into an object that will be stored in the Record. It is passed the
12748 * following parameters:<ul>
12749 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12751 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12753 * <br>usage:<br><pre><code>
12754 var TopicRecord = Roo.data.Record.create(
12755 {name: 'title', mapping: 'topic_title'},
12756 {name: 'author', mapping: 'username'},
12757 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12758 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12759 {name: 'lastPoster', mapping: 'user2'},
12760 {name: 'excerpt', mapping: 'post_text'}
12763 var myNewRecord = new TopicRecord({
12764 title: 'Do my job please',
12767 lastPost: new Date(),
12768 lastPoster: 'Animal',
12769 excerpt: 'No way dude!'
12771 myStore.add(myNewRecord);
12776 Roo.data.Record.create = function(o){
12777 var f = function(){
12778 f.superclass.constructor.apply(this, arguments);
12780 Roo.extend(f, Roo.data.Record);
12781 var p = f.prototype;
12782 p.fields = new Roo.util.MixedCollection(false, function(field){
12785 for(var i = 0, len = o.length; i < len; i++){
12786 p.fields.add(new Roo.data.Field(o[i]));
12788 f.getField = function(name){
12789 return p.fields.get(name);
12794 Roo.data.Record.AUTO_ID = 1000;
12795 Roo.data.Record.EDIT = 'edit';
12796 Roo.data.Record.REJECT = 'reject';
12797 Roo.data.Record.COMMIT = 'commit';
12799 Roo.data.Record.prototype = {
12801 * Readonly flag - true if this record has been modified.
12810 join : function(store){
12811 this.store = store;
12815 * Set the named field to the specified value.
12816 * @param {String} name The name of the field to set.
12817 * @param {Object} value The value to set the field to.
12819 set : function(name, value){
12820 if(this.data[name] == value){
12824 if(!this.modified){
12825 this.modified = {};
12827 if(typeof this.modified[name] == 'undefined'){
12828 this.modified[name] = this.data[name];
12830 this.data[name] = value;
12831 if(!this.editing && this.store){
12832 this.store.afterEdit(this);
12837 * Get the value of the named field.
12838 * @param {String} name The name of the field to get the value of.
12839 * @return {Object} The value of the field.
12841 get : function(name){
12842 return this.data[name];
12846 beginEdit : function(){
12847 this.editing = true;
12848 this.modified = {};
12852 cancelEdit : function(){
12853 this.editing = false;
12854 delete this.modified;
12858 endEdit : function(){
12859 this.editing = false;
12860 if(this.dirty && this.store){
12861 this.store.afterEdit(this);
12866 * Usually called by the {@link Roo.data.Store} which owns the Record.
12867 * Rejects all changes made to the Record since either creation, or the last commit operation.
12868 * Modified fields are reverted to their original values.
12870 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12871 * of reject operations.
12873 reject : function(){
12874 var m = this.modified;
12876 if(typeof m[n] != "function"){
12877 this.data[n] = m[n];
12880 this.dirty = false;
12881 delete this.modified;
12882 this.editing = false;
12884 this.store.afterReject(this);
12889 * Usually called by the {@link Roo.data.Store} which owns the Record.
12890 * Commits all changes made to the Record since either creation, or the last commit operation.
12892 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12893 * of commit operations.
12895 commit : function(){
12896 this.dirty = false;
12897 delete this.modified;
12898 this.editing = false;
12900 this.store.afterCommit(this);
12905 hasError : function(){
12906 return this.error != null;
12910 clearError : function(){
12915 * Creates a copy of this record.
12916 * @param {String} id (optional) A new record id if you don't want to use this record's id
12919 copy : function(newId) {
12920 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12924 * Ext JS Library 1.1.1
12925 * Copyright(c) 2006-2007, Ext JS, LLC.
12927 * Originally Released Under LGPL - original licence link has changed is not relivant.
12930 * <script type="text/javascript">
12936 * @class Roo.data.Store
12937 * @extends Roo.util.Observable
12938 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12939 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12941 * 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
12942 * has no knowledge of the format of the data returned by the Proxy.<br>
12944 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12945 * instances from the data object. These records are cached and made available through accessor functions.
12947 * Creates a new Store.
12948 * @param {Object} config A config object containing the objects needed for the Store to access data,
12949 * and read the data into Records.
12951 Roo.data.Store = function(config){
12952 this.data = new Roo.util.MixedCollection(false);
12953 this.data.getKey = function(o){
12956 this.baseParams = {};
12958 this.paramNames = {
12963 "multisort" : "_multisort"
12966 if(config && config.data){
12967 this.inlineData = config.data;
12968 delete config.data;
12971 Roo.apply(this, config);
12973 if(this.reader){ // reader passed
12974 this.reader = Roo.factory(this.reader, Roo.data);
12975 this.reader.xmodule = this.xmodule || false;
12976 if(!this.recordType){
12977 this.recordType = this.reader.recordType;
12979 if(this.reader.onMetaChange){
12980 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12984 if(this.recordType){
12985 this.fields = this.recordType.prototype.fields;
12987 this.modified = [];
12991 * @event datachanged
12992 * Fires when the data cache has changed, and a widget which is using this Store
12993 * as a Record cache should refresh its view.
12994 * @param {Store} this
12996 datachanged : true,
12998 * @event metachange
12999 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13000 * @param {Store} this
13001 * @param {Object} meta The JSON metadata
13006 * Fires when Records have been added to the Store
13007 * @param {Store} this
13008 * @param {Roo.data.Record[]} records The array of Records added
13009 * @param {Number} index The index at which the record(s) were added
13014 * Fires when a Record has been removed from the Store
13015 * @param {Store} this
13016 * @param {Roo.data.Record} record The Record that was removed
13017 * @param {Number} index The index at which the record was removed
13022 * Fires when a Record has been updated
13023 * @param {Store} this
13024 * @param {Roo.data.Record} record The Record that was updated
13025 * @param {String} operation The update operation being performed. Value may be one of:
13027 Roo.data.Record.EDIT
13028 Roo.data.Record.REJECT
13029 Roo.data.Record.COMMIT
13035 * Fires when the data cache has been cleared.
13036 * @param {Store} this
13040 * @event beforeload
13041 * Fires before a request is made for a new data object. If the beforeload handler returns false
13042 * the load action will be canceled.
13043 * @param {Store} this
13044 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13048 * @event beforeloadadd
13049 * Fires after a new set of Records has been loaded.
13050 * @param {Store} this
13051 * @param {Roo.data.Record[]} records The Records that were loaded
13052 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13054 beforeloadadd : true,
13057 * Fires after a new set of Records has been loaded, before they are added to the store.
13058 * @param {Store} this
13059 * @param {Roo.data.Record[]} records The Records that were loaded
13060 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13061 * @params {Object} return from reader
13065 * @event loadexception
13066 * Fires if an exception occurs in the Proxy during loading.
13067 * Called with the signature of the Proxy's "loadexception" event.
13068 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13071 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13072 * @param {Object} load options
13073 * @param {Object} jsonData from your request (normally this contains the Exception)
13075 loadexception : true
13079 this.proxy = Roo.factory(this.proxy, Roo.data);
13080 this.proxy.xmodule = this.xmodule || false;
13081 this.relayEvents(this.proxy, ["loadexception"]);
13083 this.sortToggle = {};
13084 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13086 Roo.data.Store.superclass.constructor.call(this);
13088 if(this.inlineData){
13089 this.loadData(this.inlineData);
13090 delete this.inlineData;
13094 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13096 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13097 * without a remote query - used by combo/forms at present.
13101 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13104 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13107 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13108 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13111 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13112 * on any HTTP request
13115 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13118 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13122 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13123 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13125 remoteSort : false,
13128 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13129 * loaded or when a record is removed. (defaults to false).
13131 pruneModifiedRecords : false,
13134 lastOptions : null,
13137 * Add Records to the Store and fires the add event.
13138 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13140 add : function(records){
13141 records = [].concat(records);
13142 for(var i = 0, len = records.length; i < len; i++){
13143 records[i].join(this);
13145 var index = this.data.length;
13146 this.data.addAll(records);
13147 this.fireEvent("add", this, records, index);
13151 * Remove a Record from the Store and fires the remove event.
13152 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13154 remove : function(record){
13155 var index = this.data.indexOf(record);
13156 this.data.removeAt(index);
13158 if(this.pruneModifiedRecords){
13159 this.modified.remove(record);
13161 this.fireEvent("remove", this, record, index);
13165 * Remove all Records from the Store and fires the clear event.
13167 removeAll : function(){
13169 if(this.pruneModifiedRecords){
13170 this.modified = [];
13172 this.fireEvent("clear", this);
13176 * Inserts Records to the Store at the given index and fires the add event.
13177 * @param {Number} index The start index at which to insert the passed Records.
13178 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13180 insert : function(index, records){
13181 records = [].concat(records);
13182 for(var i = 0, len = records.length; i < len; i++){
13183 this.data.insert(index, records[i]);
13184 records[i].join(this);
13186 this.fireEvent("add", this, records, index);
13190 * Get the index within the cache of the passed Record.
13191 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13192 * @return {Number} The index of the passed Record. Returns -1 if not found.
13194 indexOf : function(record){
13195 return this.data.indexOf(record);
13199 * Get the index within the cache of the Record with the passed id.
13200 * @param {String} id The id of the Record to find.
13201 * @return {Number} The index of the Record. Returns -1 if not found.
13203 indexOfId : function(id){
13204 return this.data.indexOfKey(id);
13208 * Get the Record with the specified id.
13209 * @param {String} id The id of the Record to find.
13210 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13212 getById : function(id){
13213 return this.data.key(id);
13217 * Get the Record at the specified index.
13218 * @param {Number} index The index of the Record to find.
13219 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13221 getAt : function(index){
13222 return this.data.itemAt(index);
13226 * Returns a range of Records between specified indices.
13227 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13228 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13229 * @return {Roo.data.Record[]} An array of Records
13231 getRange : function(start, end){
13232 return this.data.getRange(start, end);
13236 storeOptions : function(o){
13237 o = Roo.apply({}, o);
13240 this.lastOptions = o;
13244 * Loads the Record cache from the configured Proxy using the configured Reader.
13246 * If using remote paging, then the first load call must specify the <em>start</em>
13247 * and <em>limit</em> properties in the options.params property to establish the initial
13248 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13250 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13251 * and this call will return before the new data has been loaded. Perform any post-processing
13252 * in a callback function, or in a "load" event handler.</strong>
13254 * @param {Object} options An object containing properties which control loading options:<ul>
13255 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13256 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13257 * passed the following arguments:<ul>
13258 * <li>r : Roo.data.Record[]</li>
13259 * <li>options: Options object from the load call</li>
13260 * <li>success: Boolean success indicator</li></ul></li>
13261 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13262 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13265 load : function(options){
13266 options = options || {};
13267 if(this.fireEvent("beforeload", this, options) !== false){
13268 this.storeOptions(options);
13269 var p = Roo.apply(options.params || {}, this.baseParams);
13270 // if meta was not loaded from remote source.. try requesting it.
13271 if (!this.reader.metaFromRemote) {
13272 p._requestMeta = 1;
13274 if(this.sortInfo && this.remoteSort){
13275 var pn = this.paramNames;
13276 p[pn["sort"]] = this.sortInfo.field;
13277 p[pn["dir"]] = this.sortInfo.direction;
13279 if (this.multiSort) {
13280 var pn = this.paramNames;
13281 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13284 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13289 * Reloads the Record cache from the configured Proxy using the configured Reader and
13290 * the options from the last load operation performed.
13291 * @param {Object} options (optional) An object containing properties which may override the options
13292 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13293 * the most recently used options are reused).
13295 reload : function(options){
13296 this.load(Roo.applyIf(options||{}, this.lastOptions));
13300 // Called as a callback by the Reader during a load operation.
13301 loadRecords : function(o, options, success){
13302 if(!o || success === false){
13303 if(success !== false){
13304 this.fireEvent("load", this, [], options, o);
13306 if(options.callback){
13307 options.callback.call(options.scope || this, [], options, false);
13311 // if data returned failure - throw an exception.
13312 if (o.success === false) {
13313 // show a message if no listener is registered.
13314 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13315 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13317 // loadmask wil be hooked into this..
13318 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13321 var r = o.records, t = o.totalRecords || r.length;
13323 this.fireEvent("beforeloadadd", this, r, options, o);
13325 if(!options || options.add !== true){
13326 if(this.pruneModifiedRecords){
13327 this.modified = [];
13329 for(var i = 0, len = r.length; i < len; i++){
13333 this.data = this.snapshot;
13334 delete this.snapshot;
13337 this.data.addAll(r);
13338 this.totalLength = t;
13340 this.fireEvent("datachanged", this);
13342 this.totalLength = Math.max(t, this.data.length+r.length);
13346 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13348 var e = new Roo.data.Record({});
13350 e.set(this.parent.displayField, this.parent.emptyTitle);
13351 e.set(this.parent.valueField, '');
13356 this.fireEvent("load", this, r, options, o);
13357 if(options.callback){
13358 options.callback.call(options.scope || this, r, options, true);
13364 * Loads data from a passed data block. A Reader which understands the format of the data
13365 * must have been configured in the constructor.
13366 * @param {Object} data The data block from which to read the Records. The format of the data expected
13367 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13368 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13370 loadData : function(o, append){
13371 var r = this.reader.readRecords(o);
13372 this.loadRecords(r, {add: append}, true);
13376 * using 'cn' the nested child reader read the child array into it's child stores.
13377 * @param {Object} rec The record with a 'children array
13379 loadDataFromChildren : function(rec)
13381 this.loadData(this.reader.toLoadData(rec));
13386 * Gets the number of cached records.
13388 * <em>If using paging, this may not be the total size of the dataset. If the data object
13389 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13390 * the data set size</em>
13392 getCount : function(){
13393 return this.data.length || 0;
13397 * Gets the total number of records in the dataset as returned by the server.
13399 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13400 * the dataset size</em>
13402 getTotalCount : function(){
13403 return this.totalLength || 0;
13407 * Returns the sort state of the Store as an object with two properties:
13409 field {String} The name of the field by which the Records are sorted
13410 direction {String} The sort order, "ASC" or "DESC"
13413 getSortState : function(){
13414 return this.sortInfo;
13418 applySort : function(){
13419 if(this.sortInfo && !this.remoteSort){
13420 var s = this.sortInfo, f = s.field;
13421 var st = this.fields.get(f).sortType;
13422 var fn = function(r1, r2){
13423 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13424 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13426 this.data.sort(s.direction, fn);
13427 if(this.snapshot && this.snapshot != this.data){
13428 this.snapshot.sort(s.direction, fn);
13434 * Sets the default sort column and order to be used by the next load operation.
13435 * @param {String} fieldName The name of the field to sort by.
13436 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13438 setDefaultSort : function(field, dir){
13439 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13443 * Sort the Records.
13444 * If remote sorting is used, the sort is performed on the server, and the cache is
13445 * reloaded. If local sorting is used, the cache is sorted internally.
13446 * @param {String} fieldName The name of the field to sort by.
13447 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13449 sort : function(fieldName, dir){
13450 var f = this.fields.get(fieldName);
13452 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13454 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13455 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13460 this.sortToggle[f.name] = dir;
13461 this.sortInfo = {field: f.name, direction: dir};
13462 if(!this.remoteSort){
13464 this.fireEvent("datachanged", this);
13466 this.load(this.lastOptions);
13471 * Calls the specified function for each of the Records in the cache.
13472 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13473 * Returning <em>false</em> aborts and exits the iteration.
13474 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13476 each : function(fn, scope){
13477 this.data.each(fn, scope);
13481 * Gets all records modified since the last commit. Modified records are persisted across load operations
13482 * (e.g., during paging).
13483 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13485 getModifiedRecords : function(){
13486 return this.modified;
13490 createFilterFn : function(property, value, anyMatch){
13491 if(!value.exec){ // not a regex
13492 value = String(value);
13493 if(value.length == 0){
13496 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13498 return function(r){
13499 return value.test(r.data[property]);
13504 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13505 * @param {String} property A field on your records
13506 * @param {Number} start The record index to start at (defaults to 0)
13507 * @param {Number} end The last record index to include (defaults to length - 1)
13508 * @return {Number} The sum
13510 sum : function(property, start, end){
13511 var rs = this.data.items, v = 0;
13512 start = start || 0;
13513 end = (end || end === 0) ? end : rs.length-1;
13515 for(var i = start; i <= end; i++){
13516 v += (rs[i].data[property] || 0);
13522 * Filter the records by a specified property.
13523 * @param {String} field A field on your records
13524 * @param {String/RegExp} value Either a string that the field
13525 * should start with or a RegExp to test against the field
13526 * @param {Boolean} anyMatch True to match any part not just the beginning
13528 filter : function(property, value, anyMatch){
13529 var fn = this.createFilterFn(property, value, anyMatch);
13530 return fn ? this.filterBy(fn) : this.clearFilter();
13534 * Filter by a function. The specified function will be called with each
13535 * record in this data source. If the function returns true the record is included,
13536 * otherwise it is filtered.
13537 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13538 * @param {Object} scope (optional) The scope of the function (defaults to this)
13540 filterBy : function(fn, scope){
13541 this.snapshot = this.snapshot || this.data;
13542 this.data = this.queryBy(fn, scope||this);
13543 this.fireEvent("datachanged", this);
13547 * Query the records by a specified property.
13548 * @param {String} field A field on your records
13549 * @param {String/RegExp} value Either a string that the field
13550 * should start with or a RegExp to test against the field
13551 * @param {Boolean} anyMatch True to match any part not just the beginning
13552 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13554 query : function(property, value, anyMatch){
13555 var fn = this.createFilterFn(property, value, anyMatch);
13556 return fn ? this.queryBy(fn) : this.data.clone();
13560 * Query by a function. The specified function will be called with each
13561 * record in this data source. If the function returns true the record is included
13563 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13564 * @param {Object} scope (optional) The scope of the function (defaults to this)
13565 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13567 queryBy : function(fn, scope){
13568 var data = this.snapshot || this.data;
13569 return data.filterBy(fn, scope||this);
13573 * Collects unique values for a particular dataIndex from this store.
13574 * @param {String} dataIndex The property to collect
13575 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13576 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13577 * @return {Array} An array of the unique values
13579 collect : function(dataIndex, allowNull, bypassFilter){
13580 var d = (bypassFilter === true && this.snapshot) ?
13581 this.snapshot.items : this.data.items;
13582 var v, sv, r = [], l = {};
13583 for(var i = 0, len = d.length; i < len; i++){
13584 v = d[i].data[dataIndex];
13586 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13595 * Revert to a view of the Record cache with no filtering applied.
13596 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13598 clearFilter : function(suppressEvent){
13599 if(this.snapshot && this.snapshot != this.data){
13600 this.data = this.snapshot;
13601 delete this.snapshot;
13602 if(suppressEvent !== true){
13603 this.fireEvent("datachanged", this);
13609 afterEdit : function(record){
13610 if(this.modified.indexOf(record) == -1){
13611 this.modified.push(record);
13613 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13617 afterReject : function(record){
13618 this.modified.remove(record);
13619 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13623 afterCommit : function(record){
13624 this.modified.remove(record);
13625 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13629 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13630 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13632 commitChanges : function(){
13633 var m = this.modified.slice(0);
13634 this.modified = [];
13635 for(var i = 0, len = m.length; i < len; i++){
13641 * Cancel outstanding changes on all changed records.
13643 rejectChanges : function(){
13644 var m = this.modified.slice(0);
13645 this.modified = [];
13646 for(var i = 0, len = m.length; i < len; i++){
13651 onMetaChange : function(meta, rtype, o){
13652 this.recordType = rtype;
13653 this.fields = rtype.prototype.fields;
13654 delete this.snapshot;
13655 this.sortInfo = meta.sortInfo || this.sortInfo;
13656 this.modified = [];
13657 this.fireEvent('metachange', this, this.reader.meta);
13660 moveIndex : function(data, type)
13662 var index = this.indexOf(data);
13664 var newIndex = index + type;
13668 this.insert(newIndex, data);
13673 * Ext JS Library 1.1.1
13674 * Copyright(c) 2006-2007, Ext JS, LLC.
13676 * Originally Released Under LGPL - original licence link has changed is not relivant.
13679 * <script type="text/javascript">
13683 * @class Roo.data.SimpleStore
13684 * @extends Roo.data.Store
13685 * Small helper class to make creating Stores from Array data easier.
13686 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13687 * @cfg {Array} fields An array of field definition objects, or field name strings.
13688 * @cfg {Object} an existing reader (eg. copied from another store)
13689 * @cfg {Array} data The multi-dimensional array of data
13691 * @param {Object} config
13693 Roo.data.SimpleStore = function(config)
13695 Roo.data.SimpleStore.superclass.constructor.call(this, {
13697 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13700 Roo.data.Record.create(config.fields)
13702 proxy : new Roo.data.MemoryProxy(config.data)
13706 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13708 * Ext JS Library 1.1.1
13709 * Copyright(c) 2006-2007, Ext JS, LLC.
13711 * Originally Released Under LGPL - original licence link has changed is not relivant.
13714 * <script type="text/javascript">
13719 * @extends Roo.data.Store
13720 * @class Roo.data.JsonStore
13721 * Small helper class to make creating Stores for JSON data easier. <br/>
13723 var store = new Roo.data.JsonStore({
13724 url: 'get-images.php',
13726 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13729 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13730 * JsonReader and HttpProxy (unless inline data is provided).</b>
13731 * @cfg {Array} fields An array of field definition objects, or field name strings.
13733 * @param {Object} config
13735 Roo.data.JsonStore = function(c){
13736 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13737 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13738 reader: new Roo.data.JsonReader(c, c.fields)
13741 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13743 * Ext JS Library 1.1.1
13744 * Copyright(c) 2006-2007, Ext JS, LLC.
13746 * Originally Released Under LGPL - original licence link has changed is not relivant.
13749 * <script type="text/javascript">
13753 Roo.data.Field = function(config){
13754 if(typeof config == "string"){
13755 config = {name: config};
13757 Roo.apply(this, config);
13760 this.type = "auto";
13763 var st = Roo.data.SortTypes;
13764 // named sortTypes are supported, here we look them up
13765 if(typeof this.sortType == "string"){
13766 this.sortType = st[this.sortType];
13769 // set default sortType for strings and dates
13770 if(!this.sortType){
13773 this.sortType = st.asUCString;
13776 this.sortType = st.asDate;
13779 this.sortType = st.none;
13784 var stripRe = /[\$,%]/g;
13786 // prebuilt conversion function for this field, instead of
13787 // switching every time we're reading a value
13789 var cv, dateFormat = this.dateFormat;
13794 cv = function(v){ return v; };
13797 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13801 return v !== undefined && v !== null && v !== '' ?
13802 parseInt(String(v).replace(stripRe, ""), 10) : '';
13807 return v !== undefined && v !== null && v !== '' ?
13808 parseFloat(String(v).replace(stripRe, ""), 10) : '';
13813 cv = function(v){ return v === true || v === "true" || v == 1; };
13820 if(v instanceof Date){
13824 if(dateFormat == "timestamp"){
13825 return new Date(v*1000);
13827 return Date.parseDate(v, dateFormat);
13829 var parsed = Date.parse(v);
13830 return parsed ? new Date(parsed) : null;
13839 Roo.data.Field.prototype = {
13847 * Ext JS Library 1.1.1
13848 * Copyright(c) 2006-2007, Ext JS, LLC.
13850 * Originally Released Under LGPL - original licence link has changed is not relivant.
13853 * <script type="text/javascript">
13856 // Base class for reading structured data from a data source. This class is intended to be
13857 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13860 * @class Roo.data.DataReader
13861 * Base class for reading structured data from a data source. This class is intended to be
13862 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13865 Roo.data.DataReader = function(meta, recordType){
13869 this.recordType = recordType instanceof Array ?
13870 Roo.data.Record.create(recordType) : recordType;
13873 Roo.data.DataReader.prototype = {
13876 readerType : 'Data',
13878 * Create an empty record
13879 * @param {Object} data (optional) - overlay some values
13880 * @return {Roo.data.Record} record created.
13882 newRow : function(d) {
13884 this.recordType.prototype.fields.each(function(c) {
13886 case 'int' : da[c.name] = 0; break;
13887 case 'date' : da[c.name] = new Date(); break;
13888 case 'float' : da[c.name] = 0.0; break;
13889 case 'boolean' : da[c.name] = false; break;
13890 default : da[c.name] = ""; break;
13894 return new this.recordType(Roo.apply(da, d));
13900 * Ext JS Library 1.1.1
13901 * Copyright(c) 2006-2007, Ext JS, LLC.
13903 * Originally Released Under LGPL - original licence link has changed is not relivant.
13906 * <script type="text/javascript">
13910 * @class Roo.data.DataProxy
13911 * @extends Roo.data.Observable
13912 * This class is an abstract base class for implementations which provide retrieval of
13913 * unformatted data objects.<br>
13915 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13916 * (of the appropriate type which knows how to parse the data object) to provide a block of
13917 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13919 * Custom implementations must implement the load method as described in
13920 * {@link Roo.data.HttpProxy#load}.
13922 Roo.data.DataProxy = function(){
13925 * @event beforeload
13926 * Fires before a network request is made to retrieve a data object.
13927 * @param {Object} This DataProxy object.
13928 * @param {Object} params The params parameter to the load function.
13933 * Fires before the load method's callback is called.
13934 * @param {Object} This DataProxy object.
13935 * @param {Object} o The data object.
13936 * @param {Object} arg The callback argument object passed to the load function.
13940 * @event loadexception
13941 * Fires if an Exception occurs during data retrieval.
13942 * @param {Object} This DataProxy object.
13943 * @param {Object} o The data object.
13944 * @param {Object} arg The callback argument object passed to the load function.
13945 * @param {Object} e The Exception.
13947 loadexception : true
13949 Roo.data.DataProxy.superclass.constructor.call(this);
13952 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13955 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13959 * Ext JS Library 1.1.1
13960 * Copyright(c) 2006-2007, Ext JS, LLC.
13962 * Originally Released Under LGPL - original licence link has changed is not relivant.
13965 * <script type="text/javascript">
13968 * @class Roo.data.MemoryProxy
13969 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13970 * to the Reader when its load method is called.
13972 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13974 Roo.data.MemoryProxy = function(data){
13978 Roo.data.MemoryProxy.superclass.constructor.call(this);
13982 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13985 * Load data from the requested source (in this case an in-memory
13986 * data object passed to the constructor), read the data object into
13987 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13988 * process that block using the passed callback.
13989 * @param {Object} params This parameter is not used by the MemoryProxy class.
13990 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13991 * object into a block of Roo.data.Records.
13992 * @param {Function} callback The function into which to pass the block of Roo.data.records.
13993 * The function must be passed <ul>
13994 * <li>The Record block object</li>
13995 * <li>The "arg" argument from the load function</li>
13996 * <li>A boolean success indicator</li>
13998 * @param {Object} scope The scope in which to call the callback
13999 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14001 load : function(params, reader, callback, scope, arg){
14002 params = params || {};
14005 result = reader.readRecords(params.data ? params.data :this.data);
14007 this.fireEvent("loadexception", this, arg, null, e);
14008 callback.call(scope, null, arg, false);
14011 callback.call(scope, result, arg, true);
14015 update : function(params, records){
14020 * Ext JS Library 1.1.1
14021 * Copyright(c) 2006-2007, Ext JS, LLC.
14023 * Originally Released Under LGPL - original licence link has changed is not relivant.
14026 * <script type="text/javascript">
14029 * @class Roo.data.HttpProxy
14030 * @extends Roo.data.DataProxy
14031 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14032 * configured to reference a certain URL.<br><br>
14034 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14035 * from which the running page was served.<br><br>
14037 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14039 * Be aware that to enable the browser to parse an XML document, the server must set
14040 * the Content-Type header in the HTTP response to "text/xml".
14042 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14043 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14044 * will be used to make the request.
14046 Roo.data.HttpProxy = function(conn){
14047 Roo.data.HttpProxy.superclass.constructor.call(this);
14048 // is conn a conn config or a real conn?
14050 this.useAjax = !conn || !conn.events;
14054 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14055 // thse are take from connection...
14058 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14061 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14062 * extra parameters to each request made by this object. (defaults to undefined)
14065 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14066 * to each request made by this object. (defaults to undefined)
14069 * @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)
14072 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14075 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14081 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14085 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14086 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14087 * a finer-grained basis than the DataProxy events.
14089 getConnection : function(){
14090 return this.useAjax ? Roo.Ajax : this.conn;
14094 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14095 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14096 * process that block using the passed callback.
14097 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14098 * for the request to the remote server.
14099 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14100 * object into a block of Roo.data.Records.
14101 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14102 * The function must be passed <ul>
14103 * <li>The Record block object</li>
14104 * <li>The "arg" argument from the load function</li>
14105 * <li>A boolean success indicator</li>
14107 * @param {Object} scope The scope in which to call the callback
14108 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14110 load : function(params, reader, callback, scope, arg){
14111 if(this.fireEvent("beforeload", this, params) !== false){
14113 params : params || {},
14115 callback : callback,
14120 callback : this.loadResponse,
14124 Roo.applyIf(o, this.conn);
14125 if(this.activeRequest){
14126 Roo.Ajax.abort(this.activeRequest);
14128 this.activeRequest = Roo.Ajax.request(o);
14130 this.conn.request(o);
14133 callback.call(scope||this, null, arg, false);
14138 loadResponse : function(o, success, response){
14139 delete this.activeRequest;
14141 this.fireEvent("loadexception", this, o, response);
14142 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14147 result = o.reader.read(response);
14149 this.fireEvent("loadexception", this, o, response, e);
14150 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14154 this.fireEvent("load", this, o, o.request.arg);
14155 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14159 update : function(dataSet){
14164 updateResponse : function(dataSet){
14169 * Ext JS Library 1.1.1
14170 * Copyright(c) 2006-2007, Ext JS, LLC.
14172 * Originally Released Under LGPL - original licence link has changed is not relivant.
14175 * <script type="text/javascript">
14179 * @class Roo.data.ScriptTagProxy
14180 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14181 * other than the originating domain of the running page.<br><br>
14183 * <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
14184 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14186 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14187 * source code that is used as the source inside a <script> tag.<br><br>
14189 * In order for the browser to process the returned data, the server must wrap the data object
14190 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14191 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14192 * depending on whether the callback name was passed:
14195 boolean scriptTag = false;
14196 String cb = request.getParameter("callback");
14199 response.setContentType("text/javascript");
14201 response.setContentType("application/x-json");
14203 Writer out = response.getWriter();
14205 out.write(cb + "(");
14207 out.print(dataBlock.toJsonString());
14214 * @param {Object} config A configuration object.
14216 Roo.data.ScriptTagProxy = function(config){
14217 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14218 Roo.apply(this, config);
14219 this.head = document.getElementsByTagName("head")[0];
14222 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14224 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14226 * @cfg {String} url The URL from which to request the data object.
14229 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14233 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14234 * the server the name of the callback function set up by the load call to process the returned data object.
14235 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14236 * javascript output which calls this named function passing the data object as its only parameter.
14238 callbackParam : "callback",
14240 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14241 * name to the request.
14246 * Load data from the configured URL, read the data object into
14247 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14248 * process that block using the passed callback.
14249 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14250 * for the request to the remote server.
14251 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14252 * object into a block of Roo.data.Records.
14253 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14254 * The function must be passed <ul>
14255 * <li>The Record block object</li>
14256 * <li>The "arg" argument from the load function</li>
14257 * <li>A boolean success indicator</li>
14259 * @param {Object} scope The scope in which to call the callback
14260 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14262 load : function(params, reader, callback, scope, arg){
14263 if(this.fireEvent("beforeload", this, params) !== false){
14265 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14267 var url = this.url;
14268 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14270 url += "&_dc=" + (new Date().getTime());
14272 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14275 cb : "stcCallback"+transId,
14276 scriptId : "stcScript"+transId,
14280 callback : callback,
14286 window[trans.cb] = function(o){
14287 conn.handleResponse(o, trans);
14290 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14292 if(this.autoAbort !== false){
14296 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14298 var script = document.createElement("script");
14299 script.setAttribute("src", url);
14300 script.setAttribute("type", "text/javascript");
14301 script.setAttribute("id", trans.scriptId);
14302 this.head.appendChild(script);
14304 this.trans = trans;
14306 callback.call(scope||this, null, arg, false);
14311 isLoading : function(){
14312 return this.trans ? true : false;
14316 * Abort the current server request.
14318 abort : function(){
14319 if(this.isLoading()){
14320 this.destroyTrans(this.trans);
14325 destroyTrans : function(trans, isLoaded){
14326 this.head.removeChild(document.getElementById(trans.scriptId));
14327 clearTimeout(trans.timeoutId);
14329 window[trans.cb] = undefined;
14331 delete window[trans.cb];
14334 // if hasn't been loaded, wait for load to remove it to prevent script error
14335 window[trans.cb] = function(){
14336 window[trans.cb] = undefined;
14338 delete window[trans.cb];
14345 handleResponse : function(o, trans){
14346 this.trans = false;
14347 this.destroyTrans(trans, true);
14350 result = trans.reader.readRecords(o);
14352 this.fireEvent("loadexception", this, o, trans.arg, e);
14353 trans.callback.call(trans.scope||window, null, trans.arg, false);
14356 this.fireEvent("load", this, o, trans.arg);
14357 trans.callback.call(trans.scope||window, result, trans.arg, true);
14361 handleFailure : function(trans){
14362 this.trans = false;
14363 this.destroyTrans(trans, false);
14364 this.fireEvent("loadexception", this, null, trans.arg);
14365 trans.callback.call(trans.scope||window, null, trans.arg, false);
14369 * Ext JS Library 1.1.1
14370 * Copyright(c) 2006-2007, Ext JS, LLC.
14372 * Originally Released Under LGPL - original licence link has changed is not relivant.
14375 * <script type="text/javascript">
14379 * @class Roo.data.JsonReader
14380 * @extends Roo.data.DataReader
14381 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14382 * based on mappings in a provided Roo.data.Record constructor.
14384 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14385 * in the reply previously.
14390 var RecordDef = Roo.data.Record.create([
14391 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14392 {name: 'occupation'} // This field will use "occupation" as the mapping.
14394 var myReader = new Roo.data.JsonReader({
14395 totalProperty: "results", // The property which contains the total dataset size (optional)
14396 root: "rows", // The property which contains an Array of row objects
14397 id: "id" // The property within each row object that provides an ID for the record (optional)
14401 * This would consume a JSON file like this:
14403 { 'results': 2, 'rows': [
14404 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14405 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14408 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14409 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14410 * paged from the remote server.
14411 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14412 * @cfg {String} root name of the property which contains the Array of row objects.
14413 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14414 * @cfg {Array} fields Array of field definition objects
14416 * Create a new JsonReader
14417 * @param {Object} meta Metadata configuration options
14418 * @param {Object} recordType Either an Array of field definition objects,
14419 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14421 Roo.data.JsonReader = function(meta, recordType){
14424 // set some defaults:
14425 Roo.applyIf(meta, {
14426 totalProperty: 'total',
14427 successProperty : 'success',
14432 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14434 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14436 readerType : 'Json',
14439 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14440 * Used by Store query builder to append _requestMeta to params.
14443 metaFromRemote : false,
14445 * This method is only used by a DataProxy which has retrieved data from a remote server.
14446 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14447 * @return {Object} data A data block which is used by an Roo.data.Store object as
14448 * a cache of Roo.data.Records.
14450 read : function(response){
14451 var json = response.responseText;
14453 var o = /* eval:var:o */ eval("("+json+")");
14455 throw {message: "JsonReader.read: Json object not found"};
14461 this.metaFromRemote = true;
14462 this.meta = o.metaData;
14463 this.recordType = Roo.data.Record.create(o.metaData.fields);
14464 this.onMetaChange(this.meta, this.recordType, o);
14466 return this.readRecords(o);
14469 // private function a store will implement
14470 onMetaChange : function(meta, recordType, o){
14477 simpleAccess: function(obj, subsc) {
14484 getJsonAccessor: function(){
14486 return function(expr) {
14488 return(re.test(expr))
14489 ? new Function("obj", "return obj." + expr)
14494 return Roo.emptyFn;
14499 * Create a data block containing Roo.data.Records from an XML document.
14500 * @param {Object} o An object which contains an Array of row objects in the property specified
14501 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14502 * which contains the total size of the dataset.
14503 * @return {Object} data A data block which is used by an Roo.data.Store object as
14504 * a cache of Roo.data.Records.
14506 readRecords : function(o){
14508 * After any data loads, the raw JSON data is available for further custom processing.
14512 var s = this.meta, Record = this.recordType,
14513 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14515 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14517 if(s.totalProperty) {
14518 this.getTotal = this.getJsonAccessor(s.totalProperty);
14520 if(s.successProperty) {
14521 this.getSuccess = this.getJsonAccessor(s.successProperty);
14523 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14525 var g = this.getJsonAccessor(s.id);
14526 this.getId = function(rec) {
14528 return (r === undefined || r === "") ? null : r;
14531 this.getId = function(){return null;};
14534 for(var jj = 0; jj < fl; jj++){
14536 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14537 this.ef[jj] = this.getJsonAccessor(map);
14541 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14542 if(s.totalProperty){
14543 var vt = parseInt(this.getTotal(o), 10);
14548 if(s.successProperty){
14549 var vs = this.getSuccess(o);
14550 if(vs === false || vs === 'false'){
14555 for(var i = 0; i < c; i++){
14558 var id = this.getId(n);
14559 for(var j = 0; j < fl; j++){
14561 var v = this.ef[j](n);
14563 Roo.log('missing convert for ' + f.name);
14567 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14569 var record = new Record(values, id);
14571 records[i] = record;
14577 totalRecords : totalRecords
14580 // used when loading children.. @see loadDataFromChildren
14581 toLoadData: function(rec)
14583 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14584 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14585 return { data : data, total : data.length };
14590 * Ext JS Library 1.1.1
14591 * Copyright(c) 2006-2007, Ext JS, LLC.
14593 * Originally Released Under LGPL - original licence link has changed is not relivant.
14596 * <script type="text/javascript">
14600 * @class Roo.data.ArrayReader
14601 * @extends Roo.data.DataReader
14602 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14603 * Each element of that Array represents a row of data fields. The
14604 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14605 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14609 var RecordDef = Roo.data.Record.create([
14610 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14611 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14613 var myReader = new Roo.data.ArrayReader({
14614 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14618 * This would consume an Array like this:
14620 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14624 * Create a new JsonReader
14625 * @param {Object} meta Metadata configuration options.
14626 * @param {Object|Array} recordType Either an Array of field definition objects
14628 * @cfg {Array} fields Array of field definition objects
14629 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14630 * as specified to {@link Roo.data.Record#create},
14631 * or an {@link Roo.data.Record} object
14634 * created using {@link Roo.data.Record#create}.
14636 Roo.data.ArrayReader = function(meta, recordType)
14638 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14641 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14644 * Create a data block containing Roo.data.Records from an XML document.
14645 * @param {Object} o An Array of row objects which represents the dataset.
14646 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14647 * a cache of Roo.data.Records.
14649 readRecords : function(o)
14651 var sid = this.meta ? this.meta.id : null;
14652 var recordType = this.recordType, fields = recordType.prototype.fields;
14655 for(var i = 0; i < root.length; i++){
14658 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14659 for(var j = 0, jlen = fields.length; j < jlen; j++){
14660 var f = fields.items[j];
14661 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14662 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14664 values[f.name] = v;
14666 var record = new recordType(values, id);
14668 records[records.length] = record;
14672 totalRecords : records.length
14675 // used when loading children.. @see loadDataFromChildren
14676 toLoadData: function(rec)
14678 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14679 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14690 * @class Roo.bootstrap.ComboBox
14691 * @extends Roo.bootstrap.TriggerField
14692 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14693 * @cfg {Boolean} append (true|false) default false
14694 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14695 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14696 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14697 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14698 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14699 * @cfg {Boolean} animate default true
14700 * @cfg {Boolean} emptyResultText only for touch device
14701 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14702 * @cfg {String} emptyTitle default ''
14704 * Create a new ComboBox.
14705 * @param {Object} config Configuration options
14707 Roo.bootstrap.ComboBox = function(config){
14708 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14712 * Fires when the dropdown list is expanded
14713 * @param {Roo.bootstrap.ComboBox} combo This combo box
14718 * Fires when the dropdown list is collapsed
14719 * @param {Roo.bootstrap.ComboBox} combo This combo box
14723 * @event beforeselect
14724 * Fires before a list item is selected. Return false to cancel the selection.
14725 * @param {Roo.bootstrap.ComboBox} combo This combo box
14726 * @param {Roo.data.Record} record The data record returned from the underlying store
14727 * @param {Number} index The index of the selected item in the dropdown list
14729 'beforeselect' : true,
14732 * Fires when a list item is selected
14733 * @param {Roo.bootstrap.ComboBox} combo This combo box
14734 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14735 * @param {Number} index The index of the selected item in the dropdown list
14739 * @event beforequery
14740 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14741 * The event object passed has these properties:
14742 * @param {Roo.bootstrap.ComboBox} combo This combo box
14743 * @param {String} query The query
14744 * @param {Boolean} forceAll true to force "all" query
14745 * @param {Boolean} cancel true to cancel the query
14746 * @param {Object} e The query event object
14748 'beforequery': true,
14751 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14752 * @param {Roo.bootstrap.ComboBox} combo This combo box
14757 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14758 * @param {Roo.bootstrap.ComboBox} combo This combo box
14759 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14764 * Fires when the remove value from the combobox array
14765 * @param {Roo.bootstrap.ComboBox} combo This combo box
14769 * @event afterremove
14770 * Fires when the remove value from the combobox array
14771 * @param {Roo.bootstrap.ComboBox} combo This combo box
14773 'afterremove' : true,
14775 * @event specialfilter
14776 * Fires when specialfilter
14777 * @param {Roo.bootstrap.ComboBox} combo This combo box
14779 'specialfilter' : true,
14782 * Fires when tick the element
14783 * @param {Roo.bootstrap.ComboBox} combo This combo box
14787 * @event touchviewdisplay
14788 * Fires when touch view require special display (default is using displayField)
14789 * @param {Roo.bootstrap.ComboBox} combo This combo box
14790 * @param {Object} cfg set html .
14792 'touchviewdisplay' : true
14797 this.tickItems = [];
14799 this.selectedIndex = -1;
14800 if(this.mode == 'local'){
14801 if(config.queryDelay === undefined){
14802 this.queryDelay = 10;
14804 if(config.minChars === undefined){
14810 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14813 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14814 * rendering into an Roo.Editor, defaults to false)
14817 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14818 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14821 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14824 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14825 * the dropdown list (defaults to undefined, with no header element)
14829 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
14833 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14835 listWidth: undefined,
14837 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14838 * mode = 'remote' or 'text' if mode = 'local')
14840 displayField: undefined,
14843 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14844 * mode = 'remote' or 'value' if mode = 'local').
14845 * Note: use of a valueField requires the user make a selection
14846 * in order for a value to be mapped.
14848 valueField: undefined,
14850 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14855 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14856 * field's data value (defaults to the underlying DOM element's name)
14858 hiddenName: undefined,
14860 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14864 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14866 selectedClass: 'active',
14869 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14873 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14874 * anchor positions (defaults to 'tl-bl')
14876 listAlign: 'tl-bl?',
14878 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14882 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
14883 * query specified by the allQuery config option (defaults to 'query')
14885 triggerAction: 'query',
14887 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14888 * (defaults to 4, does not apply if editable = false)
14892 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14893 * delay (typeAheadDelay) if it matches a known value (defaults to false)
14897 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14898 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14902 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14903 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
14907 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
14908 * when editable = true (defaults to false)
14910 selectOnFocus:false,
14912 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14914 queryParam: 'query',
14916 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
14917 * when mode = 'remote' (defaults to 'Loading...')
14919 loadingText: 'Loading...',
14921 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14925 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14929 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14930 * traditional select (defaults to true)
14934 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14938 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14942 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14943 * listWidth has a higher value)
14947 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14948 * allow the user to set arbitrary text into the field (defaults to false)
14950 forceSelection:false,
14952 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14953 * if typeAhead = true (defaults to 250)
14955 typeAheadDelay : 250,
14957 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14958 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14960 valueNotFoundText : undefined,
14962 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14964 blockFocus : false,
14967 * @cfg {Boolean} disableClear Disable showing of clear button.
14969 disableClear : false,
14971 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
14973 alwaysQuery : false,
14976 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
14981 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14983 invalidClass : "has-warning",
14986 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14988 validClass : "has-success",
14991 * @cfg {Boolean} specialFilter (true|false) special filter default false
14993 specialFilter : false,
14996 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14998 mobileTouchView : true,
15001 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15003 useNativeIOS : false,
15006 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15008 mobile_restrict_height : false,
15010 ios_options : false,
15022 btnPosition : 'right',
15023 triggerList : true,
15024 showToggleBtn : true,
15026 emptyResultText: 'Empty',
15027 triggerText : 'Select',
15030 // element that contains real text value.. (when hidden is used..)
15032 getAutoCreate : function()
15037 * Render classic select for iso
15040 if(Roo.isIOS && this.useNativeIOS){
15041 cfg = this.getAutoCreateNativeIOS();
15049 if(Roo.isTouch && this.mobileTouchView){
15050 cfg = this.getAutoCreateTouchView();
15057 if(!this.tickable){
15058 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15063 * ComboBox with tickable selections
15066 var align = this.labelAlign || this.parentLabelAlign();
15069 cls : 'form-group roo-combobox-tickable' //input-group
15072 var btn_text_select = '';
15073 var btn_text_done = '';
15074 var btn_text_cancel = '';
15076 if (this.btn_text_show) {
15077 btn_text_select = 'Select';
15078 btn_text_done = 'Done';
15079 btn_text_cancel = 'Cancel';
15084 cls : 'tickable-buttons',
15089 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15090 //html : this.triggerText
15091 html: btn_text_select
15097 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15099 html: btn_text_done
15105 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15107 html: btn_text_cancel
15113 buttons.cn.unshift({
15115 cls: 'roo-select2-search-field-input'
15121 Roo.each(buttons.cn, function(c){
15123 c.cls += ' btn-' + _this.size;
15126 if (_this.disabled) {
15133 style : 'display: contents',
15138 cls: 'form-hidden-field'
15142 cls: 'roo-select2-choices',
15146 cls: 'roo-select2-search-field',
15157 cls: 'roo-select2-container input-group roo-select2-container-multi',
15163 // cls: 'typeahead typeahead-long dropdown-menu',
15164 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15169 if(this.hasFeedback && !this.allowBlank){
15173 cls: 'glyphicon form-control-feedback'
15176 combobox.cn.push(feedback);
15183 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15184 tooltip : 'This field is required'
15186 if (Roo.bootstrap.version == 4) {
15189 style : 'display:none'
15192 if (align ==='left' && this.fieldLabel.length) {
15194 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15201 cls : 'control-label col-form-label',
15202 html : this.fieldLabel
15214 var labelCfg = cfg.cn[1];
15215 var contentCfg = cfg.cn[2];
15218 if(this.indicatorpos == 'right'){
15224 cls : 'control-label col-form-label',
15228 html : this.fieldLabel
15244 labelCfg = cfg.cn[0];
15245 contentCfg = cfg.cn[1];
15249 if(this.labelWidth > 12){
15250 labelCfg.style = "width: " + this.labelWidth + 'px';
15253 if(this.labelWidth < 13 && this.labelmd == 0){
15254 this.labelmd = this.labelWidth;
15257 if(this.labellg > 0){
15258 labelCfg.cls += ' col-lg-' + this.labellg;
15259 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15262 if(this.labelmd > 0){
15263 labelCfg.cls += ' col-md-' + this.labelmd;
15264 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15267 if(this.labelsm > 0){
15268 labelCfg.cls += ' col-sm-' + this.labelsm;
15269 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15272 if(this.labelxs > 0){
15273 labelCfg.cls += ' col-xs-' + this.labelxs;
15274 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15278 } else if ( this.fieldLabel.length) {
15279 // Roo.log(" label");
15284 //cls : 'input-group-addon',
15285 html : this.fieldLabel
15290 if(this.indicatorpos == 'right'){
15294 //cls : 'input-group-addon',
15295 html : this.fieldLabel
15305 // Roo.log(" no label && no align");
15312 ['xs','sm','md','lg'].map(function(size){
15313 if (settings[size]) {
15314 cfg.cls += ' col-' + size + '-' + settings[size];
15322 _initEventsCalled : false,
15325 initEvents: function()
15327 if (this._initEventsCalled) { // as we call render... prevent looping...
15330 this._initEventsCalled = true;
15333 throw "can not find store for combo";
15336 this.indicator = this.indicatorEl();
15338 this.store = Roo.factory(this.store, Roo.data);
15339 this.store.parent = this;
15341 // if we are building from html. then this element is so complex, that we can not really
15342 // use the rendered HTML.
15343 // so we have to trash and replace the previous code.
15344 if (Roo.XComponent.build_from_html) {
15345 // remove this element....
15346 var e = this.el.dom, k=0;
15347 while (e ) { e = e.previousSibling; ++k;}
15352 this.rendered = false;
15354 this.render(this.parent().getChildContainer(true), k);
15357 if(Roo.isIOS && this.useNativeIOS){
15358 this.initIOSView();
15366 if(Roo.isTouch && this.mobileTouchView){
15367 this.initTouchView();
15372 this.initTickableEvents();
15376 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15378 if(this.hiddenName){
15380 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15382 this.hiddenField.dom.value =
15383 this.hiddenValue !== undefined ? this.hiddenValue :
15384 this.value !== undefined ? this.value : '';
15386 // prevent input submission
15387 this.el.dom.removeAttribute('name');
15388 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15393 // this.el.dom.setAttribute('autocomplete', 'off');
15396 var cls = 'x-combo-list';
15398 //this.list = new Roo.Layer({
15399 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15405 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15406 _this.list.setWidth(lw);
15409 this.list.on('mouseover', this.onViewOver, this);
15410 this.list.on('mousemove', this.onViewMove, this);
15411 this.list.on('scroll', this.onViewScroll, this);
15414 this.list.swallowEvent('mousewheel');
15415 this.assetHeight = 0;
15418 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15419 this.assetHeight += this.header.getHeight();
15422 this.innerList = this.list.createChild({cls:cls+'-inner'});
15423 this.innerList.on('mouseover', this.onViewOver, this);
15424 this.innerList.on('mousemove', this.onViewMove, this);
15425 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15427 if(this.allowBlank && !this.pageSize && !this.disableClear){
15428 this.footer = this.list.createChild({cls:cls+'-ft'});
15429 this.pageTb = new Roo.Toolbar(this.footer);
15433 this.footer = this.list.createChild({cls:cls+'-ft'});
15434 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15435 {pageSize: this.pageSize});
15439 if (this.pageTb && this.allowBlank && !this.disableClear) {
15441 this.pageTb.add(new Roo.Toolbar.Fill(), {
15442 cls: 'x-btn-icon x-btn-clear',
15444 handler: function()
15447 _this.clearValue();
15448 _this.onSelect(false, -1);
15453 this.assetHeight += this.footer.getHeight();
15458 this.tpl = Roo.bootstrap.version == 4 ?
15459 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15460 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15463 this.view = new Roo.View(this.list, this.tpl, {
15464 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15466 //this.view.wrapEl.setDisplayed(false);
15467 this.view.on('click', this.onViewClick, this);
15470 this.store.on('beforeload', this.onBeforeLoad, this);
15471 this.store.on('load', this.onLoad, this);
15472 this.store.on('loadexception', this.onLoadException, this);
15474 if(this.resizable){
15475 this.resizer = new Roo.Resizable(this.list, {
15476 pinned:true, handles:'se'
15478 this.resizer.on('resize', function(r, w, h){
15479 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15480 this.listWidth = w;
15481 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15482 this.restrictHeight();
15484 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15487 if(!this.editable){
15488 this.editable = true;
15489 this.setEditable(false);
15494 if (typeof(this.events.add.listeners) != 'undefined') {
15496 this.addicon = this.wrap.createChild(
15497 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15499 this.addicon.on('click', function(e) {
15500 this.fireEvent('add', this);
15503 if (typeof(this.events.edit.listeners) != 'undefined') {
15505 this.editicon = this.wrap.createChild(
15506 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15507 if (this.addicon) {
15508 this.editicon.setStyle('margin-left', '40px');
15510 this.editicon.on('click', function(e) {
15512 // we fire even if inothing is selected..
15513 this.fireEvent('edit', this, this.lastData );
15519 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15520 "up" : function(e){
15521 this.inKeyMode = true;
15525 "down" : function(e){
15526 if(!this.isExpanded()){
15527 this.onTriggerClick();
15529 this.inKeyMode = true;
15534 "enter" : function(e){
15535 // this.onViewClick();
15539 if(this.fireEvent("specialkey", this, e)){
15540 this.onViewClick(false);
15546 "esc" : function(e){
15550 "tab" : function(e){
15553 if(this.fireEvent("specialkey", this, e)){
15554 this.onViewClick(false);
15562 doRelay : function(foo, bar, hname){
15563 if(hname == 'down' || this.scope.isExpanded()){
15564 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15573 this.queryDelay = Math.max(this.queryDelay || 10,
15574 this.mode == 'local' ? 10 : 250);
15577 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15579 if(this.typeAhead){
15580 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15582 if(this.editable !== false){
15583 this.inputEl().on("keyup", this.onKeyUp, this);
15585 if(this.forceSelection){
15586 this.inputEl().on('blur', this.doForce, this);
15590 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15591 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15595 initTickableEvents: function()
15599 if(this.hiddenName){
15601 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15603 this.hiddenField.dom.value =
15604 this.hiddenValue !== undefined ? this.hiddenValue :
15605 this.value !== undefined ? this.value : '';
15607 // prevent input submission
15608 this.el.dom.removeAttribute('name');
15609 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15614 // this.list = this.el.select('ul.dropdown-menu',true).first();
15616 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15617 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15618 if(this.triggerList){
15619 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15622 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15623 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15625 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15626 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15628 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15629 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15631 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15632 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15633 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15636 this.cancelBtn.hide();
15641 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15642 _this.list.setWidth(lw);
15645 this.list.on('mouseover', this.onViewOver, this);
15646 this.list.on('mousemove', this.onViewMove, this);
15648 this.list.on('scroll', this.onViewScroll, this);
15651 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15652 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15655 this.view = new Roo.View(this.list, this.tpl, {
15660 selectedClass: this.selectedClass
15663 //this.view.wrapEl.setDisplayed(false);
15664 this.view.on('click', this.onViewClick, this);
15668 this.store.on('beforeload', this.onBeforeLoad, this);
15669 this.store.on('load', this.onLoad, this);
15670 this.store.on('loadexception', this.onLoadException, this);
15673 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15674 "up" : function(e){
15675 this.inKeyMode = true;
15679 "down" : function(e){
15680 this.inKeyMode = true;
15684 "enter" : function(e){
15685 if(this.fireEvent("specialkey", this, e)){
15686 this.onViewClick(false);
15692 "esc" : function(e){
15693 this.onTickableFooterButtonClick(e, false, false);
15696 "tab" : function(e){
15697 this.fireEvent("specialkey", this, e);
15699 this.onTickableFooterButtonClick(e, false, false);
15706 doRelay : function(e, fn, key){
15707 if(this.scope.isExpanded()){
15708 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15717 this.queryDelay = Math.max(this.queryDelay || 10,
15718 this.mode == 'local' ? 10 : 250);
15721 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15723 if(this.typeAhead){
15724 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15727 if(this.editable !== false){
15728 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15731 this.indicator = this.indicatorEl();
15733 if(this.indicator){
15734 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15735 this.indicator.hide();
15740 onDestroy : function(){
15742 this.view.setStore(null);
15743 this.view.el.removeAllListeners();
15744 this.view.el.remove();
15745 this.view.purgeListeners();
15748 this.list.dom.innerHTML = '';
15752 this.store.un('beforeload', this.onBeforeLoad, this);
15753 this.store.un('load', this.onLoad, this);
15754 this.store.un('loadexception', this.onLoadException, this);
15756 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15760 fireKey : function(e){
15761 if(e.isNavKeyPress() && !this.list.isVisible()){
15762 this.fireEvent("specialkey", this, e);
15767 onResize: function(w, h){
15768 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15770 // if(typeof w != 'number'){
15771 // // we do not handle it!?!?
15774 // var tw = this.trigger.getWidth();
15775 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15776 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15778 // this.inputEl().setWidth( this.adjustWidth('input', x));
15780 // //this.trigger.setStyle('left', x+'px');
15782 // if(this.list && this.listWidth === undefined){
15783 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15784 // this.list.setWidth(lw);
15785 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15793 * Allow or prevent the user from directly editing the field text. If false is passed,
15794 * the user will only be able to select from the items defined in the dropdown list. This method
15795 * is the runtime equivalent of setting the 'editable' config option at config time.
15796 * @param {Boolean} value True to allow the user to directly edit the field text
15798 setEditable : function(value){
15799 if(value == this.editable){
15802 this.editable = value;
15804 this.inputEl().dom.setAttribute('readOnly', true);
15805 this.inputEl().on('mousedown', this.onTriggerClick, this);
15806 this.inputEl().addClass('x-combo-noedit');
15808 this.inputEl().dom.setAttribute('readOnly', false);
15809 this.inputEl().un('mousedown', this.onTriggerClick, this);
15810 this.inputEl().removeClass('x-combo-noedit');
15816 onBeforeLoad : function(combo,opts){
15817 if(!this.hasFocus){
15821 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15823 this.restrictHeight();
15824 this.selectedIndex = -1;
15828 onLoad : function(){
15830 this.hasQuery = false;
15832 if(!this.hasFocus){
15836 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15837 this.loading.hide();
15840 if(this.store.getCount() > 0){
15843 this.restrictHeight();
15844 if(this.lastQuery == this.allQuery){
15845 if(this.editable && !this.tickable){
15846 this.inputEl().dom.select();
15850 !this.selectByValue(this.value, true) &&
15853 !this.store.lastOptions ||
15854 typeof(this.store.lastOptions.add) == 'undefined' ||
15855 this.store.lastOptions.add != true
15858 this.select(0, true);
15861 if(this.autoFocus){
15864 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15865 this.taTask.delay(this.typeAheadDelay);
15869 this.onEmptyResults();
15875 onLoadException : function()
15877 this.hasQuery = false;
15879 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15880 this.loading.hide();
15883 if(this.tickable && this.editable){
15888 // only causes errors at present
15889 //Roo.log(this.store.reader.jsonData);
15890 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15892 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15898 onTypeAhead : function(){
15899 if(this.store.getCount() > 0){
15900 var r = this.store.getAt(0);
15901 var newValue = r.data[this.displayField];
15902 var len = newValue.length;
15903 var selStart = this.getRawValue().length;
15905 if(selStart != len){
15906 this.setRawValue(newValue);
15907 this.selectText(selStart, newValue.length);
15913 onSelect : function(record, index){
15915 if(this.fireEvent('beforeselect', this, record, index) !== false){
15917 this.setFromData(index > -1 ? record.data : false);
15920 this.fireEvent('select', this, record, index);
15925 * Returns the currently selected field value or empty string if no value is set.
15926 * @return {String} value The selected value
15928 getValue : function()
15930 if(Roo.isIOS && this.useNativeIOS){
15931 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15935 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15938 if(this.valueField){
15939 return typeof this.value != 'undefined' ? this.value : '';
15941 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15945 getRawValue : function()
15947 if(Roo.isIOS && this.useNativeIOS){
15948 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15951 var v = this.inputEl().getValue();
15957 * Clears any text/value currently set in the field
15959 clearValue : function(){
15961 if(this.hiddenField){
15962 this.hiddenField.dom.value = '';
15965 this.setRawValue('');
15966 this.lastSelectionText = '';
15967 this.lastData = false;
15969 var close = this.closeTriggerEl();
15980 * Sets the specified value into the field. If the value finds a match, the corresponding record text
15981 * will be displayed in the field. If the value does not match the data value of an existing item,
15982 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15983 * Otherwise the field will be blank (although the value will still be set).
15984 * @param {String} value The value to match
15986 setValue : function(v)
15988 if(Roo.isIOS && this.useNativeIOS){
15989 this.setIOSValue(v);
15999 if(this.valueField){
16000 var r = this.findRecord(this.valueField, v);
16002 text = r.data[this.displayField];
16003 }else if(this.valueNotFoundText !== undefined){
16004 text = this.valueNotFoundText;
16007 this.lastSelectionText = text;
16008 if(this.hiddenField){
16009 this.hiddenField.dom.value = v;
16011 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16014 var close = this.closeTriggerEl();
16017 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16023 * @property {Object} the last set data for the element
16028 * Sets the value of the field based on a object which is related to the record format for the store.
16029 * @param {Object} value the value to set as. or false on reset?
16031 setFromData : function(o){
16038 var dv = ''; // display value
16039 var vv = ''; // value value..
16041 if (this.displayField) {
16042 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16044 // this is an error condition!!!
16045 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16048 if(this.valueField){
16049 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16052 var close = this.closeTriggerEl();
16055 if(dv.length || vv * 1 > 0){
16057 this.blockFocus=true;
16063 if(this.hiddenField){
16064 this.hiddenField.dom.value = vv;
16066 this.lastSelectionText = dv;
16067 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16071 // no hidden field.. - we store the value in 'value', but still display
16072 // display field!!!!
16073 this.lastSelectionText = dv;
16074 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16081 reset : function(){
16082 // overridden so that last data is reset..
16089 this.setValue(this.originalValue);
16090 //this.clearInvalid();
16091 this.lastData = false;
16093 this.view.clearSelections();
16099 findRecord : function(prop, value){
16101 if(this.store.getCount() > 0){
16102 this.store.each(function(r){
16103 if(r.data[prop] == value){
16113 getName: function()
16115 // returns hidden if it's set..
16116 if (!this.rendered) {return ''};
16117 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16121 onViewMove : function(e, t){
16122 this.inKeyMode = false;
16126 onViewOver : function(e, t){
16127 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16130 var item = this.view.findItemFromChild(t);
16133 var index = this.view.indexOf(item);
16134 this.select(index, false);
16139 onViewClick : function(view, doFocus, el, e)
16141 var index = this.view.getSelectedIndexes()[0];
16143 var r = this.store.getAt(index);
16147 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16154 Roo.each(this.tickItems, function(v,k){
16156 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16158 _this.tickItems.splice(k, 1);
16160 if(typeof(e) == 'undefined' && view == false){
16161 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16173 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16174 this.tickItems.push(r.data);
16177 if(typeof(e) == 'undefined' && view == false){
16178 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16185 this.onSelect(r, index);
16187 if(doFocus !== false && !this.blockFocus){
16188 this.inputEl().focus();
16193 restrictHeight : function(){
16194 //this.innerList.dom.style.height = '';
16195 //var inner = this.innerList.dom;
16196 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16197 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16198 //this.list.beginUpdate();
16199 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16200 this.list.alignTo(this.inputEl(), this.listAlign);
16201 this.list.alignTo(this.inputEl(), this.listAlign);
16202 //this.list.endUpdate();
16206 onEmptyResults : function(){
16208 if(this.tickable && this.editable){
16209 this.hasFocus = false;
16210 this.restrictHeight();
16218 * Returns true if the dropdown list is expanded, else false.
16220 isExpanded : function(){
16221 return this.list.isVisible();
16225 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16226 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16227 * @param {String} value The data value of the item to select
16228 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16229 * selected item if it is not currently in view (defaults to true)
16230 * @return {Boolean} True if the value matched an item in the list, else false
16232 selectByValue : function(v, scrollIntoView){
16233 if(v !== undefined && v !== null){
16234 var r = this.findRecord(this.valueField || this.displayField, v);
16236 this.select(this.store.indexOf(r), scrollIntoView);
16244 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16245 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16246 * @param {Number} index The zero-based index of the list item to select
16247 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16248 * selected item if it is not currently in view (defaults to true)
16250 select : function(index, scrollIntoView){
16251 this.selectedIndex = index;
16252 this.view.select(index);
16253 if(scrollIntoView !== false){
16254 var el = this.view.getNode(index);
16256 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16259 this.list.scrollChildIntoView(el, false);
16265 selectNext : function(){
16266 var ct = this.store.getCount();
16268 if(this.selectedIndex == -1){
16270 }else if(this.selectedIndex < ct-1){
16271 this.select(this.selectedIndex+1);
16277 selectPrev : function(){
16278 var ct = this.store.getCount();
16280 if(this.selectedIndex == -1){
16282 }else if(this.selectedIndex != 0){
16283 this.select(this.selectedIndex-1);
16289 onKeyUp : function(e){
16290 if(this.editable !== false && !e.isSpecialKey()){
16291 this.lastKey = e.getKey();
16292 this.dqTask.delay(this.queryDelay);
16297 validateBlur : function(){
16298 return !this.list || !this.list.isVisible();
16302 initQuery : function(){
16304 var v = this.getRawValue();
16306 if(this.tickable && this.editable){
16307 v = this.tickableInputEl().getValue();
16314 doForce : function(){
16315 if(this.inputEl().dom.value.length > 0){
16316 this.inputEl().dom.value =
16317 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16323 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16324 * query allowing the query action to be canceled if needed.
16325 * @param {String} query The SQL query to execute
16326 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16327 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16328 * saved in the current store (defaults to false)
16330 doQuery : function(q, forceAll){
16332 if(q === undefined || q === null){
16337 forceAll: forceAll,
16341 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16346 forceAll = qe.forceAll;
16347 if(forceAll === true || (q.length >= this.minChars)){
16349 this.hasQuery = true;
16351 if(this.lastQuery != q || this.alwaysQuery){
16352 this.lastQuery = q;
16353 if(this.mode == 'local'){
16354 this.selectedIndex = -1;
16356 this.store.clearFilter();
16359 if(this.specialFilter){
16360 this.fireEvent('specialfilter', this);
16365 this.store.filter(this.displayField, q);
16368 this.store.fireEvent("datachanged", this.store);
16375 this.store.baseParams[this.queryParam] = q;
16377 var options = {params : this.getParams(q)};
16380 options.add = true;
16381 options.params.start = this.page * this.pageSize;
16384 this.store.load(options);
16387 * this code will make the page width larger, at the beginning, the list not align correctly,
16388 * we should expand the list on onLoad
16389 * so command out it
16394 this.selectedIndex = -1;
16399 this.loadNext = false;
16403 getParams : function(q){
16405 //p[this.queryParam] = q;
16409 p.limit = this.pageSize;
16415 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16417 collapse : function(){
16418 if(!this.isExpanded()){
16424 this.hasFocus = false;
16428 this.cancelBtn.hide();
16429 this.trigger.show();
16432 this.tickableInputEl().dom.value = '';
16433 this.tickableInputEl().blur();
16438 Roo.get(document).un('mousedown', this.collapseIf, this);
16439 Roo.get(document).un('mousewheel', this.collapseIf, this);
16440 if (!this.editable) {
16441 Roo.get(document).un('keydown', this.listKeyPress, this);
16443 this.fireEvent('collapse', this);
16449 collapseIf : function(e){
16450 var in_combo = e.within(this.el);
16451 var in_list = e.within(this.list);
16452 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16454 if (in_combo || in_list || is_list) {
16455 //e.stopPropagation();
16460 this.onTickableFooterButtonClick(e, false, false);
16468 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16470 expand : function(){
16472 if(this.isExpanded() || !this.hasFocus){
16476 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16477 this.list.setWidth(lw);
16483 this.restrictHeight();
16487 this.tickItems = Roo.apply([], this.item);
16490 this.cancelBtn.show();
16491 this.trigger.hide();
16494 this.tickableInputEl().focus();
16499 Roo.get(document).on('mousedown', this.collapseIf, this);
16500 Roo.get(document).on('mousewheel', this.collapseIf, this);
16501 if (!this.editable) {
16502 Roo.get(document).on('keydown', this.listKeyPress, this);
16505 this.fireEvent('expand', this);
16509 // Implements the default empty TriggerField.onTriggerClick function
16510 onTriggerClick : function(e)
16512 Roo.log('trigger click');
16514 if(this.disabled || !this.triggerList){
16519 this.loadNext = false;
16521 if(this.isExpanded()){
16523 if (!this.blockFocus) {
16524 this.inputEl().focus();
16528 this.hasFocus = true;
16529 if(this.triggerAction == 'all') {
16530 this.doQuery(this.allQuery, true);
16532 this.doQuery(this.getRawValue());
16534 if (!this.blockFocus) {
16535 this.inputEl().focus();
16540 onTickableTriggerClick : function(e)
16547 this.loadNext = false;
16548 this.hasFocus = true;
16550 if(this.triggerAction == 'all') {
16551 this.doQuery(this.allQuery, true);
16553 this.doQuery(this.getRawValue());
16557 onSearchFieldClick : function(e)
16559 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16560 this.onTickableFooterButtonClick(e, false, false);
16564 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16569 this.loadNext = false;
16570 this.hasFocus = true;
16572 if(this.triggerAction == 'all') {
16573 this.doQuery(this.allQuery, true);
16575 this.doQuery(this.getRawValue());
16579 listKeyPress : function(e)
16581 //Roo.log('listkeypress');
16582 // scroll to first matching element based on key pres..
16583 if (e.isSpecialKey()) {
16586 var k = String.fromCharCode(e.getKey()).toUpperCase();
16589 var csel = this.view.getSelectedNodes();
16590 var cselitem = false;
16592 var ix = this.view.indexOf(csel[0]);
16593 cselitem = this.store.getAt(ix);
16594 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16600 this.store.each(function(v) {
16602 // start at existing selection.
16603 if (cselitem.id == v.id) {
16609 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16610 match = this.store.indexOf(v);
16616 if (match === false) {
16617 return true; // no more action?
16620 this.view.select(match);
16621 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16622 sn.scrollIntoView(sn.dom.parentNode, false);
16625 onViewScroll : function(e, t){
16627 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){
16631 this.hasQuery = true;
16633 this.loading = this.list.select('.loading', true).first();
16635 if(this.loading === null){
16636 this.list.createChild({
16638 cls: 'loading roo-select2-more-results roo-select2-active',
16639 html: 'Loading more results...'
16642 this.loading = this.list.select('.loading', true).first();
16644 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16646 this.loading.hide();
16649 this.loading.show();
16654 this.loadNext = true;
16656 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16661 addItem : function(o)
16663 var dv = ''; // display value
16665 if (this.displayField) {
16666 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16668 // this is an error condition!!!
16669 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16676 var choice = this.choices.createChild({
16678 cls: 'roo-select2-search-choice',
16687 cls: 'roo-select2-search-choice-close fa fa-times',
16692 }, this.searchField);
16694 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16696 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16704 this.inputEl().dom.value = '';
16709 onRemoveItem : function(e, _self, o)
16711 e.preventDefault();
16713 this.lastItem = Roo.apply([], this.item);
16715 var index = this.item.indexOf(o.data) * 1;
16718 Roo.log('not this item?!');
16722 this.item.splice(index, 1);
16727 this.fireEvent('remove', this, e);
16733 syncValue : function()
16735 if(!this.item.length){
16742 Roo.each(this.item, function(i){
16743 if(_this.valueField){
16744 value.push(i[_this.valueField]);
16751 this.value = value.join(',');
16753 if(this.hiddenField){
16754 this.hiddenField.dom.value = this.value;
16757 this.store.fireEvent("datachanged", this.store);
16762 clearItem : function()
16764 if(!this.multiple){
16770 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16778 if(this.tickable && !Roo.isTouch){
16779 this.view.refresh();
16783 inputEl: function ()
16785 if(Roo.isIOS && this.useNativeIOS){
16786 return this.el.select('select.roo-ios-select', true).first();
16789 if(Roo.isTouch && this.mobileTouchView){
16790 return this.el.select('input.form-control',true).first();
16794 return this.searchField;
16797 return this.el.select('input.form-control',true).first();
16800 onTickableFooterButtonClick : function(e, btn, el)
16802 e.preventDefault();
16804 this.lastItem = Roo.apply([], this.item);
16806 if(btn && btn.name == 'cancel'){
16807 this.tickItems = Roo.apply([], this.item);
16816 Roo.each(this.tickItems, function(o){
16824 validate : function()
16826 if(this.getVisibilityEl().hasClass('hidden')){
16830 var v = this.getRawValue();
16833 v = this.getValue();
16836 if(this.disabled || this.allowBlank || v.length){
16841 this.markInvalid();
16845 tickableInputEl : function()
16847 if(!this.tickable || !this.editable){
16848 return this.inputEl();
16851 return this.inputEl().select('.roo-select2-search-field-input', true).first();
16855 getAutoCreateTouchView : function()
16860 cls: 'form-group' //input-group
16866 type : this.inputType,
16867 cls : 'form-control x-combo-noedit',
16868 autocomplete: 'new-password',
16869 placeholder : this.placeholder || '',
16874 input.name = this.name;
16878 input.cls += ' input-' + this.size;
16881 if (this.disabled) {
16882 input.disabled = true;
16893 inputblock.cls += ' input-group';
16895 inputblock.cn.unshift({
16897 cls : 'input-group-addon input-group-prepend input-group-text',
16902 if(this.removable && !this.multiple){
16903 inputblock.cls += ' roo-removable';
16905 inputblock.cn.push({
16908 cls : 'roo-combo-removable-btn close'
16912 if(this.hasFeedback && !this.allowBlank){
16914 inputblock.cls += ' has-feedback';
16916 inputblock.cn.push({
16918 cls: 'glyphicon form-control-feedback'
16925 inputblock.cls += (this.before) ? '' : ' input-group';
16927 inputblock.cn.push({
16929 cls : 'input-group-addon input-group-append input-group-text',
16935 var ibwrap = inputblock;
16940 cls: 'roo-select2-choices',
16944 cls: 'roo-select2-search-field',
16957 cls: 'roo-select2-container input-group roo-touchview-combobox ',
16962 cls: 'form-hidden-field'
16968 if(!this.multiple && this.showToggleBtn){
16974 if (this.caret != false) {
16977 cls: 'fa fa-' + this.caret
16984 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16986 Roo.bootstrap.version == 3 ? caret : '',
16989 cls: 'combobox-clear',
17003 combobox.cls += ' roo-select2-container-multi';
17006 var align = this.labelAlign || this.parentLabelAlign();
17008 if (align ==='left' && this.fieldLabel.length) {
17013 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17014 tooltip : 'This field is required'
17018 cls : 'control-label col-form-label',
17019 html : this.fieldLabel
17030 var labelCfg = cfg.cn[1];
17031 var contentCfg = cfg.cn[2];
17034 if(this.indicatorpos == 'right'){
17039 cls : 'control-label col-form-label',
17043 html : this.fieldLabel
17047 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17048 tooltip : 'This field is required'
17061 labelCfg = cfg.cn[0];
17062 contentCfg = cfg.cn[1];
17067 if(this.labelWidth > 12){
17068 labelCfg.style = "width: " + this.labelWidth + 'px';
17071 if(this.labelWidth < 13 && this.labelmd == 0){
17072 this.labelmd = this.labelWidth;
17075 if(this.labellg > 0){
17076 labelCfg.cls += ' col-lg-' + this.labellg;
17077 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17080 if(this.labelmd > 0){
17081 labelCfg.cls += ' col-md-' + this.labelmd;
17082 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17085 if(this.labelsm > 0){
17086 labelCfg.cls += ' col-sm-' + this.labelsm;
17087 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17090 if(this.labelxs > 0){
17091 labelCfg.cls += ' col-xs-' + this.labelxs;
17092 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17096 } else if ( this.fieldLabel.length) {
17100 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17101 tooltip : 'This field is required'
17105 cls : 'control-label',
17106 html : this.fieldLabel
17117 if(this.indicatorpos == 'right'){
17121 cls : 'control-label',
17122 html : this.fieldLabel,
17126 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17127 tooltip : 'This field is required'
17144 var settings = this;
17146 ['xs','sm','md','lg'].map(function(size){
17147 if (settings[size]) {
17148 cfg.cls += ' col-' + size + '-' + settings[size];
17155 initTouchView : function()
17157 this.renderTouchView();
17159 this.touchViewEl.on('scroll', function(){
17160 this.el.dom.scrollTop = 0;
17163 this.originalValue = this.getValue();
17165 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17167 this.inputEl().on("click", this.showTouchView, this);
17168 if (this.triggerEl) {
17169 this.triggerEl.on("click", this.showTouchView, this);
17173 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17174 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17176 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17178 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17179 this.store.on('load', this.onTouchViewLoad, this);
17180 this.store.on('loadexception', this.onTouchViewLoadException, this);
17182 if(this.hiddenName){
17184 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17186 this.hiddenField.dom.value =
17187 this.hiddenValue !== undefined ? this.hiddenValue :
17188 this.value !== undefined ? this.value : '';
17190 this.el.dom.removeAttribute('name');
17191 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17195 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17196 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17199 if(this.removable && !this.multiple){
17200 var close = this.closeTriggerEl();
17202 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17203 close.on('click', this.removeBtnClick, this, close);
17207 * fix the bug in Safari iOS8
17209 this.inputEl().on("focus", function(e){
17210 document.activeElement.blur();
17213 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17220 renderTouchView : function()
17222 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17223 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17225 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17226 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17228 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17229 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17230 this.touchViewBodyEl.setStyle('overflow', 'auto');
17232 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17233 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17235 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17236 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17240 showTouchView : function()
17246 this.touchViewHeaderEl.hide();
17248 if(this.modalTitle.length){
17249 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17250 this.touchViewHeaderEl.show();
17253 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17254 this.touchViewEl.show();
17256 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17258 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17259 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17261 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17263 if(this.modalTitle.length){
17264 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17267 this.touchViewBodyEl.setHeight(bodyHeight);
17271 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17273 this.touchViewEl.addClass('in');
17276 if(this._touchViewMask){
17277 Roo.get(document.body).addClass("x-body-masked");
17278 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17279 this._touchViewMask.setStyle('z-index', 10000);
17280 this._touchViewMask.addClass('show');
17283 this.doTouchViewQuery();
17287 hideTouchView : function()
17289 this.touchViewEl.removeClass('in');
17293 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17295 this.touchViewEl.setStyle('display', 'none');
17298 if(this._touchViewMask){
17299 this._touchViewMask.removeClass('show');
17300 Roo.get(document.body).removeClass("x-body-masked");
17304 setTouchViewValue : function()
17311 Roo.each(this.tickItems, function(o){
17316 this.hideTouchView();
17319 doTouchViewQuery : function()
17328 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17332 if(!this.alwaysQuery || this.mode == 'local'){
17333 this.onTouchViewLoad();
17340 onTouchViewBeforeLoad : function(combo,opts)
17346 onTouchViewLoad : function()
17348 if(this.store.getCount() < 1){
17349 this.onTouchViewEmptyResults();
17353 this.clearTouchView();
17355 var rawValue = this.getRawValue();
17357 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17359 this.tickItems = [];
17361 this.store.data.each(function(d, rowIndex){
17362 var row = this.touchViewListGroup.createChild(template);
17364 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17365 row.addClass(d.data.cls);
17368 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17371 html : d.data[this.displayField]
17374 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17375 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17378 row.removeClass('selected');
17379 if(!this.multiple && this.valueField &&
17380 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17383 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17384 row.addClass('selected');
17387 if(this.multiple && this.valueField &&
17388 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17392 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17393 this.tickItems.push(d.data);
17396 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17400 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17402 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17404 if(this.modalTitle.length){
17405 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17408 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17410 if(this.mobile_restrict_height && listHeight < bodyHeight){
17411 this.touchViewBodyEl.setHeight(listHeight);
17416 if(firstChecked && listHeight > bodyHeight){
17417 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17422 onTouchViewLoadException : function()
17424 this.hideTouchView();
17427 onTouchViewEmptyResults : function()
17429 this.clearTouchView();
17431 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17433 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17437 clearTouchView : function()
17439 this.touchViewListGroup.dom.innerHTML = '';
17442 onTouchViewClick : function(e, el, o)
17444 e.preventDefault();
17447 var rowIndex = o.rowIndex;
17449 var r = this.store.getAt(rowIndex);
17451 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17453 if(!this.multiple){
17454 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17455 c.dom.removeAttribute('checked');
17458 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17460 this.setFromData(r.data);
17462 var close = this.closeTriggerEl();
17468 this.hideTouchView();
17470 this.fireEvent('select', this, r, rowIndex);
17475 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17476 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17477 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17481 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17482 this.addItem(r.data);
17483 this.tickItems.push(r.data);
17487 getAutoCreateNativeIOS : function()
17490 cls: 'form-group' //input-group,
17495 cls : 'roo-ios-select'
17499 combobox.name = this.name;
17502 if (this.disabled) {
17503 combobox.disabled = true;
17506 var settings = this;
17508 ['xs','sm','md','lg'].map(function(size){
17509 if (settings[size]) {
17510 cfg.cls += ' col-' + size + '-' + settings[size];
17520 initIOSView : function()
17522 this.store.on('load', this.onIOSViewLoad, this);
17527 onIOSViewLoad : function()
17529 if(this.store.getCount() < 1){
17533 this.clearIOSView();
17535 if(this.allowBlank) {
17537 var default_text = '-- SELECT --';
17539 if(this.placeholder.length){
17540 default_text = this.placeholder;
17543 if(this.emptyTitle.length){
17544 default_text += ' - ' + this.emptyTitle + ' -';
17547 var opt = this.inputEl().createChild({
17550 html : default_text
17554 o[this.valueField] = 0;
17555 o[this.displayField] = default_text;
17557 this.ios_options.push({
17564 this.store.data.each(function(d, rowIndex){
17568 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17569 html = d.data[this.displayField];
17574 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17575 value = d.data[this.valueField];
17584 if(this.value == d.data[this.valueField]){
17585 option['selected'] = true;
17588 var opt = this.inputEl().createChild(option);
17590 this.ios_options.push({
17597 this.inputEl().on('change', function(){
17598 this.fireEvent('select', this);
17603 clearIOSView: function()
17605 this.inputEl().dom.innerHTML = '';
17607 this.ios_options = [];
17610 setIOSValue: function(v)
17614 if(!this.ios_options){
17618 Roo.each(this.ios_options, function(opts){
17620 opts.el.dom.removeAttribute('selected');
17622 if(opts.data[this.valueField] != v){
17626 opts.el.dom.setAttribute('selected', true);
17632 * @cfg {Boolean} grow
17636 * @cfg {Number} growMin
17640 * @cfg {Number} growMax
17649 Roo.apply(Roo.bootstrap.ComboBox, {
17653 cls: 'modal-header',
17675 cls: 'list-group-item',
17679 cls: 'roo-combobox-list-group-item-value'
17683 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17697 listItemCheckbox : {
17699 cls: 'list-group-item',
17703 cls: 'roo-combobox-list-group-item-value'
17707 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17723 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17728 cls: 'modal-footer',
17736 cls: 'col-xs-6 text-left',
17739 cls: 'btn btn-danger roo-touch-view-cancel',
17745 cls: 'col-xs-6 text-right',
17748 cls: 'btn btn-success roo-touch-view-ok',
17759 Roo.apply(Roo.bootstrap.ComboBox, {
17761 touchViewTemplate : {
17763 cls: 'modal fade roo-combobox-touch-view',
17767 cls: 'modal-dialog',
17768 style : 'position:fixed', // we have to fix position....
17772 cls: 'modal-content',
17774 Roo.bootstrap.ComboBox.header,
17775 Roo.bootstrap.ComboBox.body,
17776 Roo.bootstrap.ComboBox.footer
17785 * Ext JS Library 1.1.1
17786 * Copyright(c) 2006-2007, Ext JS, LLC.
17788 * Originally Released Under LGPL - original licence link has changed is not relivant.
17791 * <script type="text/javascript">
17796 * @extends Roo.util.Observable
17797 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17798 * This class also supports single and multi selection modes. <br>
17799 * Create a data model bound view:
17801 var store = new Roo.data.Store(...);
17803 var view = new Roo.View({
17805 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
17807 singleSelect: true,
17808 selectedClass: "ydataview-selected",
17812 // listen for node click?
17813 view.on("click", function(vw, index, node, e){
17814 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17818 dataModel.load("foobar.xml");
17820 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17822 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17823 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17825 * Note: old style constructor is still suported (container, template, config)
17828 * Create a new View
17829 * @param {Object} config The config object
17832 Roo.View = function(config, depreciated_tpl, depreciated_config){
17834 this.parent = false;
17836 if (typeof(depreciated_tpl) == 'undefined') {
17837 // new way.. - universal constructor.
17838 Roo.apply(this, config);
17839 this.el = Roo.get(this.el);
17842 this.el = Roo.get(config);
17843 this.tpl = depreciated_tpl;
17844 Roo.apply(this, depreciated_config);
17846 this.wrapEl = this.el.wrap().wrap();
17847 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17850 if(typeof(this.tpl) == "string"){
17851 this.tpl = new Roo.Template(this.tpl);
17853 // support xtype ctors..
17854 this.tpl = new Roo.factory(this.tpl, Roo);
17858 this.tpl.compile();
17863 * @event beforeclick
17864 * Fires before a click is processed. Returns false to cancel the default action.
17865 * @param {Roo.View} this
17866 * @param {Number} index The index of the target node
17867 * @param {HTMLElement} node The target node
17868 * @param {Roo.EventObject} e The raw event object
17870 "beforeclick" : true,
17873 * Fires when a template node is clicked.
17874 * @param {Roo.View} this
17875 * @param {Number} index The index of the target node
17876 * @param {HTMLElement} node The target node
17877 * @param {Roo.EventObject} e The raw event object
17882 * Fires when a template node is double clicked.
17883 * @param {Roo.View} this
17884 * @param {Number} index The index of the target node
17885 * @param {HTMLElement} node The target node
17886 * @param {Roo.EventObject} e The raw event object
17890 * @event contextmenu
17891 * Fires when a template node is right clicked.
17892 * @param {Roo.View} this
17893 * @param {Number} index The index of the target node
17894 * @param {HTMLElement} node The target node
17895 * @param {Roo.EventObject} e The raw event object
17897 "contextmenu" : true,
17899 * @event selectionchange
17900 * Fires when the selected nodes change.
17901 * @param {Roo.View} this
17902 * @param {Array} selections Array of the selected nodes
17904 "selectionchange" : true,
17907 * @event beforeselect
17908 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17909 * @param {Roo.View} this
17910 * @param {HTMLElement} node The node to be selected
17911 * @param {Array} selections Array of currently selected nodes
17913 "beforeselect" : true,
17915 * @event preparedata
17916 * Fires on every row to render, to allow you to change the data.
17917 * @param {Roo.View} this
17918 * @param {Object} data to be rendered (change this)
17920 "preparedata" : true
17928 "click": this.onClick,
17929 "dblclick": this.onDblClick,
17930 "contextmenu": this.onContextMenu,
17934 this.selections = [];
17936 this.cmp = new Roo.CompositeElementLite([]);
17938 this.store = Roo.factory(this.store, Roo.data);
17939 this.setStore(this.store, true);
17942 if ( this.footer && this.footer.xtype) {
17944 var fctr = this.wrapEl.appendChild(document.createElement("div"));
17946 this.footer.dataSource = this.store;
17947 this.footer.container = fctr;
17948 this.footer = Roo.factory(this.footer, Roo);
17949 fctr.insertFirst(this.el);
17951 // this is a bit insane - as the paging toolbar seems to detach the el..
17952 // dom.parentNode.parentNode.parentNode
17953 // they get detached?
17957 Roo.View.superclass.constructor.call(this);
17962 Roo.extend(Roo.View, Roo.util.Observable, {
17965 * @cfg {Roo.data.Store} store Data store to load data from.
17970 * @cfg {String|Roo.Element} el The container element.
17975 * @cfg {String|Roo.Template} tpl The template used by this View
17979 * @cfg {String} dataName the named area of the template to use as the data area
17980 * Works with domtemplates roo-name="name"
17984 * @cfg {String} selectedClass The css class to add to selected nodes
17986 selectedClass : "x-view-selected",
17988 * @cfg {String} emptyText The empty text to show when nothing is loaded.
17993 * @cfg {String} text to display on mask (default Loading)
17997 * @cfg {Boolean} multiSelect Allow multiple selection
17999 multiSelect : false,
18001 * @cfg {Boolean} singleSelect Allow single selection
18003 singleSelect: false,
18006 * @cfg {Boolean} toggleSelect - selecting
18008 toggleSelect : false,
18011 * @cfg {Boolean} tickable - selecting
18016 * Returns the element this view is bound to.
18017 * @return {Roo.Element}
18019 getEl : function(){
18020 return this.wrapEl;
18026 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18028 refresh : function(){
18029 //Roo.log('refresh');
18032 // if we are using something like 'domtemplate', then
18033 // the what gets used is:
18034 // t.applySubtemplate(NAME, data, wrapping data..)
18035 // the outer template then get' applied with
18036 // the store 'extra data'
18037 // and the body get's added to the
18038 // roo-name="data" node?
18039 // <span class='roo-tpl-{name}'></span> ?????
18043 this.clearSelections();
18044 this.el.update("");
18046 var records = this.store.getRange();
18047 if(records.length < 1) {
18049 // is this valid?? = should it render a template??
18051 this.el.update(this.emptyText);
18055 if (this.dataName) {
18056 this.el.update(t.apply(this.store.meta)); //????
18057 el = this.el.child('.roo-tpl-' + this.dataName);
18060 for(var i = 0, len = records.length; i < len; i++){
18061 var data = this.prepareData(records[i].data, i, records[i]);
18062 this.fireEvent("preparedata", this, data, i, records[i]);
18064 var d = Roo.apply({}, data);
18067 Roo.apply(d, {'roo-id' : Roo.id()});
18071 Roo.each(this.parent.item, function(item){
18072 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18075 Roo.apply(d, {'roo-data-checked' : 'checked'});
18079 html[html.length] = Roo.util.Format.trim(
18081 t.applySubtemplate(this.dataName, d, this.store.meta) :
18088 el.update(html.join(""));
18089 this.nodes = el.dom.childNodes;
18090 this.updateIndexes(0);
18095 * Function to override to reformat the data that is sent to
18096 * the template for each node.
18097 * DEPRICATED - use the preparedata event handler.
18098 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18099 * a JSON object for an UpdateManager bound view).
18101 prepareData : function(data, index, record)
18103 this.fireEvent("preparedata", this, data, index, record);
18107 onUpdate : function(ds, record){
18108 // Roo.log('on update');
18109 this.clearSelections();
18110 var index = this.store.indexOf(record);
18111 var n = this.nodes[index];
18112 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18113 n.parentNode.removeChild(n);
18114 this.updateIndexes(index, index);
18120 onAdd : function(ds, records, index)
18122 //Roo.log(['on Add', ds, records, index] );
18123 this.clearSelections();
18124 if(this.nodes.length == 0){
18128 var n = this.nodes[index];
18129 for(var i = 0, len = records.length; i < len; i++){
18130 var d = this.prepareData(records[i].data, i, records[i]);
18132 this.tpl.insertBefore(n, d);
18135 this.tpl.append(this.el, d);
18138 this.updateIndexes(index);
18141 onRemove : function(ds, record, index){
18142 // Roo.log('onRemove');
18143 this.clearSelections();
18144 var el = this.dataName ?
18145 this.el.child('.roo-tpl-' + this.dataName) :
18148 el.dom.removeChild(this.nodes[index]);
18149 this.updateIndexes(index);
18153 * Refresh an individual node.
18154 * @param {Number} index
18156 refreshNode : function(index){
18157 this.onUpdate(this.store, this.store.getAt(index));
18160 updateIndexes : function(startIndex, endIndex){
18161 var ns = this.nodes;
18162 startIndex = startIndex || 0;
18163 endIndex = endIndex || ns.length - 1;
18164 for(var i = startIndex; i <= endIndex; i++){
18165 ns[i].nodeIndex = i;
18170 * Changes the data store this view uses and refresh the view.
18171 * @param {Store} store
18173 setStore : function(store, initial){
18174 if(!initial && this.store){
18175 this.store.un("datachanged", this.refresh);
18176 this.store.un("add", this.onAdd);
18177 this.store.un("remove", this.onRemove);
18178 this.store.un("update", this.onUpdate);
18179 this.store.un("clear", this.refresh);
18180 this.store.un("beforeload", this.onBeforeLoad);
18181 this.store.un("load", this.onLoad);
18182 this.store.un("loadexception", this.onLoad);
18186 store.on("datachanged", this.refresh, this);
18187 store.on("add", this.onAdd, this);
18188 store.on("remove", this.onRemove, this);
18189 store.on("update", this.onUpdate, this);
18190 store.on("clear", this.refresh, this);
18191 store.on("beforeload", this.onBeforeLoad, this);
18192 store.on("load", this.onLoad, this);
18193 store.on("loadexception", this.onLoad, this);
18201 * onbeforeLoad - masks the loading area.
18204 onBeforeLoad : function(store,opts)
18206 //Roo.log('onBeforeLoad');
18208 this.el.update("");
18210 this.el.mask(this.mask ? this.mask : "Loading" );
18212 onLoad : function ()
18219 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18220 * @param {HTMLElement} node
18221 * @return {HTMLElement} The template node
18223 findItemFromChild : function(node){
18224 var el = this.dataName ?
18225 this.el.child('.roo-tpl-' + this.dataName,true) :
18228 if(!node || node.parentNode == el){
18231 var p = node.parentNode;
18232 while(p && p != el){
18233 if(p.parentNode == el){
18242 onClick : function(e){
18243 var item = this.findItemFromChild(e.getTarget());
18245 var index = this.indexOf(item);
18246 if(this.onItemClick(item, index, e) !== false){
18247 this.fireEvent("click", this, index, item, e);
18250 this.clearSelections();
18255 onContextMenu : function(e){
18256 var item = this.findItemFromChild(e.getTarget());
18258 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18263 onDblClick : function(e){
18264 var item = this.findItemFromChild(e.getTarget());
18266 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18270 onItemClick : function(item, index, e)
18272 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18275 if (this.toggleSelect) {
18276 var m = this.isSelected(item) ? 'unselect' : 'select';
18279 _t[m](item, true, false);
18282 if(this.multiSelect || this.singleSelect){
18283 if(this.multiSelect && e.shiftKey && this.lastSelection){
18284 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18286 this.select(item, this.multiSelect && e.ctrlKey);
18287 this.lastSelection = item;
18290 if(!this.tickable){
18291 e.preventDefault();
18299 * Get the number of selected nodes.
18302 getSelectionCount : function(){
18303 return this.selections.length;
18307 * Get the currently selected nodes.
18308 * @return {Array} An array of HTMLElements
18310 getSelectedNodes : function(){
18311 return this.selections;
18315 * Get the indexes of the selected nodes.
18318 getSelectedIndexes : function(){
18319 var indexes = [], s = this.selections;
18320 for(var i = 0, len = s.length; i < len; i++){
18321 indexes.push(s[i].nodeIndex);
18327 * Clear all selections
18328 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18330 clearSelections : function(suppressEvent){
18331 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18332 this.cmp.elements = this.selections;
18333 this.cmp.removeClass(this.selectedClass);
18334 this.selections = [];
18335 if(!suppressEvent){
18336 this.fireEvent("selectionchange", this, this.selections);
18342 * Returns true if the passed node is selected
18343 * @param {HTMLElement/Number} node The node or node index
18344 * @return {Boolean}
18346 isSelected : function(node){
18347 var s = this.selections;
18351 node = this.getNode(node);
18352 return s.indexOf(node) !== -1;
18357 * @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
18358 * @param {Boolean} keepExisting (optional) true to keep existing selections
18359 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18361 select : function(nodeInfo, keepExisting, suppressEvent){
18362 if(nodeInfo instanceof Array){
18364 this.clearSelections(true);
18366 for(var i = 0, len = nodeInfo.length; i < len; i++){
18367 this.select(nodeInfo[i], true, true);
18371 var node = this.getNode(nodeInfo);
18372 if(!node || this.isSelected(node)){
18373 return; // already selected.
18376 this.clearSelections(true);
18379 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18380 Roo.fly(node).addClass(this.selectedClass);
18381 this.selections.push(node);
18382 if(!suppressEvent){
18383 this.fireEvent("selectionchange", this, this.selections);
18391 * @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
18392 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18393 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18395 unselect : function(nodeInfo, keepExisting, suppressEvent)
18397 if(nodeInfo instanceof Array){
18398 Roo.each(this.selections, function(s) {
18399 this.unselect(s, nodeInfo);
18403 var node = this.getNode(nodeInfo);
18404 if(!node || !this.isSelected(node)){
18405 //Roo.log("not selected");
18406 return; // not selected.
18410 Roo.each(this.selections, function(s) {
18412 Roo.fly(node).removeClass(this.selectedClass);
18419 this.selections= ns;
18420 this.fireEvent("selectionchange", this, this.selections);
18424 * Gets a template node.
18425 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18426 * @return {HTMLElement} The node or null if it wasn't found
18428 getNode : function(nodeInfo){
18429 if(typeof nodeInfo == "string"){
18430 return document.getElementById(nodeInfo);
18431 }else if(typeof nodeInfo == "number"){
18432 return this.nodes[nodeInfo];
18438 * Gets a range template nodes.
18439 * @param {Number} startIndex
18440 * @param {Number} endIndex
18441 * @return {Array} An array of nodes
18443 getNodes : function(start, end){
18444 var ns = this.nodes;
18445 start = start || 0;
18446 end = typeof end == "undefined" ? ns.length - 1 : end;
18449 for(var i = start; i <= end; i++){
18453 for(var i = start; i >= end; i--){
18461 * Finds the index of the passed node
18462 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18463 * @return {Number} The index of the node or -1
18465 indexOf : function(node){
18466 node = this.getNode(node);
18467 if(typeof node.nodeIndex == "number"){
18468 return node.nodeIndex;
18470 var ns = this.nodes;
18471 for(var i = 0, len = ns.length; i < len; i++){
18482 * based on jquery fullcalendar
18486 Roo.bootstrap = Roo.bootstrap || {};
18488 * @class Roo.bootstrap.Calendar
18489 * @extends Roo.bootstrap.Component
18490 * Bootstrap Calendar class
18491 * @cfg {Boolean} loadMask (true|false) default false
18492 * @cfg {Object} header generate the user specific header of the calendar, default false
18495 * Create a new Container
18496 * @param {Object} config The config object
18501 Roo.bootstrap.Calendar = function(config){
18502 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18506 * Fires when a date is selected
18507 * @param {DatePicker} this
18508 * @param {Date} date The selected date
18512 * @event monthchange
18513 * Fires when the displayed month changes
18514 * @param {DatePicker} this
18515 * @param {Date} date The selected month
18517 'monthchange': true,
18519 * @event evententer
18520 * Fires when mouse over an event
18521 * @param {Calendar} this
18522 * @param {event} Event
18524 'evententer': true,
18526 * @event eventleave
18527 * Fires when the mouse leaves an
18528 * @param {Calendar} this
18531 'eventleave': true,
18533 * @event eventclick
18534 * Fires when the mouse click an
18535 * @param {Calendar} this
18544 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18547 * @cfg {Number} startDay
18548 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18556 getAutoCreate : function(){
18559 var fc_button = function(name, corner, style, content ) {
18560 return Roo.apply({},{
18562 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18564 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18567 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18578 style : 'width:100%',
18585 cls : 'fc-header-left',
18587 fc_button('prev', 'left', 'arrow', '‹' ),
18588 fc_button('next', 'right', 'arrow', '›' ),
18589 { tag: 'span', cls: 'fc-header-space' },
18590 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18598 cls : 'fc-header-center',
18602 cls: 'fc-header-title',
18605 html : 'month / year'
18613 cls : 'fc-header-right',
18615 /* fc_button('month', 'left', '', 'month' ),
18616 fc_button('week', '', '', 'week' ),
18617 fc_button('day', 'right', '', 'day' )
18629 header = this.header;
18632 var cal_heads = function() {
18634 // fixme - handle this.
18636 for (var i =0; i < Date.dayNames.length; i++) {
18637 var d = Date.dayNames[i];
18640 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18641 html : d.substring(0,3)
18645 ret[0].cls += ' fc-first';
18646 ret[6].cls += ' fc-last';
18649 var cal_cell = function(n) {
18652 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18657 cls: 'fc-day-number',
18661 cls: 'fc-day-content',
18665 style: 'position: relative;' // height: 17px;
18677 var cal_rows = function() {
18680 for (var r = 0; r < 6; r++) {
18687 for (var i =0; i < Date.dayNames.length; i++) {
18688 var d = Date.dayNames[i];
18689 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18692 row.cn[0].cls+=' fc-first';
18693 row.cn[0].cn[0].style = 'min-height:90px';
18694 row.cn[6].cls+=' fc-last';
18698 ret[0].cls += ' fc-first';
18699 ret[4].cls += ' fc-prev-last';
18700 ret[5].cls += ' fc-last';
18707 cls: 'fc-border-separate',
18708 style : 'width:100%',
18716 cls : 'fc-first fc-last',
18734 cls : 'fc-content',
18735 style : "position: relative;",
18738 cls : 'fc-view fc-view-month fc-grid',
18739 style : 'position: relative',
18740 unselectable : 'on',
18743 cls : 'fc-event-container',
18744 style : 'position:absolute;z-index:8;top:0;left:0;'
18762 initEvents : function()
18765 throw "can not find store for calendar";
18771 style: "text-align:center",
18775 style: "background-color:white;width:50%;margin:250 auto",
18779 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18790 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18792 var size = this.el.select('.fc-content', true).first().getSize();
18793 this.maskEl.setSize(size.width, size.height);
18794 this.maskEl.enableDisplayMode("block");
18795 if(!this.loadMask){
18796 this.maskEl.hide();
18799 this.store = Roo.factory(this.store, Roo.data);
18800 this.store.on('load', this.onLoad, this);
18801 this.store.on('beforeload', this.onBeforeLoad, this);
18805 this.cells = this.el.select('.fc-day',true);
18806 //Roo.log(this.cells);
18807 this.textNodes = this.el.query('.fc-day-number');
18808 this.cells.addClassOnOver('fc-state-hover');
18810 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18811 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18812 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18813 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18815 this.on('monthchange', this.onMonthChange, this);
18817 this.update(new Date().clearTime());
18820 resize : function() {
18821 var sz = this.el.getSize();
18823 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18824 this.el.select('.fc-day-content div',true).setHeight(34);
18829 showPrevMonth : function(e){
18830 this.update(this.activeDate.add("mo", -1));
18832 showToday : function(e){
18833 this.update(new Date().clearTime());
18836 showNextMonth : function(e){
18837 this.update(this.activeDate.add("mo", 1));
18841 showPrevYear : function(){
18842 this.update(this.activeDate.add("y", -1));
18846 showNextYear : function(){
18847 this.update(this.activeDate.add("y", 1));
18852 update : function(date)
18854 var vd = this.activeDate;
18855 this.activeDate = date;
18856 // if(vd && this.el){
18857 // var t = date.getTime();
18858 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18859 // Roo.log('using add remove');
18861 // this.fireEvent('monthchange', this, date);
18863 // this.cells.removeClass("fc-state-highlight");
18864 // this.cells.each(function(c){
18865 // if(c.dateValue == t){
18866 // c.addClass("fc-state-highlight");
18867 // setTimeout(function(){
18868 // try{c.dom.firstChild.focus();}catch(e){}
18878 var days = date.getDaysInMonth();
18880 var firstOfMonth = date.getFirstDateOfMonth();
18881 var startingPos = firstOfMonth.getDay()-this.startDay;
18883 if(startingPos < this.startDay){
18887 var pm = date.add(Date.MONTH, -1);
18888 var prevStart = pm.getDaysInMonth()-startingPos;
18890 this.cells = this.el.select('.fc-day',true);
18891 this.textNodes = this.el.query('.fc-day-number');
18892 this.cells.addClassOnOver('fc-state-hover');
18894 var cells = this.cells.elements;
18895 var textEls = this.textNodes;
18897 Roo.each(cells, function(cell){
18898 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18901 days += startingPos;
18903 // convert everything to numbers so it's fast
18904 var day = 86400000;
18905 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18908 //Roo.log(prevStart);
18910 var today = new Date().clearTime().getTime();
18911 var sel = date.clearTime().getTime();
18912 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18913 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18914 var ddMatch = this.disabledDatesRE;
18915 var ddText = this.disabledDatesText;
18916 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18917 var ddaysText = this.disabledDaysText;
18918 var format = this.format;
18920 var setCellClass = function(cal, cell){
18924 //Roo.log('set Cell Class');
18926 var t = d.getTime();
18930 cell.dateValue = t;
18932 cell.className += " fc-today";
18933 cell.className += " fc-state-highlight";
18934 cell.title = cal.todayText;
18937 // disable highlight in other month..
18938 //cell.className += " fc-state-highlight";
18943 cell.className = " fc-state-disabled";
18944 cell.title = cal.minText;
18948 cell.className = " fc-state-disabled";
18949 cell.title = cal.maxText;
18953 if(ddays.indexOf(d.getDay()) != -1){
18954 cell.title = ddaysText;
18955 cell.className = " fc-state-disabled";
18958 if(ddMatch && format){
18959 var fvalue = d.dateFormat(format);
18960 if(ddMatch.test(fvalue)){
18961 cell.title = ddText.replace("%0", fvalue);
18962 cell.className = " fc-state-disabled";
18966 if (!cell.initialClassName) {
18967 cell.initialClassName = cell.dom.className;
18970 cell.dom.className = cell.initialClassName + ' ' + cell.className;
18975 for(; i < startingPos; i++) {
18976 textEls[i].innerHTML = (++prevStart);
18977 d.setDate(d.getDate()+1);
18979 cells[i].className = "fc-past fc-other-month";
18980 setCellClass(this, cells[i]);
18985 for(; i < days; i++){
18986 intDay = i - startingPos + 1;
18987 textEls[i].innerHTML = (intDay);
18988 d.setDate(d.getDate()+1);
18990 cells[i].className = ''; // "x-date-active";
18991 setCellClass(this, cells[i]);
18995 for(; i < 42; i++) {
18996 textEls[i].innerHTML = (++extraDays);
18997 d.setDate(d.getDate()+1);
18999 cells[i].className = "fc-future fc-other-month";
19000 setCellClass(this, cells[i]);
19003 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19005 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19007 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19008 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19010 if(totalRows != 6){
19011 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19012 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19015 this.fireEvent('monthchange', this, date);
19019 if(!this.internalRender){
19020 var main = this.el.dom.firstChild;
19021 var w = main.offsetWidth;
19022 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19023 Roo.fly(main).setWidth(w);
19024 this.internalRender = true;
19025 // opera does not respect the auto grow header center column
19026 // then, after it gets a width opera refuses to recalculate
19027 // without a second pass
19028 if(Roo.isOpera && !this.secondPass){
19029 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19030 this.secondPass = true;
19031 this.update.defer(10, this, [date]);
19038 findCell : function(dt) {
19039 dt = dt.clearTime().getTime();
19041 this.cells.each(function(c){
19042 //Roo.log("check " +c.dateValue + '?=' + dt);
19043 if(c.dateValue == dt){
19053 findCells : function(ev) {
19054 var s = ev.start.clone().clearTime().getTime();
19056 var e= ev.end.clone().clearTime().getTime();
19059 this.cells.each(function(c){
19060 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19062 if(c.dateValue > e){
19065 if(c.dateValue < s){
19074 // findBestRow: function(cells)
19078 // for (var i =0 ; i < cells.length;i++) {
19079 // ret = Math.max(cells[i].rows || 0,ret);
19086 addItem : function(ev)
19088 // look for vertical location slot in
19089 var cells = this.findCells(ev);
19091 // ev.row = this.findBestRow(cells);
19093 // work out the location.
19097 for(var i =0; i < cells.length; i++) {
19099 cells[i].row = cells[0].row;
19102 cells[i].row = cells[i].row + 1;
19112 if (crow.start.getY() == cells[i].getY()) {
19114 crow.end = cells[i];
19131 cells[0].events.push(ev);
19133 this.calevents.push(ev);
19136 clearEvents: function() {
19138 if(!this.calevents){
19142 Roo.each(this.cells.elements, function(c){
19148 Roo.each(this.calevents, function(e) {
19149 Roo.each(e.els, function(el) {
19150 el.un('mouseenter' ,this.onEventEnter, this);
19151 el.un('mouseleave' ,this.onEventLeave, this);
19156 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19162 renderEvents: function()
19166 this.cells.each(function(c) {
19175 if(c.row != c.events.length){
19176 r = 4 - (4 - (c.row - c.events.length));
19179 c.events = ev.slice(0, r);
19180 c.more = ev.slice(r);
19182 if(c.more.length && c.more.length == 1){
19183 c.events.push(c.more.pop());
19186 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19190 this.cells.each(function(c) {
19192 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19195 for (var e = 0; e < c.events.length; e++){
19196 var ev = c.events[e];
19197 var rows = ev.rows;
19199 for(var i = 0; i < rows.length; i++) {
19201 // how many rows should it span..
19204 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19205 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19207 unselectable : "on",
19210 cls: 'fc-event-inner',
19214 // cls: 'fc-event-time',
19215 // html : cells.length > 1 ? '' : ev.time
19219 cls: 'fc-event-title',
19220 html : String.format('{0}', ev.title)
19227 cls: 'ui-resizable-handle ui-resizable-e',
19228 html : '  '
19235 cfg.cls += ' fc-event-start';
19237 if ((i+1) == rows.length) {
19238 cfg.cls += ' fc-event-end';
19241 var ctr = _this.el.select('.fc-event-container',true).first();
19242 var cg = ctr.createChild(cfg);
19244 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19245 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19247 var r = (c.more.length) ? 1 : 0;
19248 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19249 cg.setWidth(ebox.right - sbox.x -2);
19251 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19252 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19253 cg.on('click', _this.onEventClick, _this, ev);
19264 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19265 style : 'position: absolute',
19266 unselectable : "on",
19269 cls: 'fc-event-inner',
19273 cls: 'fc-event-title',
19281 cls: 'ui-resizable-handle ui-resizable-e',
19282 html : '  '
19288 var ctr = _this.el.select('.fc-event-container',true).first();
19289 var cg = ctr.createChild(cfg);
19291 var sbox = c.select('.fc-day-content',true).first().getBox();
19292 var ebox = c.select('.fc-day-content',true).first().getBox();
19294 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19295 cg.setWidth(ebox.right - sbox.x -2);
19297 cg.on('click', _this.onMoreEventClick, _this, c.more);
19307 onEventEnter: function (e, el,event,d) {
19308 this.fireEvent('evententer', this, el, event);
19311 onEventLeave: function (e, el,event,d) {
19312 this.fireEvent('eventleave', this, el, event);
19315 onEventClick: function (e, el,event,d) {
19316 this.fireEvent('eventclick', this, el, event);
19319 onMonthChange: function () {
19323 onMoreEventClick: function(e, el, more)
19327 this.calpopover.placement = 'right';
19328 this.calpopover.setTitle('More');
19330 this.calpopover.setContent('');
19332 var ctr = this.calpopover.el.select('.popover-content', true).first();
19334 Roo.each(more, function(m){
19336 cls : 'fc-event-hori fc-event-draggable',
19339 var cg = ctr.createChild(cfg);
19341 cg.on('click', _this.onEventClick, _this, m);
19344 this.calpopover.show(el);
19349 onLoad: function ()
19351 this.calevents = [];
19354 if(this.store.getCount() > 0){
19355 this.store.data.each(function(d){
19358 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19359 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19360 time : d.data.start_time,
19361 title : d.data.title,
19362 description : d.data.description,
19363 venue : d.data.venue
19368 this.renderEvents();
19370 if(this.calevents.length && this.loadMask){
19371 this.maskEl.hide();
19375 onBeforeLoad: function()
19377 this.clearEvents();
19379 this.maskEl.show();
19393 * @class Roo.bootstrap.Popover
19394 * @extends Roo.bootstrap.Component
19395 * Bootstrap Popover class
19396 * @cfg {String} html contents of the popover (or false to use children..)
19397 * @cfg {String} title of popover (or false to hide)
19398 * @cfg {String} placement how it is placed
19399 * @cfg {String} trigger click || hover (or false to trigger manually)
19400 * @cfg {String} over what (parent or false to trigger manually.)
19401 * @cfg {Number} delay - delay before showing
19404 * Create a new Popover
19405 * @param {Object} config The config object
19408 Roo.bootstrap.Popover = function(config){
19409 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19415 * After the popover show
19417 * @param {Roo.bootstrap.Popover} this
19422 * After the popover hide
19424 * @param {Roo.bootstrap.Popover} this
19430 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19432 title: 'Fill in a title',
19435 placement : 'right',
19436 trigger : 'hover', // hover
19442 can_build_overlaid : false,
19444 getChildContainer : function()
19446 return this.el.select('.popover-content',true).first();
19449 getAutoCreate : function(){
19452 cls : 'popover roo-dynamic',
19453 style: 'display:block',
19459 cls : 'popover-inner',
19463 cls: 'popover-title popover-header',
19467 cls : 'popover-content popover-body',
19478 setTitle: function(str)
19481 this.el.select('.popover-title',true).first().dom.innerHTML = str;
19483 setContent: function(str)
19486 this.el.select('.popover-content',true).first().dom.innerHTML = str;
19488 // as it get's added to the bottom of the page.
19489 onRender : function(ct, position)
19491 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19493 var cfg = Roo.apply({}, this.getAutoCreate());
19497 cfg.cls += ' ' + this.cls;
19500 cfg.style = this.style;
19502 //Roo.log("adding to ");
19503 this.el = Roo.get(document.body).createChild(cfg, position);
19504 // Roo.log(this.el);
19509 initEvents : function()
19511 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19512 this.el.enableDisplayMode('block');
19514 if (this.over === false) {
19517 if (this.triggers === false) {
19520 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19521 var triggers = this.trigger ? this.trigger.split(' ') : [];
19522 Roo.each(triggers, function(trigger) {
19524 if (trigger == 'click') {
19525 on_el.on('click', this.toggle, this);
19526 } else if (trigger != 'manual') {
19527 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19528 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19530 on_el.on(eventIn ,this.enter, this);
19531 on_el.on(eventOut, this.leave, this);
19542 toggle : function () {
19543 this.hoverState == 'in' ? this.leave() : this.enter();
19546 enter : function () {
19548 clearTimeout(this.timeout);
19550 this.hoverState = 'in';
19552 if (!this.delay || !this.delay.show) {
19557 this.timeout = setTimeout(function () {
19558 if (_t.hoverState == 'in') {
19561 }, this.delay.show)
19564 leave : function() {
19565 clearTimeout(this.timeout);
19567 this.hoverState = 'out';
19569 if (!this.delay || !this.delay.hide) {
19574 this.timeout = setTimeout(function () {
19575 if (_t.hoverState == 'out') {
19578 }, this.delay.hide)
19581 show : function (on_el)
19584 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19588 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19589 if (this.html !== false) {
19590 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19592 this.el.removeClass([
19593 'fade','top','bottom', 'left', 'right','in',
19594 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19596 if (!this.title.length) {
19597 this.el.select('.popover-title',true).hide();
19600 var placement = typeof this.placement == 'function' ?
19601 this.placement.call(this, this.el, on_el) :
19604 var autoToken = /\s?auto?\s?/i;
19605 var autoPlace = autoToken.test(placement);
19607 placement = placement.replace(autoToken, '') || 'top';
19611 //this.el.setXY([0,0]);
19613 this.el.dom.style.display='block';
19614 this.el.addClass(placement);
19616 //this.el.appendTo(on_el);
19618 var p = this.getPosition();
19619 var box = this.el.getBox();
19624 var align = Roo.bootstrap.Popover.alignment[placement];
19627 this.el.alignTo(on_el, align[0],align[1]);
19628 //var arrow = this.el.select('.arrow',true).first();
19629 //arrow.set(align[2],
19631 this.el.addClass('in');
19634 if (this.el.hasClass('fade')) {
19638 this.hoverState = 'in';
19640 this.fireEvent('show', this);
19645 this.el.setXY([0,0]);
19646 this.el.removeClass('in');
19648 this.hoverState = null;
19650 this.fireEvent('hide', this);
19655 Roo.bootstrap.Popover.alignment = {
19656 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19657 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19658 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19659 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19670 * @class Roo.bootstrap.Progress
19671 * @extends Roo.bootstrap.Component
19672 * Bootstrap Progress class
19673 * @cfg {Boolean} striped striped of the progress bar
19674 * @cfg {Boolean} active animated of the progress bar
19678 * Create a new Progress
19679 * @param {Object} config The config object
19682 Roo.bootstrap.Progress = function(config){
19683 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19686 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19691 getAutoCreate : function(){
19699 cfg.cls += ' progress-striped';
19703 cfg.cls += ' active';
19722 * @class Roo.bootstrap.ProgressBar
19723 * @extends Roo.bootstrap.Component
19724 * Bootstrap ProgressBar class
19725 * @cfg {Number} aria_valuenow aria-value now
19726 * @cfg {Number} aria_valuemin aria-value min
19727 * @cfg {Number} aria_valuemax aria-value max
19728 * @cfg {String} label label for the progress bar
19729 * @cfg {String} panel (success | info | warning | danger )
19730 * @cfg {String} role role of the progress bar
19731 * @cfg {String} sr_only text
19735 * Create a new ProgressBar
19736 * @param {Object} config The config object
19739 Roo.bootstrap.ProgressBar = function(config){
19740 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19743 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19747 aria_valuemax : 100,
19753 getAutoCreate : function()
19758 cls: 'progress-bar',
19759 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19771 cfg.role = this.role;
19774 if(this.aria_valuenow){
19775 cfg['aria-valuenow'] = this.aria_valuenow;
19778 if(this.aria_valuemin){
19779 cfg['aria-valuemin'] = this.aria_valuemin;
19782 if(this.aria_valuemax){
19783 cfg['aria-valuemax'] = this.aria_valuemax;
19786 if(this.label && !this.sr_only){
19787 cfg.html = this.label;
19791 cfg.cls += ' progress-bar-' + this.panel;
19797 update : function(aria_valuenow)
19799 this.aria_valuenow = aria_valuenow;
19801 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19816 * @class Roo.bootstrap.TabGroup
19817 * @extends Roo.bootstrap.Column
19818 * Bootstrap Column class
19819 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19820 * @cfg {Boolean} carousel true to make the group behave like a carousel
19821 * @cfg {Boolean} bullets show bullets for the panels
19822 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19823 * @cfg {Number} timer auto slide timer .. default 0 millisecond
19824 * @cfg {Boolean} showarrow (true|false) show arrow default true
19827 * Create a new TabGroup
19828 * @param {Object} config The config object
19831 Roo.bootstrap.TabGroup = function(config){
19832 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19834 this.navId = Roo.id();
19837 Roo.bootstrap.TabGroup.register(this);
19841 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
19844 transition : false,
19849 slideOnTouch : false,
19852 getAutoCreate : function()
19854 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19856 cfg.cls += ' tab-content';
19858 if (this.carousel) {
19859 cfg.cls += ' carousel slide';
19862 cls : 'carousel-inner',
19866 if(this.bullets && !Roo.isTouch){
19869 cls : 'carousel-bullets',
19873 if(this.bullets_cls){
19874 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19881 cfg.cn[0].cn.push(bullets);
19884 if(this.showarrow){
19885 cfg.cn[0].cn.push({
19887 class : 'carousel-arrow',
19891 class : 'carousel-prev',
19895 class : 'fa fa-chevron-left'
19901 class : 'carousel-next',
19905 class : 'fa fa-chevron-right'
19918 initEvents: function()
19920 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19921 // this.el.on("touchstart", this.onTouchStart, this);
19924 if(this.autoslide){
19927 this.slideFn = window.setInterval(function() {
19928 _this.showPanelNext();
19932 if(this.showarrow){
19933 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19934 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19940 // onTouchStart : function(e, el, o)
19942 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19946 // this.showPanelNext();
19950 getChildContainer : function()
19952 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19956 * register a Navigation item
19957 * @param {Roo.bootstrap.NavItem} the navitem to add
19959 register : function(item)
19961 this.tabs.push( item);
19962 item.navId = this.navId; // not really needed..
19967 getActivePanel : function()
19970 Roo.each(this.tabs, function(t) {
19980 getPanelByName : function(n)
19983 Roo.each(this.tabs, function(t) {
19984 if (t.tabId == n) {
19992 indexOfPanel : function(p)
19995 Roo.each(this.tabs, function(t,i) {
19996 if (t.tabId == p.tabId) {
20005 * show a specific panel
20006 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20007 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20009 showPanel : function (pan)
20011 if(this.transition || typeof(pan) == 'undefined'){
20012 Roo.log("waiting for the transitionend");
20016 if (typeof(pan) == 'number') {
20017 pan = this.tabs[pan];
20020 if (typeof(pan) == 'string') {
20021 pan = this.getPanelByName(pan);
20024 var cur = this.getActivePanel();
20027 Roo.log('pan or acitve pan is undefined');
20031 if (pan.tabId == this.getActivePanel().tabId) {
20035 if (false === cur.fireEvent('beforedeactivate')) {
20039 if(this.bullets > 0 && !Roo.isTouch){
20040 this.setActiveBullet(this.indexOfPanel(pan));
20043 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20045 //class="carousel-item carousel-item-next carousel-item-left"
20047 this.transition = true;
20048 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20049 var lr = dir == 'next' ? 'left' : 'right';
20050 pan.el.addClass(dir); // or prev
20051 pan.el.addClass('carousel-item-' + dir); // or prev
20052 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20053 cur.el.addClass(lr); // or right
20054 pan.el.addClass(lr);
20055 cur.el.addClass('carousel-item-' +lr); // or right
20056 pan.el.addClass('carousel-item-' +lr);
20060 cur.el.on('transitionend', function() {
20061 Roo.log("trans end?");
20063 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20064 pan.setActive(true);
20066 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20067 cur.setActive(false);
20069 _this.transition = false;
20071 }, this, { single: true } );
20076 cur.setActive(false);
20077 pan.setActive(true);
20082 showPanelNext : function()
20084 var i = this.indexOfPanel(this.getActivePanel());
20086 if (i >= this.tabs.length - 1 && !this.autoslide) {
20090 if (i >= this.tabs.length - 1 && this.autoslide) {
20094 this.showPanel(this.tabs[i+1]);
20097 showPanelPrev : function()
20099 var i = this.indexOfPanel(this.getActivePanel());
20101 if (i < 1 && !this.autoslide) {
20105 if (i < 1 && this.autoslide) {
20106 i = this.tabs.length;
20109 this.showPanel(this.tabs[i-1]);
20113 addBullet: function()
20115 if(!this.bullets || Roo.isTouch){
20118 var ctr = this.el.select('.carousel-bullets',true).first();
20119 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20120 var bullet = ctr.createChild({
20121 cls : 'bullet bullet-' + i
20122 },ctr.dom.lastChild);
20127 bullet.on('click', (function(e, el, o, ii, t){
20129 e.preventDefault();
20131 this.showPanel(ii);
20133 if(this.autoslide && this.slideFn){
20134 clearInterval(this.slideFn);
20135 this.slideFn = window.setInterval(function() {
20136 _this.showPanelNext();
20140 }).createDelegate(this, [i, bullet], true));
20145 setActiveBullet : function(i)
20151 Roo.each(this.el.select('.bullet', true).elements, function(el){
20152 el.removeClass('selected');
20155 var bullet = this.el.select('.bullet-' + i, true).first();
20161 bullet.addClass('selected');
20172 Roo.apply(Roo.bootstrap.TabGroup, {
20176 * register a Navigation Group
20177 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20179 register : function(navgrp)
20181 this.groups[navgrp.navId] = navgrp;
20185 * fetch a Navigation Group based on the navigation ID
20186 * if one does not exist , it will get created.
20187 * @param {string} the navgroup to add
20188 * @returns {Roo.bootstrap.NavGroup} the navgroup
20190 get: function(navId) {
20191 if (typeof(this.groups[navId]) == 'undefined') {
20192 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20194 return this.groups[navId] ;
20209 * @class Roo.bootstrap.TabPanel
20210 * @extends Roo.bootstrap.Component
20211 * Bootstrap TabPanel class
20212 * @cfg {Boolean} active panel active
20213 * @cfg {String} html panel content
20214 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20215 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20216 * @cfg {String} href click to link..
20220 * Create a new TabPanel
20221 * @param {Object} config The config object
20224 Roo.bootstrap.TabPanel = function(config){
20225 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20229 * Fires when the active status changes
20230 * @param {Roo.bootstrap.TabPanel} this
20231 * @param {Boolean} state the new state
20236 * @event beforedeactivate
20237 * Fires before a tab is de-activated - can be used to do validation on a form.
20238 * @param {Roo.bootstrap.TabPanel} this
20239 * @return {Boolean} false if there is an error
20242 'beforedeactivate': true
20245 this.tabId = this.tabId || Roo.id();
20249 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20257 getAutoCreate : function(){
20262 // item is needed for carousel - not sure if it has any effect otherwise
20263 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20264 html: this.html || ''
20268 cfg.cls += ' active';
20272 cfg.tabId = this.tabId;
20280 initEvents: function()
20282 var p = this.parent();
20284 this.navId = this.navId || p.navId;
20286 if (typeof(this.navId) != 'undefined') {
20287 // not really needed.. but just in case.. parent should be a NavGroup.
20288 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20292 var i = tg.tabs.length - 1;
20294 if(this.active && tg.bullets > 0 && i < tg.bullets){
20295 tg.setActiveBullet(i);
20299 this.el.on('click', this.onClick, this);
20302 this.el.on("touchstart", this.onTouchStart, this);
20303 this.el.on("touchmove", this.onTouchMove, this);
20304 this.el.on("touchend", this.onTouchEnd, this);
20309 onRender : function(ct, position)
20311 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20314 setActive : function(state)
20316 Roo.log("panel - set active " + this.tabId + "=" + state);
20318 this.active = state;
20320 this.el.removeClass('active');
20322 } else if (!this.el.hasClass('active')) {
20323 this.el.addClass('active');
20326 this.fireEvent('changed', this, state);
20329 onClick : function(e)
20331 e.preventDefault();
20333 if(!this.href.length){
20337 window.location.href = this.href;
20346 onTouchStart : function(e)
20348 this.swiping = false;
20350 this.startX = e.browserEvent.touches[0].clientX;
20351 this.startY = e.browserEvent.touches[0].clientY;
20354 onTouchMove : function(e)
20356 this.swiping = true;
20358 this.endX = e.browserEvent.touches[0].clientX;
20359 this.endY = e.browserEvent.touches[0].clientY;
20362 onTouchEnd : function(e)
20369 var tabGroup = this.parent();
20371 if(this.endX > this.startX){ // swiping right
20372 tabGroup.showPanelPrev();
20376 if(this.startX > this.endX){ // swiping left
20377 tabGroup.showPanelNext();
20396 * @class Roo.bootstrap.DateField
20397 * @extends Roo.bootstrap.Input
20398 * Bootstrap DateField class
20399 * @cfg {Number} weekStart default 0
20400 * @cfg {String} viewMode default empty, (months|years)
20401 * @cfg {String} minViewMode default empty, (months|years)
20402 * @cfg {Number} startDate default -Infinity
20403 * @cfg {Number} endDate default Infinity
20404 * @cfg {Boolean} todayHighlight default false
20405 * @cfg {Boolean} todayBtn default false
20406 * @cfg {Boolean} calendarWeeks default false
20407 * @cfg {Object} daysOfWeekDisabled default empty
20408 * @cfg {Boolean} singleMode default false (true | false)
20410 * @cfg {Boolean} keyboardNavigation default true
20411 * @cfg {String} language default en
20414 * Create a new DateField
20415 * @param {Object} config The config object
20418 Roo.bootstrap.DateField = function(config){
20419 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20423 * Fires when this field show.
20424 * @param {Roo.bootstrap.DateField} this
20425 * @param {Mixed} date The date value
20430 * Fires when this field hide.
20431 * @param {Roo.bootstrap.DateField} this
20432 * @param {Mixed} date The date value
20437 * Fires when select a date.
20438 * @param {Roo.bootstrap.DateField} this
20439 * @param {Mixed} date The date value
20443 * @event beforeselect
20444 * Fires when before select a date.
20445 * @param {Roo.bootstrap.DateField} this
20446 * @param {Mixed} date The date value
20448 beforeselect : true
20452 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20455 * @cfg {String} format
20456 * The default date format string which can be overriden for localization support. The format must be
20457 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20461 * @cfg {String} altFormats
20462 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20463 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20465 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20473 todayHighlight : false,
20479 keyboardNavigation: true,
20481 calendarWeeks: false,
20483 startDate: -Infinity,
20487 daysOfWeekDisabled: [],
20491 singleMode : false,
20493 UTCDate: function()
20495 return new Date(Date.UTC.apply(Date, arguments));
20498 UTCToday: function()
20500 var today = new Date();
20501 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20504 getDate: function() {
20505 var d = this.getUTCDate();
20506 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20509 getUTCDate: function() {
20513 setDate: function(d) {
20514 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20517 setUTCDate: function(d) {
20519 this.setValue(this.formatDate(this.date));
20522 onRender: function(ct, position)
20525 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20527 this.language = this.language || 'en';
20528 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20529 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20531 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20532 this.format = this.format || 'm/d/y';
20533 this.isInline = false;
20534 this.isInput = true;
20535 this.component = this.el.select('.add-on', true).first() || false;
20536 this.component = (this.component && this.component.length === 0) ? false : this.component;
20537 this.hasInput = this.component && this.inputEl().length;
20539 if (typeof(this.minViewMode === 'string')) {
20540 switch (this.minViewMode) {
20542 this.minViewMode = 1;
20545 this.minViewMode = 2;
20548 this.minViewMode = 0;
20553 if (typeof(this.viewMode === 'string')) {
20554 switch (this.viewMode) {
20567 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20569 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20571 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20573 this.picker().on('mousedown', this.onMousedown, this);
20574 this.picker().on('click', this.onClick, this);
20576 this.picker().addClass('datepicker-dropdown');
20578 this.startViewMode = this.viewMode;
20580 if(this.singleMode){
20581 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20582 v.setVisibilityMode(Roo.Element.DISPLAY);
20586 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20587 v.setStyle('width', '189px');
20591 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20592 if(!this.calendarWeeks){
20597 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20598 v.attr('colspan', function(i, val){
20599 return parseInt(val) + 1;
20604 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20606 this.setStartDate(this.startDate);
20607 this.setEndDate(this.endDate);
20609 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20616 if(this.isInline) {
20621 picker : function()
20623 return this.pickerEl;
20624 // return this.el.select('.datepicker', true).first();
20627 fillDow: function()
20629 var dowCnt = this.weekStart;
20638 if(this.calendarWeeks){
20646 while (dowCnt < this.weekStart + 7) {
20650 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20654 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20657 fillMonths: function()
20660 var months = this.picker().select('>.datepicker-months td', true).first();
20662 months.dom.innerHTML = '';
20668 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20671 months.createChild(month);
20678 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;
20680 if (this.date < this.startDate) {
20681 this.viewDate = new Date(this.startDate);
20682 } else if (this.date > this.endDate) {
20683 this.viewDate = new Date(this.endDate);
20685 this.viewDate = new Date(this.date);
20693 var d = new Date(this.viewDate),
20694 year = d.getUTCFullYear(),
20695 month = d.getUTCMonth(),
20696 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20697 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20698 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20699 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20700 currentDate = this.date && this.date.valueOf(),
20701 today = this.UTCToday();
20703 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20705 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20707 // this.picker.select('>tfoot th.today').
20708 // .text(dates[this.language].today)
20709 // .toggle(this.todayBtn !== false);
20711 this.updateNavArrows();
20714 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20716 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20718 prevMonth.setUTCDate(day);
20720 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20722 var nextMonth = new Date(prevMonth);
20724 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20726 nextMonth = nextMonth.valueOf();
20728 var fillMonths = false;
20730 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20732 while(prevMonth.valueOf() <= nextMonth) {
20735 if (prevMonth.getUTCDay() === this.weekStart) {
20737 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20745 if(this.calendarWeeks){
20746 // ISO 8601: First week contains first thursday.
20747 // ISO also states week starts on Monday, but we can be more abstract here.
20749 // Start of current week: based on weekstart/current date
20750 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20751 // Thursday of this week
20752 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20753 // First Thursday of year, year from thursday
20754 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20755 // Calendar week: ms between thursdays, div ms per day, div 7 days
20756 calWeek = (th - yth) / 864e5 / 7 + 1;
20758 fillMonths.cn.push({
20766 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20768 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20771 if (this.todayHighlight &&
20772 prevMonth.getUTCFullYear() == today.getFullYear() &&
20773 prevMonth.getUTCMonth() == today.getMonth() &&
20774 prevMonth.getUTCDate() == today.getDate()) {
20775 clsName += ' today';
20778 if (currentDate && prevMonth.valueOf() === currentDate) {
20779 clsName += ' active';
20782 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20783 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20784 clsName += ' disabled';
20787 fillMonths.cn.push({
20789 cls: 'day ' + clsName,
20790 html: prevMonth.getDate()
20793 prevMonth.setDate(prevMonth.getDate()+1);
20796 var currentYear = this.date && this.date.getUTCFullYear();
20797 var currentMonth = this.date && this.date.getUTCMonth();
20799 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20801 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20802 v.removeClass('active');
20804 if(currentYear === year && k === currentMonth){
20805 v.addClass('active');
20808 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20809 v.addClass('disabled');
20815 year = parseInt(year/10, 10) * 10;
20817 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20819 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20822 for (var i = -1; i < 11; i++) {
20823 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20825 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20833 showMode: function(dir)
20836 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20839 Roo.each(this.picker().select('>div',true).elements, function(v){
20840 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20843 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20848 if(this.isInline) {
20852 this.picker().removeClass(['bottom', 'top']);
20854 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20856 * place to the top of element!
20860 this.picker().addClass('top');
20861 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20866 this.picker().addClass('bottom');
20868 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20871 parseDate : function(value)
20873 if(!value || value instanceof Date){
20876 var v = Date.parseDate(value, this.format);
20877 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20878 v = Date.parseDate(value, 'Y-m-d');
20880 if(!v && this.altFormats){
20881 if(!this.altFormatsArray){
20882 this.altFormatsArray = this.altFormats.split("|");
20884 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20885 v = Date.parseDate(value, this.altFormatsArray[i]);
20891 formatDate : function(date, fmt)
20893 return (!date || !(date instanceof Date)) ?
20894 date : date.dateFormat(fmt || this.format);
20897 onFocus : function()
20899 Roo.bootstrap.DateField.superclass.onFocus.call(this);
20903 onBlur : function()
20905 Roo.bootstrap.DateField.superclass.onBlur.call(this);
20907 var d = this.inputEl().getValue();
20914 showPopup : function()
20916 this.picker().show();
20920 this.fireEvent('showpopup', this, this.date);
20923 hidePopup : function()
20925 if(this.isInline) {
20928 this.picker().hide();
20929 this.viewMode = this.startViewMode;
20932 this.fireEvent('hidepopup', this, this.date);
20936 onMousedown: function(e)
20938 e.stopPropagation();
20939 e.preventDefault();
20944 Roo.bootstrap.DateField.superclass.keyup.call(this);
20948 setValue: function(v)
20950 if(this.fireEvent('beforeselect', this, v) !== false){
20951 var d = new Date(this.parseDate(v) ).clearTime();
20953 if(isNaN(d.getTime())){
20954 this.date = this.viewDate = '';
20955 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20959 v = this.formatDate(d);
20961 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20963 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20967 this.fireEvent('select', this, this.date);
20971 getValue: function()
20973 return this.formatDate(this.date);
20976 fireKey: function(e)
20978 if (!this.picker().isVisible()){
20979 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20985 var dateChanged = false,
20987 newDate, newViewDate;
20992 e.preventDefault();
20996 if (!this.keyboardNavigation) {
20999 dir = e.keyCode == 37 ? -1 : 1;
21002 newDate = this.moveYear(this.date, dir);
21003 newViewDate = this.moveYear(this.viewDate, dir);
21004 } else if (e.shiftKey){
21005 newDate = this.moveMonth(this.date, dir);
21006 newViewDate = this.moveMonth(this.viewDate, dir);
21008 newDate = new Date(this.date);
21009 newDate.setUTCDate(this.date.getUTCDate() + dir);
21010 newViewDate = new Date(this.viewDate);
21011 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21013 if (this.dateWithinRange(newDate)){
21014 this.date = newDate;
21015 this.viewDate = newViewDate;
21016 this.setValue(this.formatDate(this.date));
21018 e.preventDefault();
21019 dateChanged = true;
21024 if (!this.keyboardNavigation) {
21027 dir = e.keyCode == 38 ? -1 : 1;
21029 newDate = this.moveYear(this.date, dir);
21030 newViewDate = this.moveYear(this.viewDate, dir);
21031 } else if (e.shiftKey){
21032 newDate = this.moveMonth(this.date, dir);
21033 newViewDate = this.moveMonth(this.viewDate, dir);
21035 newDate = new Date(this.date);
21036 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21037 newViewDate = new Date(this.viewDate);
21038 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21040 if (this.dateWithinRange(newDate)){
21041 this.date = newDate;
21042 this.viewDate = newViewDate;
21043 this.setValue(this.formatDate(this.date));
21045 e.preventDefault();
21046 dateChanged = true;
21050 this.setValue(this.formatDate(this.date));
21052 e.preventDefault();
21055 this.setValue(this.formatDate(this.date));
21069 onClick: function(e)
21071 e.stopPropagation();
21072 e.preventDefault();
21074 var target = e.getTarget();
21076 if(target.nodeName.toLowerCase() === 'i'){
21077 target = Roo.get(target).dom.parentNode;
21080 var nodeName = target.nodeName;
21081 var className = target.className;
21082 var html = target.innerHTML;
21083 //Roo.log(nodeName);
21085 switch(nodeName.toLowerCase()) {
21087 switch(className) {
21093 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21094 switch(this.viewMode){
21096 this.viewDate = this.moveMonth(this.viewDate, dir);
21100 this.viewDate = this.moveYear(this.viewDate, dir);
21106 var date = new Date();
21107 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21109 this.setValue(this.formatDate(this.date));
21116 if (className.indexOf('disabled') < 0) {
21117 this.viewDate.setUTCDate(1);
21118 if (className.indexOf('month') > -1) {
21119 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21121 var year = parseInt(html, 10) || 0;
21122 this.viewDate.setUTCFullYear(year);
21126 if(this.singleMode){
21127 this.setValue(this.formatDate(this.viewDate));
21138 //Roo.log(className);
21139 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21140 var day = parseInt(html, 10) || 1;
21141 var year = this.viewDate.getUTCFullYear(),
21142 month = this.viewDate.getUTCMonth();
21144 if (className.indexOf('old') > -1) {
21151 } else if (className.indexOf('new') > -1) {
21159 //Roo.log([year,month,day]);
21160 this.date = this.UTCDate(year, month, day,0,0,0,0);
21161 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21163 //Roo.log(this.formatDate(this.date));
21164 this.setValue(this.formatDate(this.date));
21171 setStartDate: function(startDate)
21173 this.startDate = startDate || -Infinity;
21174 if (this.startDate !== -Infinity) {
21175 this.startDate = this.parseDate(this.startDate);
21178 this.updateNavArrows();
21181 setEndDate: function(endDate)
21183 this.endDate = endDate || Infinity;
21184 if (this.endDate !== Infinity) {
21185 this.endDate = this.parseDate(this.endDate);
21188 this.updateNavArrows();
21191 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21193 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21194 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21195 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21197 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21198 return parseInt(d, 10);
21201 this.updateNavArrows();
21204 updateNavArrows: function()
21206 if(this.singleMode){
21210 var d = new Date(this.viewDate),
21211 year = d.getUTCFullYear(),
21212 month = d.getUTCMonth();
21214 Roo.each(this.picker().select('.prev', true).elements, function(v){
21216 switch (this.viewMode) {
21219 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21225 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21232 Roo.each(this.picker().select('.next', true).elements, function(v){
21234 switch (this.viewMode) {
21237 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21243 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21251 moveMonth: function(date, dir)
21256 var new_date = new Date(date.valueOf()),
21257 day = new_date.getUTCDate(),
21258 month = new_date.getUTCMonth(),
21259 mag = Math.abs(dir),
21261 dir = dir > 0 ? 1 : -1;
21264 // If going back one month, make sure month is not current month
21265 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21267 return new_date.getUTCMonth() == month;
21269 // If going forward one month, make sure month is as expected
21270 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21272 return new_date.getUTCMonth() != new_month;
21274 new_month = month + dir;
21275 new_date.setUTCMonth(new_month);
21276 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21277 if (new_month < 0 || new_month > 11) {
21278 new_month = (new_month + 12) % 12;
21281 // For magnitudes >1, move one month at a time...
21282 for (var i=0; i<mag; i++) {
21283 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21284 new_date = this.moveMonth(new_date, dir);
21286 // ...then reset the day, keeping it in the new month
21287 new_month = new_date.getUTCMonth();
21288 new_date.setUTCDate(day);
21290 return new_month != new_date.getUTCMonth();
21293 // Common date-resetting loop -- if date is beyond end of month, make it
21296 new_date.setUTCDate(--day);
21297 new_date.setUTCMonth(new_month);
21302 moveYear: function(date, dir)
21304 return this.moveMonth(date, dir*12);
21307 dateWithinRange: function(date)
21309 return date >= this.startDate && date <= this.endDate;
21315 this.picker().remove();
21318 validateValue : function(value)
21320 if(this.getVisibilityEl().hasClass('hidden')){
21324 if(value.length < 1) {
21325 if(this.allowBlank){
21331 if(value.length < this.minLength){
21334 if(value.length > this.maxLength){
21338 var vt = Roo.form.VTypes;
21339 if(!vt[this.vtype](value, this)){
21343 if(typeof this.validator == "function"){
21344 var msg = this.validator(value);
21350 if(this.regex && !this.regex.test(value)){
21354 if(typeof(this.parseDate(value)) == 'undefined'){
21358 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21362 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21372 this.date = this.viewDate = '';
21374 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21379 Roo.apply(Roo.bootstrap.DateField, {
21390 html: '<i class="fa fa-arrow-left"/>'
21400 html: '<i class="fa fa-arrow-right"/>'
21442 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21443 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21444 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21445 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21446 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21459 navFnc: 'FullYear',
21464 navFnc: 'FullYear',
21469 Roo.apply(Roo.bootstrap.DateField, {
21473 cls: 'datepicker dropdown-menu roo-dynamic',
21477 cls: 'datepicker-days',
21481 cls: 'table-condensed',
21483 Roo.bootstrap.DateField.head,
21487 Roo.bootstrap.DateField.footer
21494 cls: 'datepicker-months',
21498 cls: 'table-condensed',
21500 Roo.bootstrap.DateField.head,
21501 Roo.bootstrap.DateField.content,
21502 Roo.bootstrap.DateField.footer
21509 cls: 'datepicker-years',
21513 cls: 'table-condensed',
21515 Roo.bootstrap.DateField.head,
21516 Roo.bootstrap.DateField.content,
21517 Roo.bootstrap.DateField.footer
21536 * @class Roo.bootstrap.TimeField
21537 * @extends Roo.bootstrap.Input
21538 * Bootstrap DateField class
21542 * Create a new TimeField
21543 * @param {Object} config The config object
21546 Roo.bootstrap.TimeField = function(config){
21547 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21551 * Fires when this field show.
21552 * @param {Roo.bootstrap.DateField} thisthis
21553 * @param {Mixed} date The date value
21558 * Fires when this field hide.
21559 * @param {Roo.bootstrap.DateField} this
21560 * @param {Mixed} date The date value
21565 * Fires when select a date.
21566 * @param {Roo.bootstrap.DateField} this
21567 * @param {Mixed} date The date value
21573 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21576 * @cfg {String} format
21577 * The default time format string which can be overriden for localization support. The format must be
21578 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21582 onRender: function(ct, position)
21585 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21587 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21589 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21591 this.pop = this.picker().select('>.datepicker-time',true).first();
21592 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21594 this.picker().on('mousedown', this.onMousedown, this);
21595 this.picker().on('click', this.onClick, this);
21597 this.picker().addClass('datepicker-dropdown');
21602 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21603 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21604 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21605 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21606 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21607 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21611 fireKey: function(e){
21612 if (!this.picker().isVisible()){
21613 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21619 e.preventDefault();
21627 this.onTogglePeriod();
21630 this.onIncrementMinutes();
21633 this.onDecrementMinutes();
21642 onClick: function(e) {
21643 e.stopPropagation();
21644 e.preventDefault();
21647 picker : function()
21649 return this.el.select('.datepicker', true).first();
21652 fillTime: function()
21654 var time = this.pop.select('tbody', true).first();
21656 time.dom.innerHTML = '';
21671 cls: 'hours-up glyphicon glyphicon-chevron-up'
21691 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21712 cls: 'timepicker-hour',
21727 cls: 'timepicker-minute',
21742 cls: 'btn btn-primary period',
21764 cls: 'hours-down glyphicon glyphicon-chevron-down'
21784 cls: 'minutes-down glyphicon glyphicon-chevron-down'
21802 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21809 var hours = this.time.getHours();
21810 var minutes = this.time.getMinutes();
21823 hours = hours - 12;
21827 hours = '0' + hours;
21831 minutes = '0' + minutes;
21834 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21835 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21836 this.pop.select('button', true).first().dom.innerHTML = period;
21842 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21844 var cls = ['bottom'];
21846 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21853 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21858 this.picker().addClass(cls.join('-'));
21862 Roo.each(cls, function(c){
21864 _this.picker().setTop(_this.inputEl().getHeight());
21868 _this.picker().setTop(0 - _this.picker().getHeight());
21873 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21877 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21884 onFocus : function()
21886 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21890 onBlur : function()
21892 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21898 this.picker().show();
21903 this.fireEvent('show', this, this.date);
21908 this.picker().hide();
21911 this.fireEvent('hide', this, this.date);
21914 setTime : function()
21917 this.setValue(this.time.format(this.format));
21919 this.fireEvent('select', this, this.date);
21924 onMousedown: function(e){
21925 e.stopPropagation();
21926 e.preventDefault();
21929 onIncrementHours: function()
21931 Roo.log('onIncrementHours');
21932 this.time = this.time.add(Date.HOUR, 1);
21937 onDecrementHours: function()
21939 Roo.log('onDecrementHours');
21940 this.time = this.time.add(Date.HOUR, -1);
21944 onIncrementMinutes: function()
21946 Roo.log('onIncrementMinutes');
21947 this.time = this.time.add(Date.MINUTE, 1);
21951 onDecrementMinutes: function()
21953 Roo.log('onDecrementMinutes');
21954 this.time = this.time.add(Date.MINUTE, -1);
21958 onTogglePeriod: function()
21960 Roo.log('onTogglePeriod');
21961 this.time = this.time.add(Date.HOUR, 12);
21968 Roo.apply(Roo.bootstrap.TimeField, {
21998 cls: 'btn btn-info ok',
22010 Roo.apply(Roo.bootstrap.TimeField, {
22014 cls: 'datepicker dropdown-menu',
22018 cls: 'datepicker-time',
22022 cls: 'table-condensed',
22024 Roo.bootstrap.TimeField.content,
22025 Roo.bootstrap.TimeField.footer
22044 * @class Roo.bootstrap.MonthField
22045 * @extends Roo.bootstrap.Input
22046 * Bootstrap MonthField class
22048 * @cfg {String} language default en
22051 * Create a new MonthField
22052 * @param {Object} config The config object
22055 Roo.bootstrap.MonthField = function(config){
22056 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22061 * Fires when this field show.
22062 * @param {Roo.bootstrap.MonthField} this
22063 * @param {Mixed} date The date value
22068 * Fires when this field hide.
22069 * @param {Roo.bootstrap.MonthField} this
22070 * @param {Mixed} date The date value
22075 * Fires when select a date.
22076 * @param {Roo.bootstrap.MonthField} this
22077 * @param {String} oldvalue The old value
22078 * @param {String} newvalue The new value
22084 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22086 onRender: function(ct, position)
22089 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22091 this.language = this.language || 'en';
22092 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22093 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22095 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22096 this.isInline = false;
22097 this.isInput = true;
22098 this.component = this.el.select('.add-on', true).first() || false;
22099 this.component = (this.component && this.component.length === 0) ? false : this.component;
22100 this.hasInput = this.component && this.inputEL().length;
22102 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22104 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22106 this.picker().on('mousedown', this.onMousedown, this);
22107 this.picker().on('click', this.onClick, this);
22109 this.picker().addClass('datepicker-dropdown');
22111 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22112 v.setStyle('width', '189px');
22119 if(this.isInline) {
22125 setValue: function(v, suppressEvent)
22127 var o = this.getValue();
22129 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22133 if(suppressEvent !== true){
22134 this.fireEvent('select', this, o, v);
22139 getValue: function()
22144 onClick: function(e)
22146 e.stopPropagation();
22147 e.preventDefault();
22149 var target = e.getTarget();
22151 if(target.nodeName.toLowerCase() === 'i'){
22152 target = Roo.get(target).dom.parentNode;
22155 var nodeName = target.nodeName;
22156 var className = target.className;
22157 var html = target.innerHTML;
22159 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22163 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22165 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22171 picker : function()
22173 return this.pickerEl;
22176 fillMonths: function()
22179 var months = this.picker().select('>.datepicker-months td', true).first();
22181 months.dom.innerHTML = '';
22187 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22190 months.createChild(month);
22199 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22200 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22203 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22204 e.removeClass('active');
22206 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22207 e.addClass('active');
22214 if(this.isInline) {
22218 this.picker().removeClass(['bottom', 'top']);
22220 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22222 * place to the top of element!
22226 this.picker().addClass('top');
22227 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22232 this.picker().addClass('bottom');
22234 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22237 onFocus : function()
22239 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22243 onBlur : function()
22245 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22247 var d = this.inputEl().getValue();
22256 this.picker().show();
22257 this.picker().select('>.datepicker-months', true).first().show();
22261 this.fireEvent('show', this, this.date);
22266 if(this.isInline) {
22269 this.picker().hide();
22270 this.fireEvent('hide', this, this.date);
22274 onMousedown: function(e)
22276 e.stopPropagation();
22277 e.preventDefault();
22282 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22286 fireKey: function(e)
22288 if (!this.picker().isVisible()){
22289 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22300 e.preventDefault();
22304 dir = e.keyCode == 37 ? -1 : 1;
22306 this.vIndex = this.vIndex + dir;
22308 if(this.vIndex < 0){
22312 if(this.vIndex > 11){
22316 if(isNaN(this.vIndex)){
22320 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22326 dir = e.keyCode == 38 ? -1 : 1;
22328 this.vIndex = this.vIndex + dir * 4;
22330 if(this.vIndex < 0){
22334 if(this.vIndex > 11){
22338 if(isNaN(this.vIndex)){
22342 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22347 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22348 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22352 e.preventDefault();
22355 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22356 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22372 this.picker().remove();
22377 Roo.apply(Roo.bootstrap.MonthField, {
22396 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22397 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22402 Roo.apply(Roo.bootstrap.MonthField, {
22406 cls: 'datepicker dropdown-menu roo-dynamic',
22410 cls: 'datepicker-months',
22414 cls: 'table-condensed',
22416 Roo.bootstrap.DateField.content
22436 * @class Roo.bootstrap.CheckBox
22437 * @extends Roo.bootstrap.Input
22438 * Bootstrap CheckBox class
22440 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22441 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22442 * @cfg {String} boxLabel The text that appears beside the checkbox
22443 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22444 * @cfg {Boolean} checked initnal the element
22445 * @cfg {Boolean} inline inline the element (default false)
22446 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22447 * @cfg {String} tooltip label tooltip
22450 * Create a new CheckBox
22451 * @param {Object} config The config object
22454 Roo.bootstrap.CheckBox = function(config){
22455 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22460 * Fires when the element is checked or unchecked.
22461 * @param {Roo.bootstrap.CheckBox} this This input
22462 * @param {Boolean} checked The new checked value
22467 * Fires when the element is click.
22468 * @param {Roo.bootstrap.CheckBox} this This input
22475 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22477 inputType: 'checkbox',
22486 // checkbox success does not make any sense really..
22491 getAutoCreate : function()
22493 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22499 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22502 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22508 type : this.inputType,
22509 value : this.inputValue,
22510 cls : 'roo-' + this.inputType, //'form-box',
22511 placeholder : this.placeholder || ''
22515 if(this.inputType != 'radio'){
22519 cls : 'roo-hidden-value',
22520 value : this.checked ? this.inputValue : this.valueOff
22525 if (this.weight) { // Validity check?
22526 cfg.cls += " " + this.inputType + "-" + this.weight;
22529 if (this.disabled) {
22530 input.disabled=true;
22534 input.checked = this.checked;
22539 input.name = this.name;
22541 if(this.inputType != 'radio'){
22542 hidden.name = this.name;
22543 input.name = '_hidden_' + this.name;
22548 input.cls += ' input-' + this.size;
22553 ['xs','sm','md','lg'].map(function(size){
22554 if (settings[size]) {
22555 cfg.cls += ' col-' + size + '-' + settings[size];
22559 var inputblock = input;
22561 if (this.before || this.after) {
22564 cls : 'input-group',
22569 inputblock.cn.push({
22571 cls : 'input-group-addon',
22576 inputblock.cn.push(input);
22578 if(this.inputType != 'radio'){
22579 inputblock.cn.push(hidden);
22583 inputblock.cn.push({
22585 cls : 'input-group-addon',
22591 var boxLabelCfg = false;
22597 //'for': id, // box label is handled by onclick - so no for...
22599 html: this.boxLabel
22602 boxLabelCfg.tooltip = this.tooltip;
22608 if (align ==='left' && this.fieldLabel.length) {
22609 // Roo.log("left and has label");
22614 cls : 'control-label',
22615 html : this.fieldLabel
22626 cfg.cn[1].cn.push(boxLabelCfg);
22629 if(this.labelWidth > 12){
22630 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22633 if(this.labelWidth < 13 && this.labelmd == 0){
22634 this.labelmd = this.labelWidth;
22637 if(this.labellg > 0){
22638 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22639 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22642 if(this.labelmd > 0){
22643 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22644 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22647 if(this.labelsm > 0){
22648 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22649 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22652 if(this.labelxs > 0){
22653 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22654 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22657 } else if ( this.fieldLabel.length) {
22658 // Roo.log(" label");
22662 tag: this.boxLabel ? 'span' : 'label',
22664 cls: 'control-label box-input-label',
22665 //cls : 'input-group-addon',
22666 html : this.fieldLabel
22673 cfg.cn.push(boxLabelCfg);
22678 // Roo.log(" no label && no align");
22679 cfg.cn = [ inputblock ] ;
22681 cfg.cn.push(boxLabelCfg);
22689 if(this.inputType != 'radio'){
22690 cfg.cn.push(hidden);
22698 * return the real input element.
22700 inputEl: function ()
22702 return this.el.select('input.roo-' + this.inputType,true).first();
22704 hiddenEl: function ()
22706 return this.el.select('input.roo-hidden-value',true).first();
22709 labelEl: function()
22711 return this.el.select('label.control-label',true).first();
22713 /* depricated... */
22717 return this.labelEl();
22720 boxLabelEl: function()
22722 return this.el.select('label.box-label',true).first();
22725 initEvents : function()
22727 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22729 this.inputEl().on('click', this.onClick, this);
22731 if (this.boxLabel) {
22732 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22735 this.startValue = this.getValue();
22738 Roo.bootstrap.CheckBox.register(this);
22742 onClick : function(e)
22744 if(this.fireEvent('click', this, e) !== false){
22745 this.setChecked(!this.checked);
22750 setChecked : function(state,suppressEvent)
22752 this.startValue = this.getValue();
22754 if(this.inputType == 'radio'){
22756 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22757 e.dom.checked = false;
22760 this.inputEl().dom.checked = true;
22762 this.inputEl().dom.value = this.inputValue;
22764 if(suppressEvent !== true){
22765 this.fireEvent('check', this, true);
22773 this.checked = state;
22775 this.inputEl().dom.checked = state;
22778 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22780 if(suppressEvent !== true){
22781 this.fireEvent('check', this, state);
22787 getValue : function()
22789 if(this.inputType == 'radio'){
22790 return this.getGroupValue();
22793 return this.hiddenEl().dom.value;
22797 getGroupValue : function()
22799 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22803 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22806 setValue : function(v,suppressEvent)
22808 if(this.inputType == 'radio'){
22809 this.setGroupValue(v, suppressEvent);
22813 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22818 setGroupValue : function(v, suppressEvent)
22820 this.startValue = this.getValue();
22822 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22823 e.dom.checked = false;
22825 if(e.dom.value == v){
22826 e.dom.checked = true;
22830 if(suppressEvent !== true){
22831 this.fireEvent('check', this, true);
22839 validate : function()
22841 if(this.getVisibilityEl().hasClass('hidden')){
22847 (this.inputType == 'radio' && this.validateRadio()) ||
22848 (this.inputType == 'checkbox' && this.validateCheckbox())
22854 this.markInvalid();
22858 validateRadio : function()
22860 if(this.getVisibilityEl().hasClass('hidden')){
22864 if(this.allowBlank){
22870 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22871 if(!e.dom.checked){
22883 validateCheckbox : function()
22886 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22887 //return (this.getValue() == this.inputValue) ? true : false;
22890 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22898 for(var i in group){
22899 if(group[i].el.isVisible(true)){
22907 for(var i in group){
22912 r = (group[i].getValue() == group[i].inputValue) ? true : false;
22919 * Mark this field as valid
22921 markValid : function()
22925 this.fireEvent('valid', this);
22927 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22930 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22937 if(this.inputType == 'radio'){
22938 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22939 var fg = e.findParent('.form-group', false, true);
22940 if (Roo.bootstrap.version == 3) {
22941 fg.removeClass([_this.invalidClass, _this.validClass]);
22942 fg.addClass(_this.validClass);
22944 fg.removeClass(['is-valid', 'is-invalid']);
22945 fg.addClass('is-valid');
22953 var fg = this.el.findParent('.form-group', false, true);
22954 if (Roo.bootstrap.version == 3) {
22955 fg.removeClass([this.invalidClass, this.validClass]);
22956 fg.addClass(this.validClass);
22958 fg.removeClass(['is-valid', 'is-invalid']);
22959 fg.addClass('is-valid');
22964 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22970 for(var i in group){
22971 var fg = group[i].el.findParent('.form-group', false, true);
22972 if (Roo.bootstrap.version == 3) {
22973 fg.removeClass([this.invalidClass, this.validClass]);
22974 fg.addClass(this.validClass);
22976 fg.removeClass(['is-valid', 'is-invalid']);
22977 fg.addClass('is-valid');
22983 * Mark this field as invalid
22984 * @param {String} msg The validation message
22986 markInvalid : function(msg)
22988 if(this.allowBlank){
22994 this.fireEvent('invalid', this, msg);
22996 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22999 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23003 label.markInvalid();
23006 if(this.inputType == 'radio'){
23008 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23009 var fg = e.findParent('.form-group', false, true);
23010 if (Roo.bootstrap.version == 3) {
23011 fg.removeClass([_this.invalidClass, _this.validClass]);
23012 fg.addClass(_this.invalidClass);
23014 fg.removeClass(['is-invalid', 'is-valid']);
23015 fg.addClass('is-invalid');
23023 var fg = this.el.findParent('.form-group', false, true);
23024 if (Roo.bootstrap.version == 3) {
23025 fg.removeClass([_this.invalidClass, _this.validClass]);
23026 fg.addClass(_this.invalidClass);
23028 fg.removeClass(['is-invalid', 'is-valid']);
23029 fg.addClass('is-invalid');
23034 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23040 for(var i in group){
23041 var fg = group[i].el.findParent('.form-group', false, true);
23042 if (Roo.bootstrap.version == 3) {
23043 fg.removeClass([_this.invalidClass, _this.validClass]);
23044 fg.addClass(_this.invalidClass);
23046 fg.removeClass(['is-invalid', 'is-valid']);
23047 fg.addClass('is-invalid');
23053 clearInvalid : function()
23055 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23057 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23059 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23061 if (label && label.iconEl) {
23062 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23063 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23067 disable : function()
23069 if(this.inputType != 'radio'){
23070 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23077 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23078 _this.getActionEl().addClass(this.disabledClass);
23079 e.dom.disabled = true;
23083 this.disabled = true;
23084 this.fireEvent("disable", this);
23088 enable : function()
23090 if(this.inputType != 'radio'){
23091 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23098 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23099 _this.getActionEl().removeClass(this.disabledClass);
23100 e.dom.disabled = false;
23104 this.disabled = false;
23105 this.fireEvent("enable", this);
23109 setBoxLabel : function(v)
23114 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23120 Roo.apply(Roo.bootstrap.CheckBox, {
23125 * register a CheckBox Group
23126 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23128 register : function(checkbox)
23130 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23131 this.groups[checkbox.groupId] = {};
23134 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23138 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23142 * fetch a CheckBox Group based on the group ID
23143 * @param {string} the group ID
23144 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23146 get: function(groupId) {
23147 if (typeof(this.groups[groupId]) == 'undefined') {
23151 return this.groups[groupId] ;
23164 * @class Roo.bootstrap.Radio
23165 * @extends Roo.bootstrap.Component
23166 * Bootstrap Radio class
23167 * @cfg {String} boxLabel - the label associated
23168 * @cfg {String} value - the value of radio
23171 * Create a new Radio
23172 * @param {Object} config The config object
23174 Roo.bootstrap.Radio = function(config){
23175 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23179 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23185 getAutoCreate : function()
23189 cls : 'form-group radio',
23194 html : this.boxLabel
23202 initEvents : function()
23204 this.parent().register(this);
23206 this.el.on('click', this.onClick, this);
23210 onClick : function(e)
23212 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23213 this.setChecked(true);
23217 setChecked : function(state, suppressEvent)
23219 this.parent().setValue(this.value, suppressEvent);
23223 setBoxLabel : function(v)
23228 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23243 * @class Roo.bootstrap.SecurePass
23244 * @extends Roo.bootstrap.Input
23245 * Bootstrap SecurePass class
23249 * Create a new SecurePass
23250 * @param {Object} config The config object
23253 Roo.bootstrap.SecurePass = function (config) {
23254 // these go here, so the translation tool can replace them..
23256 PwdEmpty: "Please type a password, and then retype it to confirm.",
23257 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23258 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23259 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23260 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23261 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23262 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23263 TooWeak: "Your password is Too Weak."
23265 this.meterLabel = "Password strength:";
23266 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23267 this.meterClass = [
23268 "roo-password-meter-tooweak",
23269 "roo-password-meter-weak",
23270 "roo-password-meter-medium",
23271 "roo-password-meter-strong",
23272 "roo-password-meter-grey"
23277 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23280 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23282 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23284 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23285 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23286 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23287 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23288 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23289 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23290 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23300 * @cfg {String/Object} Label for the strength meter (defaults to
23301 * 'Password strength:')
23306 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23307 * ['Weak', 'Medium', 'Strong'])
23310 pwdStrengths: false,
23323 initEvents: function ()
23325 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23327 if (this.el.is('input[type=password]') && Roo.isSafari) {
23328 this.el.on('keydown', this.SafariOnKeyDown, this);
23331 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23334 onRender: function (ct, position)
23336 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23337 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23338 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23340 this.trigger.createChild({
23345 cls: 'roo-password-meter-grey col-xs-12',
23348 //width: this.meterWidth + 'px'
23352 cls: 'roo-password-meter-text'
23358 if (this.hideTrigger) {
23359 this.trigger.setDisplayed(false);
23361 this.setSize(this.width || '', this.height || '');
23364 onDestroy: function ()
23366 if (this.trigger) {
23367 this.trigger.removeAllListeners();
23368 this.trigger.remove();
23371 this.wrap.remove();
23373 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23376 checkStrength: function ()
23378 var pwd = this.inputEl().getValue();
23379 if (pwd == this._lastPwd) {
23384 if (this.ClientSideStrongPassword(pwd)) {
23386 } else if (this.ClientSideMediumPassword(pwd)) {
23388 } else if (this.ClientSideWeakPassword(pwd)) {
23394 Roo.log('strength1: ' + strength);
23396 //var pm = this.trigger.child('div/div/div').dom;
23397 var pm = this.trigger.child('div/div');
23398 pm.removeClass(this.meterClass);
23399 pm.addClass(this.meterClass[strength]);
23402 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23404 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23406 this._lastPwd = pwd;
23410 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23412 this._lastPwd = '';
23414 var pm = this.trigger.child('div/div');
23415 pm.removeClass(this.meterClass);
23416 pm.addClass('roo-password-meter-grey');
23419 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23422 this.inputEl().dom.type='password';
23425 validateValue: function (value)
23428 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23431 if (value.length == 0) {
23432 if (this.allowBlank) {
23433 this.clearInvalid();
23437 this.markInvalid(this.errors.PwdEmpty);
23438 this.errorMsg = this.errors.PwdEmpty;
23446 if ('[\x21-\x7e]*'.match(value)) {
23447 this.markInvalid(this.errors.PwdBadChar);
23448 this.errorMsg = this.errors.PwdBadChar;
23451 if (value.length < 6) {
23452 this.markInvalid(this.errors.PwdShort);
23453 this.errorMsg = this.errors.PwdShort;
23456 if (value.length > 16) {
23457 this.markInvalid(this.errors.PwdLong);
23458 this.errorMsg = this.errors.PwdLong;
23462 if (this.ClientSideStrongPassword(value)) {
23464 } else if (this.ClientSideMediumPassword(value)) {
23466 } else if (this.ClientSideWeakPassword(value)) {
23473 if (strength < 2) {
23474 //this.markInvalid(this.errors.TooWeak);
23475 this.errorMsg = this.errors.TooWeak;
23480 console.log('strength2: ' + strength);
23482 //var pm = this.trigger.child('div/div/div').dom;
23484 var pm = this.trigger.child('div/div');
23485 pm.removeClass(this.meterClass);
23486 pm.addClass(this.meterClass[strength]);
23488 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23490 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23492 this.errorMsg = '';
23496 CharacterSetChecks: function (type)
23499 this.fResult = false;
23502 isctype: function (character, type)
23505 case this.kCapitalLetter:
23506 if (character >= 'A' && character <= 'Z') {
23511 case this.kSmallLetter:
23512 if (character >= 'a' && character <= 'z') {
23518 if (character >= '0' && character <= '9') {
23523 case this.kPunctuation:
23524 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23535 IsLongEnough: function (pwd, size)
23537 return !(pwd == null || isNaN(size) || pwd.length < size);
23540 SpansEnoughCharacterSets: function (word, nb)
23542 if (!this.IsLongEnough(word, nb))
23547 var characterSetChecks = new Array(
23548 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23549 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23552 for (var index = 0; index < word.length; ++index) {
23553 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23554 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23555 characterSetChecks[nCharSet].fResult = true;
23562 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23563 if (characterSetChecks[nCharSet].fResult) {
23568 if (nCharSets < nb) {
23574 ClientSideStrongPassword: function (pwd)
23576 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23579 ClientSideMediumPassword: function (pwd)
23581 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23584 ClientSideWeakPassword: function (pwd)
23586 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23589 })//<script type="text/javascript">
23592 * Based Ext JS Library 1.1.1
23593 * Copyright(c) 2006-2007, Ext JS, LLC.
23599 * @class Roo.HtmlEditorCore
23600 * @extends Roo.Component
23601 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23603 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23606 Roo.HtmlEditorCore = function(config){
23609 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23614 * @event initialize
23615 * Fires when the editor is fully initialized (including the iframe)
23616 * @param {Roo.HtmlEditorCore} this
23621 * Fires when the editor is first receives the focus. Any insertion must wait
23622 * until after this event.
23623 * @param {Roo.HtmlEditorCore} this
23627 * @event beforesync
23628 * Fires before the textarea is updated with content from the editor iframe. Return false
23629 * to cancel the sync.
23630 * @param {Roo.HtmlEditorCore} this
23631 * @param {String} html
23635 * @event beforepush
23636 * Fires before the iframe editor is updated with content from the textarea. Return false
23637 * to cancel the push.
23638 * @param {Roo.HtmlEditorCore} this
23639 * @param {String} html
23644 * Fires when the textarea is updated with content from the editor iframe.
23645 * @param {Roo.HtmlEditorCore} this
23646 * @param {String} html
23651 * Fires when the iframe editor is updated with content from the textarea.
23652 * @param {Roo.HtmlEditorCore} this
23653 * @param {String} html
23658 * @event editorevent
23659 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23660 * @param {Roo.HtmlEditorCore} this
23666 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23668 // defaults : white / black...
23669 this.applyBlacklists();
23676 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23680 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23686 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23691 * @cfg {Number} height (in pixels)
23695 * @cfg {Number} width (in pixels)
23700 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23703 stylesheets: false,
23708 // private properties
23709 validationEvent : false,
23711 initialized : false,
23713 sourceEditMode : false,
23714 onFocus : Roo.emptyFn,
23716 hideMode:'offsets',
23720 // blacklist + whitelisted elements..
23727 * Protected method that will not generally be called directly. It
23728 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23729 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23731 getDocMarkup : function(){
23735 // inherit styels from page...??
23736 if (this.stylesheets === false) {
23738 Roo.get(document.head).select('style').each(function(node) {
23739 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23742 Roo.get(document.head).select('link').each(function(node) {
23743 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23746 } else if (!this.stylesheets.length) {
23748 st = '<style type="text/css">' +
23749 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23752 st = '<style type="text/css">' +
23757 st += '<style type="text/css">' +
23758 'IMG { cursor: pointer } ' +
23761 var cls = 'roo-htmleditor-body';
23763 if(this.bodyCls.length){
23764 cls += ' ' + this.bodyCls;
23767 return '<html><head>' + st +
23768 //<style type="text/css">' +
23769 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23771 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23775 onRender : function(ct, position)
23778 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23779 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23782 this.el.dom.style.border = '0 none';
23783 this.el.dom.setAttribute('tabIndex', -1);
23784 this.el.addClass('x-hidden hide');
23788 if(Roo.isIE){ // fix IE 1px bogus margin
23789 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23793 this.frameId = Roo.id();
23797 var iframe = this.owner.wrap.createChild({
23799 cls: 'form-control', // bootstrap..
23801 name: this.frameId,
23802 frameBorder : 'no',
23803 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
23808 this.iframe = iframe.dom;
23810 this.assignDocWin();
23812 this.doc.designMode = 'on';
23815 this.doc.write(this.getDocMarkup());
23819 var task = { // must defer to wait for browser to be ready
23821 //console.log("run task?" + this.doc.readyState);
23822 this.assignDocWin();
23823 if(this.doc.body || this.doc.readyState == 'complete'){
23825 this.doc.designMode="on";
23829 Roo.TaskMgr.stop(task);
23830 this.initEditor.defer(10, this);
23837 Roo.TaskMgr.start(task);
23842 onResize : function(w, h)
23844 Roo.log('resize: ' +w + ',' + h );
23845 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23849 if(typeof w == 'number'){
23851 this.iframe.style.width = w + 'px';
23853 if(typeof h == 'number'){
23855 this.iframe.style.height = h + 'px';
23857 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23864 * Toggles the editor between standard and source edit mode.
23865 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23867 toggleSourceEdit : function(sourceEditMode){
23869 this.sourceEditMode = sourceEditMode === true;
23871 if(this.sourceEditMode){
23873 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
23876 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23877 //this.iframe.className = '';
23880 //this.setSize(this.owner.wrap.getSize());
23881 //this.fireEvent('editmodechange', this, this.sourceEditMode);
23888 * Protected method that will not generally be called directly. If you need/want
23889 * custom HTML cleanup, this is the method you should override.
23890 * @param {String} html The HTML to be cleaned
23891 * return {String} The cleaned HTML
23893 cleanHtml : function(html){
23894 html = String(html);
23895 if(html.length > 5){
23896 if(Roo.isSafari){ // strip safari nonsense
23897 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23900 if(html == ' '){
23907 * HTML Editor -> Textarea
23908 * Protected method that will not generally be called directly. Syncs the contents
23909 * of the editor iframe with the textarea.
23911 syncValue : function(){
23912 if(this.initialized){
23913 var bd = (this.doc.body || this.doc.documentElement);
23914 //this.cleanUpPaste(); -- this is done else where and causes havoc..
23915 var html = bd.innerHTML;
23917 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23918 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23920 html = '<div style="'+m[0]+'">' + html + '</div>';
23923 html = this.cleanHtml(html);
23924 // fix up the special chars.. normaly like back quotes in word...
23925 // however we do not want to do this with chinese..
23926 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23928 var cc = match.charCodeAt();
23930 // Get the character value, handling surrogate pairs
23931 if (match.length == 2) {
23932 // It's a surrogate pair, calculate the Unicode code point
23933 var high = match.charCodeAt(0) - 0xD800;
23934 var low = match.charCodeAt(1) - 0xDC00;
23935 cc = (high * 0x400) + low + 0x10000;
23937 (cc >= 0x4E00 && cc < 0xA000 ) ||
23938 (cc >= 0x3400 && cc < 0x4E00 ) ||
23939 (cc >= 0xf900 && cc < 0xfb00 )
23944 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23945 return "&#" + cc + ";";
23952 if(this.owner.fireEvent('beforesync', this, html) !== false){
23953 this.el.dom.value = html;
23954 this.owner.fireEvent('sync', this, html);
23960 * Protected method that will not generally be called directly. Pushes the value of the textarea
23961 * into the iframe editor.
23963 pushValue : function(){
23964 if(this.initialized){
23965 var v = this.el.dom.value.trim();
23967 // if(v.length < 1){
23971 if(this.owner.fireEvent('beforepush', this, v) !== false){
23972 var d = (this.doc.body || this.doc.documentElement);
23974 this.cleanUpPaste();
23975 this.el.dom.value = d.innerHTML;
23976 this.owner.fireEvent('push', this, v);
23982 deferFocus : function(){
23983 this.focus.defer(10, this);
23987 focus : function(){
23988 if(this.win && !this.sourceEditMode){
23995 assignDocWin: function()
23997 var iframe = this.iframe;
24000 this.doc = iframe.contentWindow.document;
24001 this.win = iframe.contentWindow;
24003 // if (!Roo.get(this.frameId)) {
24006 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24007 // this.win = Roo.get(this.frameId).dom.contentWindow;
24009 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24013 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24014 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24019 initEditor : function(){
24020 //console.log("INIT EDITOR");
24021 this.assignDocWin();
24025 this.doc.designMode="on";
24027 this.doc.write(this.getDocMarkup());
24030 var dbody = (this.doc.body || this.doc.documentElement);
24031 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24032 // this copies styles from the containing element into thsi one..
24033 // not sure why we need all of this..
24034 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24036 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24037 //ss['background-attachment'] = 'fixed'; // w3c
24038 dbody.bgProperties = 'fixed'; // ie
24039 //Roo.DomHelper.applyStyles(dbody, ss);
24040 Roo.EventManager.on(this.doc, {
24041 //'mousedown': this.onEditorEvent,
24042 'mouseup': this.onEditorEvent,
24043 'dblclick': this.onEditorEvent,
24044 'click': this.onEditorEvent,
24045 'keyup': this.onEditorEvent,
24050 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24052 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24053 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24055 this.initialized = true;
24057 this.owner.fireEvent('initialize', this);
24062 onDestroy : function(){
24068 //for (var i =0; i < this.toolbars.length;i++) {
24069 // // fixme - ask toolbars for heights?
24070 // this.toolbars[i].onDestroy();
24073 //this.wrap.dom.innerHTML = '';
24074 //this.wrap.remove();
24079 onFirstFocus : function(){
24081 this.assignDocWin();
24084 this.activated = true;
24087 if(Roo.isGecko){ // prevent silly gecko errors
24089 var s = this.win.getSelection();
24090 if(!s.focusNode || s.focusNode.nodeType != 3){
24091 var r = s.getRangeAt(0);
24092 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24097 this.execCmd('useCSS', true);
24098 this.execCmd('styleWithCSS', false);
24101 this.owner.fireEvent('activate', this);
24105 adjustFont: function(btn){
24106 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24107 //if(Roo.isSafari){ // safari
24110 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24111 if(Roo.isSafari){ // safari
24112 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24113 v = (v < 10) ? 10 : v;
24114 v = (v > 48) ? 48 : v;
24115 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24120 v = Math.max(1, v+adjust);
24122 this.execCmd('FontSize', v );
24125 onEditorEvent : function(e)
24127 this.owner.fireEvent('editorevent', this, e);
24128 // this.updateToolbar();
24129 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24132 insertTag : function(tg)
24134 // could be a bit smarter... -> wrap the current selected tRoo..
24135 if (tg.toLowerCase() == 'span' ||
24136 tg.toLowerCase() == 'code' ||
24137 tg.toLowerCase() == 'sup' ||
24138 tg.toLowerCase() == 'sub'
24141 range = this.createRange(this.getSelection());
24142 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24143 wrappingNode.appendChild(range.extractContents());
24144 range.insertNode(wrappingNode);
24151 this.execCmd("formatblock", tg);
24155 insertText : function(txt)
24159 var range = this.createRange();
24160 range.deleteContents();
24161 //alert(Sender.getAttribute('label'));
24163 range.insertNode(this.doc.createTextNode(txt));
24169 * Executes a Midas editor command on the editor document and performs necessary focus and
24170 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24171 * @param {String} cmd The Midas command
24172 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24174 relayCmd : function(cmd, value){
24176 this.execCmd(cmd, value);
24177 this.owner.fireEvent('editorevent', this);
24178 //this.updateToolbar();
24179 this.owner.deferFocus();
24183 * Executes a Midas editor command directly on the editor document.
24184 * For visual commands, you should use {@link #relayCmd} instead.
24185 * <b>This should only be called after the editor is initialized.</b>
24186 * @param {String} cmd The Midas command
24187 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24189 execCmd : function(cmd, value){
24190 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24197 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24199 * @param {String} text | dom node..
24201 insertAtCursor : function(text)
24204 if(!this.activated){
24210 var r = this.doc.selection.createRange();
24221 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24225 // from jquery ui (MIT licenced)
24227 var win = this.win;
24229 if (win.getSelection && win.getSelection().getRangeAt) {
24230 range = win.getSelection().getRangeAt(0);
24231 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24232 range.insertNode(node);
24233 } else if (win.document.selection && win.document.selection.createRange) {
24234 // no firefox support
24235 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24236 win.document.selection.createRange().pasteHTML(txt);
24238 // no firefox support
24239 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24240 this.execCmd('InsertHTML', txt);
24249 mozKeyPress : function(e){
24251 var c = e.getCharCode(), cmd;
24254 c = String.fromCharCode(c).toLowerCase();
24268 this.cleanUpPaste.defer(100, this);
24276 e.preventDefault();
24284 fixKeys : function(){ // load time branching for fastest keydown performance
24286 return function(e){
24287 var k = e.getKey(), r;
24290 r = this.doc.selection.createRange();
24293 r.pasteHTML('    ');
24300 r = this.doc.selection.createRange();
24302 var target = r.parentElement();
24303 if(!target || target.tagName.toLowerCase() != 'li'){
24305 r.pasteHTML('<br />');
24311 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24312 this.cleanUpPaste.defer(100, this);
24318 }else if(Roo.isOpera){
24319 return function(e){
24320 var k = e.getKey();
24324 this.execCmd('InsertHTML','    ');
24327 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24328 this.cleanUpPaste.defer(100, this);
24333 }else if(Roo.isSafari){
24334 return function(e){
24335 var k = e.getKey();
24339 this.execCmd('InsertText','\t');
24343 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24344 this.cleanUpPaste.defer(100, this);
24352 getAllAncestors: function()
24354 var p = this.getSelectedNode();
24357 a.push(p); // push blank onto stack..
24358 p = this.getParentElement();
24362 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24366 a.push(this.doc.body);
24370 lastSelNode : false,
24373 getSelection : function()
24375 this.assignDocWin();
24376 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24379 getSelectedNode: function()
24381 // this may only work on Gecko!!!
24383 // should we cache this!!!!
24388 var range = this.createRange(this.getSelection()).cloneRange();
24391 var parent = range.parentElement();
24393 var testRange = range.duplicate();
24394 testRange.moveToElementText(parent);
24395 if (testRange.inRange(range)) {
24398 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24401 parent = parent.parentElement;
24406 // is ancestor a text element.
24407 var ac = range.commonAncestorContainer;
24408 if (ac.nodeType == 3) {
24409 ac = ac.parentNode;
24412 var ar = ac.childNodes;
24415 var other_nodes = [];
24416 var has_other_nodes = false;
24417 for (var i=0;i<ar.length;i++) {
24418 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24421 // fullly contained node.
24423 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24428 // probably selected..
24429 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24430 other_nodes.push(ar[i]);
24434 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24439 has_other_nodes = true;
24441 if (!nodes.length && other_nodes.length) {
24442 nodes= other_nodes;
24444 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24450 createRange: function(sel)
24452 // this has strange effects when using with
24453 // top toolbar - not sure if it's a great idea.
24454 //this.editor.contentWindow.focus();
24455 if (typeof sel != "undefined") {
24457 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24459 return this.doc.createRange();
24462 return this.doc.createRange();
24465 getParentElement: function()
24468 this.assignDocWin();
24469 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24471 var range = this.createRange(sel);
24474 var p = range.commonAncestorContainer;
24475 while (p.nodeType == 3) { // text node
24486 * Range intersection.. the hard stuff...
24490 * [ -- selected range --- ]
24494 * if end is before start or hits it. fail.
24495 * if start is after end or hits it fail.
24497 * if either hits (but other is outside. - then it's not
24503 // @see http://www.thismuchiknow.co.uk/?p=64.
24504 rangeIntersectsNode : function(range, node)
24506 var nodeRange = node.ownerDocument.createRange();
24508 nodeRange.selectNode(node);
24510 nodeRange.selectNodeContents(node);
24513 var rangeStartRange = range.cloneRange();
24514 rangeStartRange.collapse(true);
24516 var rangeEndRange = range.cloneRange();
24517 rangeEndRange.collapse(false);
24519 var nodeStartRange = nodeRange.cloneRange();
24520 nodeStartRange.collapse(true);
24522 var nodeEndRange = nodeRange.cloneRange();
24523 nodeEndRange.collapse(false);
24525 return rangeStartRange.compareBoundaryPoints(
24526 Range.START_TO_START, nodeEndRange) == -1 &&
24527 rangeEndRange.compareBoundaryPoints(
24528 Range.START_TO_START, nodeStartRange) == 1;
24532 rangeCompareNode : function(range, node)
24534 var nodeRange = node.ownerDocument.createRange();
24536 nodeRange.selectNode(node);
24538 nodeRange.selectNodeContents(node);
24542 range.collapse(true);
24544 nodeRange.collapse(true);
24546 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24547 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24549 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24551 var nodeIsBefore = ss == 1;
24552 var nodeIsAfter = ee == -1;
24554 if (nodeIsBefore && nodeIsAfter) {
24557 if (!nodeIsBefore && nodeIsAfter) {
24558 return 1; //right trailed.
24561 if (nodeIsBefore && !nodeIsAfter) {
24562 return 2; // left trailed.
24568 // private? - in a new class?
24569 cleanUpPaste : function()
24571 // cleans up the whole document..
24572 Roo.log('cleanuppaste');
24574 this.cleanUpChildren(this.doc.body);
24575 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24576 if (clean != this.doc.body.innerHTML) {
24577 this.doc.body.innerHTML = clean;
24582 cleanWordChars : function(input) {// change the chars to hex code
24583 var he = Roo.HtmlEditorCore;
24585 var output = input;
24586 Roo.each(he.swapCodes, function(sw) {
24587 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24589 output = output.replace(swapper, sw[1]);
24596 cleanUpChildren : function (n)
24598 if (!n.childNodes.length) {
24601 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24602 this.cleanUpChild(n.childNodes[i]);
24609 cleanUpChild : function (node)
24612 //console.log(node);
24613 if (node.nodeName == "#text") {
24614 // clean up silly Windows -- stuff?
24617 if (node.nodeName == "#comment") {
24618 node.parentNode.removeChild(node);
24619 // clean up silly Windows -- stuff?
24622 var lcname = node.tagName.toLowerCase();
24623 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24624 // whitelist of tags..
24626 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24628 node.parentNode.removeChild(node);
24633 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24635 // spans with no attributes - just remove them..
24636 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24637 remove_keep_children = true;
24640 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24641 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24643 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24644 // remove_keep_children = true;
24647 if (remove_keep_children) {
24648 this.cleanUpChildren(node);
24649 // inserts everything just before this node...
24650 while (node.childNodes.length) {
24651 var cn = node.childNodes[0];
24652 node.removeChild(cn);
24653 node.parentNode.insertBefore(cn, node);
24655 node.parentNode.removeChild(node);
24659 if (!node.attributes || !node.attributes.length) {
24664 this.cleanUpChildren(node);
24668 function cleanAttr(n,v)
24671 if (v.match(/^\./) || v.match(/^\//)) {
24674 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24677 if (v.match(/^#/)) {
24680 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24681 node.removeAttribute(n);
24685 var cwhite = this.cwhite;
24686 var cblack = this.cblack;
24688 function cleanStyle(n,v)
24690 if (v.match(/expression/)) { //XSS?? should we even bother..
24691 node.removeAttribute(n);
24695 var parts = v.split(/;/);
24698 Roo.each(parts, function(p) {
24699 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24703 var l = p.split(':').shift().replace(/\s+/g,'');
24704 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24706 if ( cwhite.length && cblack.indexOf(l) > -1) {
24707 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24708 //node.removeAttribute(n);
24712 // only allow 'c whitelisted system attributes'
24713 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24714 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24715 //node.removeAttribute(n);
24725 if (clean.length) {
24726 node.setAttribute(n, clean.join(';'));
24728 node.removeAttribute(n);
24734 for (var i = node.attributes.length-1; i > -1 ; i--) {
24735 var a = node.attributes[i];
24738 if (a.name.toLowerCase().substr(0,2)=='on') {
24739 node.removeAttribute(a.name);
24742 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24743 node.removeAttribute(a.name);
24746 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24747 cleanAttr(a.name,a.value); // fixme..
24750 if (a.name == 'style') {
24751 cleanStyle(a.name,a.value);
24754 /// clean up MS crap..
24755 // tecnically this should be a list of valid class'es..
24758 if (a.name == 'class') {
24759 if (a.value.match(/^Mso/)) {
24760 node.removeAttribute('class');
24763 if (a.value.match(/^body$/)) {
24764 node.removeAttribute('class');
24775 this.cleanUpChildren(node);
24781 * Clean up MS wordisms...
24783 cleanWord : function(node)
24786 this.cleanWord(this.doc.body);
24791 node.nodeName == 'SPAN' &&
24792 !node.hasAttributes() &&
24793 node.childNodes.length == 1 &&
24794 node.firstChild.nodeName == "#text"
24796 var textNode = node.firstChild;
24797 node.removeChild(textNode);
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.insertBefore(textNode, node);
24802 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24803 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24805 node.parentNode.removeChild(node);
24808 if (node.nodeName == "#text") {
24809 // clean up silly Windows -- stuff?
24812 if (node.nodeName == "#comment") {
24813 node.parentNode.removeChild(node);
24814 // clean up silly Windows -- stuff?
24818 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24819 node.parentNode.removeChild(node);
24822 //Roo.log(node.tagName);
24823 // remove - but keep children..
24824 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24825 //Roo.log('-- removed');
24826 while (node.childNodes.length) {
24827 var cn = node.childNodes[0];
24828 node.removeChild(cn);
24829 node.parentNode.insertBefore(cn, node);
24830 // move node to parent - and clean it..
24831 this.cleanWord(cn);
24833 node.parentNode.removeChild(node);
24834 /// no need to iterate chidlren = it's got none..
24835 //this.iterateChildren(node, this.cleanWord);
24839 if (node.className.length) {
24841 var cn = node.className.split(/\W+/);
24843 Roo.each(cn, function(cls) {
24844 if (cls.match(/Mso[a-zA-Z]+/)) {
24849 node.className = cna.length ? cna.join(' ') : '';
24851 node.removeAttribute("class");
24855 if (node.hasAttribute("lang")) {
24856 node.removeAttribute("lang");
24859 if (node.hasAttribute("style")) {
24861 var styles = node.getAttribute("style").split(";");
24863 Roo.each(styles, function(s) {
24864 if (!s.match(/:/)) {
24867 var kv = s.split(":");
24868 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24871 // what ever is left... we allow.
24874 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24875 if (!nstyle.length) {
24876 node.removeAttribute('style');
24879 this.iterateChildren(node, this.cleanWord);
24885 * iterateChildren of a Node, calling fn each time, using this as the scole..
24886 * @param {DomNode} node node to iterate children of.
24887 * @param {Function} fn method of this class to call on each item.
24889 iterateChildren : function(node, fn)
24891 if (!node.childNodes.length) {
24894 for (var i = node.childNodes.length-1; i > -1 ; i--) {
24895 fn.call(this, node.childNodes[i])
24901 * cleanTableWidths.
24903 * Quite often pasting from word etc.. results in tables with column and widths.
24904 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24907 cleanTableWidths : function(node)
24912 this.cleanTableWidths(this.doc.body);
24917 if (node.nodeName == "#text" || node.nodeName == "#comment") {
24920 Roo.log(node.tagName);
24921 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24922 this.iterateChildren(node, this.cleanTableWidths);
24925 if (node.hasAttribute('width')) {
24926 node.removeAttribute('width');
24930 if (node.hasAttribute("style")) {
24933 var styles = node.getAttribute("style").split(";");
24935 Roo.each(styles, function(s) {
24936 if (!s.match(/:/)) {
24939 var kv = s.split(":");
24940 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24943 // what ever is left... we allow.
24946 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24947 if (!nstyle.length) {
24948 node.removeAttribute('style');
24952 this.iterateChildren(node, this.cleanTableWidths);
24960 domToHTML : function(currentElement, depth, nopadtext) {
24962 depth = depth || 0;
24963 nopadtext = nopadtext || false;
24965 if (!currentElement) {
24966 return this.domToHTML(this.doc.body);
24969 //Roo.log(currentElement);
24971 var allText = false;
24972 var nodeName = currentElement.nodeName;
24973 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24975 if (nodeName == '#text') {
24977 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24982 if (nodeName != 'BODY') {
24985 // Prints the node tagName, such as <A>, <IMG>, etc
24988 for(i = 0; i < currentElement.attributes.length;i++) {
24990 var aname = currentElement.attributes.item(i).name;
24991 if (!currentElement.attributes.item(i).value.length) {
24994 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24997 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25006 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25009 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25014 // Traverse the tree
25016 var currentElementChild = currentElement.childNodes.item(i);
25017 var allText = true;
25018 var innerHTML = '';
25020 while (currentElementChild) {
25021 // Formatting code (indent the tree so it looks nice on the screen)
25022 var nopad = nopadtext;
25023 if (lastnode == 'SPAN') {
25027 if (currentElementChild.nodeName == '#text') {
25028 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25029 toadd = nopadtext ? toadd : toadd.trim();
25030 if (!nopad && toadd.length > 80) {
25031 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25033 innerHTML += toadd;
25036 currentElementChild = currentElement.childNodes.item(i);
25042 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25044 // Recursively traverse the tree structure of the child node
25045 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25046 lastnode = currentElementChild.nodeName;
25048 currentElementChild=currentElement.childNodes.item(i);
25054 // The remaining code is mostly for formatting the tree
25055 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25060 ret+= "</"+tagName+">";
25066 applyBlacklists : function()
25068 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25069 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25073 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25074 if (b.indexOf(tag) > -1) {
25077 this.white.push(tag);
25081 Roo.each(w, function(tag) {
25082 if (b.indexOf(tag) > -1) {
25085 if (this.white.indexOf(tag) > -1) {
25088 this.white.push(tag);
25093 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25094 if (w.indexOf(tag) > -1) {
25097 this.black.push(tag);
25101 Roo.each(b, function(tag) {
25102 if (w.indexOf(tag) > -1) {
25105 if (this.black.indexOf(tag) > -1) {
25108 this.black.push(tag);
25113 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25114 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25118 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25119 if (b.indexOf(tag) > -1) {
25122 this.cwhite.push(tag);
25126 Roo.each(w, function(tag) {
25127 if (b.indexOf(tag) > -1) {
25130 if (this.cwhite.indexOf(tag) > -1) {
25133 this.cwhite.push(tag);
25138 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25139 if (w.indexOf(tag) > -1) {
25142 this.cblack.push(tag);
25146 Roo.each(b, function(tag) {
25147 if (w.indexOf(tag) > -1) {
25150 if (this.cblack.indexOf(tag) > -1) {
25153 this.cblack.push(tag);
25158 setStylesheets : function(stylesheets)
25160 if(typeof(stylesheets) == 'string'){
25161 Roo.get(this.iframe.contentDocument.head).createChild({
25163 rel : 'stylesheet',
25172 Roo.each(stylesheets, function(s) {
25177 Roo.get(_this.iframe.contentDocument.head).createChild({
25179 rel : 'stylesheet',
25188 removeStylesheets : function()
25192 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25197 setStyle : function(style)
25199 Roo.get(this.iframe.contentDocument.head).createChild({
25208 // hide stuff that is not compatible
25222 * @event specialkey
25226 * @cfg {String} fieldClass @hide
25229 * @cfg {String} focusClass @hide
25232 * @cfg {String} autoCreate @hide
25235 * @cfg {String} inputType @hide
25238 * @cfg {String} invalidClass @hide
25241 * @cfg {String} invalidText @hide
25244 * @cfg {String} msgFx @hide
25247 * @cfg {String} validateOnBlur @hide
25251 Roo.HtmlEditorCore.white = [
25252 'area', 'br', 'img', 'input', 'hr', 'wbr',
25254 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25255 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25256 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25257 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25258 'table', 'ul', 'xmp',
25260 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25263 'dir', 'menu', 'ol', 'ul', 'dl',
25269 Roo.HtmlEditorCore.black = [
25270 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25272 'base', 'basefont', 'bgsound', 'blink', 'body',
25273 'frame', 'frameset', 'head', 'html', 'ilayer',
25274 'iframe', 'layer', 'link', 'meta', 'object',
25275 'script', 'style' ,'title', 'xml' // clean later..
25277 Roo.HtmlEditorCore.clean = [
25278 'script', 'style', 'title', 'xml'
25280 Roo.HtmlEditorCore.remove = [
25285 Roo.HtmlEditorCore.ablack = [
25289 Roo.HtmlEditorCore.aclean = [
25290 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25294 Roo.HtmlEditorCore.pwhite= [
25295 'http', 'https', 'mailto'
25298 // white listed style attributes.
25299 Roo.HtmlEditorCore.cwhite= [
25300 // 'text-align', /// default is to allow most things..
25306 // black listed style attributes.
25307 Roo.HtmlEditorCore.cblack= [
25308 // 'font-size' -- this can be set by the project
25312 Roo.HtmlEditorCore.swapCodes =[
25331 * @class Roo.bootstrap.HtmlEditor
25332 * @extends Roo.bootstrap.TextArea
25333 * Bootstrap HtmlEditor class
25336 * Create a new HtmlEditor
25337 * @param {Object} config The config object
25340 Roo.bootstrap.HtmlEditor = function(config){
25341 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25342 if (!this.toolbars) {
25343 this.toolbars = [];
25346 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25349 * @event initialize
25350 * Fires when the editor is fully initialized (including the iframe)
25351 * @param {HtmlEditor} this
25356 * Fires when the editor is first receives the focus. Any insertion must wait
25357 * until after this event.
25358 * @param {HtmlEditor} this
25362 * @event beforesync
25363 * Fires before the textarea is updated with content from the editor iframe. Return false
25364 * to cancel the sync.
25365 * @param {HtmlEditor} this
25366 * @param {String} html
25370 * @event beforepush
25371 * Fires before the iframe editor is updated with content from the textarea. Return false
25372 * to cancel the push.
25373 * @param {HtmlEditor} this
25374 * @param {String} html
25379 * Fires when the textarea is updated with content from the editor iframe.
25380 * @param {HtmlEditor} this
25381 * @param {String} html
25386 * Fires when the iframe editor is updated with content from the textarea.
25387 * @param {HtmlEditor} this
25388 * @param {String} html
25392 * @event editmodechange
25393 * Fires when the editor switches edit modes
25394 * @param {HtmlEditor} this
25395 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25397 editmodechange: true,
25399 * @event editorevent
25400 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25401 * @param {HtmlEditor} this
25405 * @event firstfocus
25406 * Fires when on first focus - needed by toolbars..
25407 * @param {HtmlEditor} this
25412 * Auto save the htmlEditor value as a file into Events
25413 * @param {HtmlEditor} this
25417 * @event savedpreview
25418 * preview the saved version of htmlEditor
25419 * @param {HtmlEditor} this
25426 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25430 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25435 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25440 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25445 * @cfg {Number} height (in pixels)
25449 * @cfg {Number} width (in pixels)
25454 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25457 stylesheets: false,
25462 // private properties
25463 validationEvent : false,
25465 initialized : false,
25468 onFocus : Roo.emptyFn,
25470 hideMode:'offsets',
25472 tbContainer : false,
25476 toolbarContainer :function() {
25477 return this.wrap.select('.x-html-editor-tb',true).first();
25481 * Protected method that will not generally be called directly. It
25482 * is called when the editor creates its toolbar. Override this method if you need to
25483 * add custom toolbar buttons.
25484 * @param {HtmlEditor} editor
25486 createToolbar : function(){
25487 Roo.log('renewing');
25488 Roo.log("create toolbars");
25490 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25491 this.toolbars[0].render(this.toolbarContainer());
25495 // if (!editor.toolbars || !editor.toolbars.length) {
25496 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25499 // for (var i =0 ; i < editor.toolbars.length;i++) {
25500 // editor.toolbars[i] = Roo.factory(
25501 // typeof(editor.toolbars[i]) == 'string' ?
25502 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25503 // Roo.bootstrap.HtmlEditor);
25504 // editor.toolbars[i].init(editor);
25510 onRender : function(ct, position)
25512 // Roo.log("Call onRender: " + this.xtype);
25514 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25516 this.wrap = this.inputEl().wrap({
25517 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25520 this.editorcore.onRender(ct, position);
25522 if (this.resizable) {
25523 this.resizeEl = new Roo.Resizable(this.wrap, {
25527 minHeight : this.height,
25528 height: this.height,
25529 handles : this.resizable,
25532 resize : function(r, w, h) {
25533 _t.onResize(w,h); // -something
25539 this.createToolbar(this);
25542 if(!this.width && this.resizable){
25543 this.setSize(this.wrap.getSize());
25545 if (this.resizeEl) {
25546 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25547 // should trigger onReize..
25553 onResize : function(w, h)
25555 Roo.log('resize: ' +w + ',' + h );
25556 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25560 if(this.inputEl() ){
25561 if(typeof w == 'number'){
25562 var aw = w - this.wrap.getFrameWidth('lr');
25563 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25566 if(typeof h == 'number'){
25567 var tbh = -11; // fixme it needs to tool bar size!
25568 for (var i =0; i < this.toolbars.length;i++) {
25569 // fixme - ask toolbars for heights?
25570 tbh += this.toolbars[i].el.getHeight();
25571 //if (this.toolbars[i].footer) {
25572 // tbh += this.toolbars[i].footer.el.getHeight();
25580 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25581 ah -= 5; // knock a few pixes off for look..
25582 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25586 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25587 this.editorcore.onResize(ew,eh);
25592 * Toggles the editor between standard and source edit mode.
25593 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25595 toggleSourceEdit : function(sourceEditMode)
25597 this.editorcore.toggleSourceEdit(sourceEditMode);
25599 if(this.editorcore.sourceEditMode){
25600 Roo.log('editor - showing textarea');
25603 // Roo.log(this.syncValue());
25605 this.inputEl().removeClass(['hide', 'x-hidden']);
25606 this.inputEl().dom.removeAttribute('tabIndex');
25607 this.inputEl().focus();
25609 Roo.log('editor - hiding textarea');
25611 // Roo.log(this.pushValue());
25614 this.inputEl().addClass(['hide', 'x-hidden']);
25615 this.inputEl().dom.setAttribute('tabIndex', -1);
25616 //this.deferFocus();
25619 if(this.resizable){
25620 this.setSize(this.wrap.getSize());
25623 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25626 // private (for BoxComponent)
25627 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25629 // private (for BoxComponent)
25630 getResizeEl : function(){
25634 // private (for BoxComponent)
25635 getPositionEl : function(){
25640 initEvents : function(){
25641 this.originalValue = this.getValue();
25645 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25648 // markInvalid : Roo.emptyFn,
25650 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25653 // clearInvalid : Roo.emptyFn,
25655 setValue : function(v){
25656 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25657 this.editorcore.pushValue();
25662 deferFocus : function(){
25663 this.focus.defer(10, this);
25667 focus : function(){
25668 this.editorcore.focus();
25674 onDestroy : function(){
25680 for (var i =0; i < this.toolbars.length;i++) {
25681 // fixme - ask toolbars for heights?
25682 this.toolbars[i].onDestroy();
25685 this.wrap.dom.innerHTML = '';
25686 this.wrap.remove();
25691 onFirstFocus : function(){
25692 //Roo.log("onFirstFocus");
25693 this.editorcore.onFirstFocus();
25694 for (var i =0; i < this.toolbars.length;i++) {
25695 this.toolbars[i].onFirstFocus();
25701 syncValue : function()
25703 this.editorcore.syncValue();
25706 pushValue : function()
25708 this.editorcore.pushValue();
25712 // hide stuff that is not compatible
25726 * @event specialkey
25730 * @cfg {String} fieldClass @hide
25733 * @cfg {String} focusClass @hide
25736 * @cfg {String} autoCreate @hide
25739 * @cfg {String} inputType @hide
25743 * @cfg {String} invalidText @hide
25746 * @cfg {String} msgFx @hide
25749 * @cfg {String} validateOnBlur @hide
25758 Roo.namespace('Roo.bootstrap.htmleditor');
25760 * @class Roo.bootstrap.HtmlEditorToolbar1
25766 new Roo.bootstrap.HtmlEditor({
25769 new Roo.bootstrap.HtmlEditorToolbar1({
25770 disable : { fonts: 1 , format: 1, ..., ... , ...],
25776 * @cfg {Object} disable List of elements to disable..
25777 * @cfg {Array} btns List of additional buttons.
25781 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25784 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25787 Roo.apply(this, config);
25789 // default disabled, based on 'good practice'..
25790 this.disable = this.disable || {};
25791 Roo.applyIf(this.disable, {
25794 specialElements : true
25796 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25798 this.editor = config.editor;
25799 this.editorcore = config.editor.editorcore;
25801 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25803 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25804 // dont call parent... till later.
25806 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
25811 editorcore : false,
25816 "h1","h2","h3","h4","h5","h6",
25818 "abbr", "acronym", "address", "cite", "samp", "var",
25822 onRender : function(ct, position)
25824 // Roo.log("Call onRender: " + this.xtype);
25826 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25828 this.el.dom.style.marginBottom = '0';
25830 var editorcore = this.editorcore;
25831 var editor= this.editor;
25834 var btn = function(id,cmd , toggle, handler, html){
25836 var event = toggle ? 'toggle' : 'click';
25841 xns: Roo.bootstrap,
25845 enableToggle:toggle !== false,
25847 pressed : toggle ? false : null,
25850 a.listeners[toggle ? 'toggle' : 'click'] = function() {
25851 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
25857 // var cb_box = function...
25862 xns: Roo.bootstrap,
25867 xns: Roo.bootstrap,
25871 Roo.each(this.formats, function(f) {
25872 style.menu.items.push({
25874 xns: Roo.bootstrap,
25875 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25880 editorcore.insertTag(this.tagname);
25887 children.push(style);
25889 btn('bold',false,true);
25890 btn('italic',false,true);
25891 btn('align-left', 'justifyleft',true);
25892 btn('align-center', 'justifycenter',true);
25893 btn('align-right' , 'justifyright',true);
25894 btn('link', false, false, function(btn) {
25895 //Roo.log("create link?");
25896 var url = prompt(this.createLinkText, this.defaultLinkValue);
25897 if(url && url != 'http:/'+'/'){
25898 this.editorcore.relayCmd('createlink', url);
25901 btn('list','insertunorderedlist',true);
25902 btn('pencil', false,true, function(btn){
25904 this.toggleSourceEdit(btn.pressed);
25907 if (this.editor.btns.length > 0) {
25908 for (var i = 0; i<this.editor.btns.length; i++) {
25909 children.push(this.editor.btns[i]);
25917 xns: Roo.bootstrap,
25922 xns: Roo.bootstrap,
25927 cog.menu.items.push({
25929 xns: Roo.bootstrap,
25930 html : Clean styles,
25935 editorcore.insertTag(this.tagname);
25944 this.xtype = 'NavSimplebar';
25946 for(var i=0;i< children.length;i++) {
25948 this.buttons.add(this.addxtypeChild(children[i]));
25952 editor.on('editorevent', this.updateToolbar, this);
25954 onBtnClick : function(id)
25956 this.editorcore.relayCmd(id);
25957 this.editorcore.focus();
25961 * Protected method that will not generally be called directly. It triggers
25962 * a toolbar update by reading the markup state of the current selection in the editor.
25964 updateToolbar: function(){
25966 if(!this.editorcore.activated){
25967 this.editor.onFirstFocus(); // is this neeed?
25971 var btns = this.buttons;
25972 var doc = this.editorcore.doc;
25973 btns.get('bold').setActive(doc.queryCommandState('bold'));
25974 btns.get('italic').setActive(doc.queryCommandState('italic'));
25975 //btns.get('underline').setActive(doc.queryCommandState('underline'));
25977 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25978 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25979 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25981 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25982 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25985 var ans = this.editorcore.getAllAncestors();
25986 if (this.formatCombo) {
25989 var store = this.formatCombo.store;
25990 this.formatCombo.setValue("");
25991 for (var i =0; i < ans.length;i++) {
25992 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25994 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26002 // hides menus... - so this cant be on a menu...
26003 Roo.bootstrap.MenuMgr.hideAll();
26005 Roo.bootstrap.MenuMgr.hideAll();
26006 //this.editorsyncValue();
26008 onFirstFocus: function() {
26009 this.buttons.each(function(item){
26013 toggleSourceEdit : function(sourceEditMode){
26016 if(sourceEditMode){
26017 Roo.log("disabling buttons");
26018 this.buttons.each( function(item){
26019 if(item.cmd != 'pencil'){
26025 Roo.log("enabling buttons");
26026 if(this.editorcore.initialized){
26027 this.buttons.each( function(item){
26033 Roo.log("calling toggole on editor");
26034 // tell the editor that it's been pressed..
26035 this.editor.toggleSourceEdit(sourceEditMode);
26045 * @class Roo.bootstrap.Table.AbstractSelectionModel
26046 * @extends Roo.util.Observable
26047 * Abstract base class for grid SelectionModels. It provides the interface that should be
26048 * implemented by descendant classes. This class should not be directly instantiated.
26051 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26052 this.locked = false;
26053 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26057 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26058 /** @ignore Called by the grid automatically. Do not call directly. */
26059 init : function(grid){
26065 * Locks the selections.
26068 this.locked = true;
26072 * Unlocks the selections.
26074 unlock : function(){
26075 this.locked = false;
26079 * Returns true if the selections are locked.
26080 * @return {Boolean}
26082 isLocked : function(){
26083 return this.locked;
26087 initEvents : function ()
26093 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26094 * @class Roo.bootstrap.Table.RowSelectionModel
26095 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26096 * It supports multiple selections and keyboard selection/navigation.
26098 * @param {Object} config
26101 Roo.bootstrap.Table.RowSelectionModel = function(config){
26102 Roo.apply(this, config);
26103 this.selections = new Roo.util.MixedCollection(false, function(o){
26108 this.lastActive = false;
26112 * @event selectionchange
26113 * Fires when the selection changes
26114 * @param {SelectionModel} this
26116 "selectionchange" : true,
26118 * @event afterselectionchange
26119 * Fires after the selection changes (eg. by key press or clicking)
26120 * @param {SelectionModel} this
26122 "afterselectionchange" : true,
26124 * @event beforerowselect
26125 * Fires when a row is selected being selected, return false to cancel.
26126 * @param {SelectionModel} this
26127 * @param {Number} rowIndex The selected index
26128 * @param {Boolean} keepExisting False if other selections will be cleared
26130 "beforerowselect" : true,
26133 * Fires when a row is selected.
26134 * @param {SelectionModel} this
26135 * @param {Number} rowIndex The selected index
26136 * @param {Roo.data.Record} r The record
26138 "rowselect" : true,
26140 * @event rowdeselect
26141 * Fires when a row is deselected.
26142 * @param {SelectionModel} this
26143 * @param {Number} rowIndex The selected index
26145 "rowdeselect" : true
26147 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26148 this.locked = false;
26151 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26153 * @cfg {Boolean} singleSelect
26154 * True to allow selection of only one row at a time (defaults to false)
26156 singleSelect : false,
26159 initEvents : function()
26162 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26163 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26164 //}else{ // allow click to work like normal
26165 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26167 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26168 this.grid.on("rowclick", this.handleMouseDown, this);
26170 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26171 "up" : function(e){
26173 this.selectPrevious(e.shiftKey);
26174 }else if(this.last !== false && this.lastActive !== false){
26175 var last = this.last;
26176 this.selectRange(this.last, this.lastActive-1);
26177 this.grid.getView().focusRow(this.lastActive);
26178 if(last !== false){
26182 this.selectFirstRow();
26184 this.fireEvent("afterselectionchange", this);
26186 "down" : function(e){
26188 this.selectNext(e.shiftKey);
26189 }else if(this.last !== false && this.lastActive !== false){
26190 var last = this.last;
26191 this.selectRange(this.last, this.lastActive+1);
26192 this.grid.getView().focusRow(this.lastActive);
26193 if(last !== false){
26197 this.selectFirstRow();
26199 this.fireEvent("afterselectionchange", this);
26203 this.grid.store.on('load', function(){
26204 this.selections.clear();
26207 var view = this.grid.view;
26208 view.on("refresh", this.onRefresh, this);
26209 view.on("rowupdated", this.onRowUpdated, this);
26210 view.on("rowremoved", this.onRemove, this);
26215 onRefresh : function()
26217 var ds = this.grid.store, i, v = this.grid.view;
26218 var s = this.selections;
26219 s.each(function(r){
26220 if((i = ds.indexOfId(r.id)) != -1){
26229 onRemove : function(v, index, r){
26230 this.selections.remove(r);
26234 onRowUpdated : function(v, index, r){
26235 if(this.isSelected(r)){
26236 v.onRowSelect(index);
26242 * @param {Array} records The records to select
26243 * @param {Boolean} keepExisting (optional) True to keep existing selections
26245 selectRecords : function(records, keepExisting)
26248 this.clearSelections();
26250 var ds = this.grid.store;
26251 for(var i = 0, len = records.length; i < len; i++){
26252 this.selectRow(ds.indexOf(records[i]), true);
26257 * Gets the number of selected rows.
26260 getCount : function(){
26261 return this.selections.length;
26265 * Selects the first row in the grid.
26267 selectFirstRow : function(){
26272 * Select the last row.
26273 * @param {Boolean} keepExisting (optional) True to keep existing selections
26275 selectLastRow : function(keepExisting){
26276 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26277 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26281 * Selects the row immediately following the last selected row.
26282 * @param {Boolean} keepExisting (optional) True to keep existing selections
26284 selectNext : function(keepExisting)
26286 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26287 this.selectRow(this.last+1, keepExisting);
26288 this.grid.getView().focusRow(this.last);
26293 * Selects the row that precedes the last selected row.
26294 * @param {Boolean} keepExisting (optional) True to keep existing selections
26296 selectPrevious : function(keepExisting){
26298 this.selectRow(this.last-1, keepExisting);
26299 this.grid.getView().focusRow(this.last);
26304 * Returns the selected records
26305 * @return {Array} Array of selected records
26307 getSelections : function(){
26308 return [].concat(this.selections.items);
26312 * Returns the first selected record.
26315 getSelected : function(){
26316 return this.selections.itemAt(0);
26321 * Clears all selections.
26323 clearSelections : function(fast)
26329 var ds = this.grid.store;
26330 var s = this.selections;
26331 s.each(function(r){
26332 this.deselectRow(ds.indexOfId(r.id));
26336 this.selections.clear();
26343 * Selects all rows.
26345 selectAll : function(){
26349 this.selections.clear();
26350 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26351 this.selectRow(i, true);
26356 * Returns True if there is a selection.
26357 * @return {Boolean}
26359 hasSelection : function(){
26360 return this.selections.length > 0;
26364 * Returns True if the specified row is selected.
26365 * @param {Number/Record} record The record or index of the record to check
26366 * @return {Boolean}
26368 isSelected : function(index){
26369 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26370 return (r && this.selections.key(r.id) ? true : false);
26374 * Returns True if the specified record id is selected.
26375 * @param {String} id The id of record to check
26376 * @return {Boolean}
26378 isIdSelected : function(id){
26379 return (this.selections.key(id) ? true : false);
26384 handleMouseDBClick : function(e, t){
26388 handleMouseDown : function(e, t)
26390 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26391 if(this.isLocked() || rowIndex < 0 ){
26394 if(e.shiftKey && this.last !== false){
26395 var last = this.last;
26396 this.selectRange(last, rowIndex, e.ctrlKey);
26397 this.last = last; // reset the last
26401 var isSelected = this.isSelected(rowIndex);
26402 //Roo.log("select row:" + rowIndex);
26404 this.deselectRow(rowIndex);
26406 this.selectRow(rowIndex, true);
26410 if(e.button !== 0 && isSelected){
26411 alert('rowIndex 2: ' + rowIndex);
26412 view.focusRow(rowIndex);
26413 }else if(e.ctrlKey && isSelected){
26414 this.deselectRow(rowIndex);
26415 }else if(!isSelected){
26416 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26417 view.focusRow(rowIndex);
26421 this.fireEvent("afterselectionchange", this);
26424 handleDragableRowClick : function(grid, rowIndex, e)
26426 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26427 this.selectRow(rowIndex, false);
26428 grid.view.focusRow(rowIndex);
26429 this.fireEvent("afterselectionchange", this);
26434 * Selects multiple rows.
26435 * @param {Array} rows Array of the indexes of the row to select
26436 * @param {Boolean} keepExisting (optional) True to keep existing selections
26438 selectRows : function(rows, keepExisting){
26440 this.clearSelections();
26442 for(var i = 0, len = rows.length; i < len; i++){
26443 this.selectRow(rows[i], true);
26448 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26449 * @param {Number} startRow The index of the first row in the range
26450 * @param {Number} endRow The index of the last row in the range
26451 * @param {Boolean} keepExisting (optional) True to retain existing selections
26453 selectRange : function(startRow, endRow, keepExisting){
26458 this.clearSelections();
26460 if(startRow <= endRow){
26461 for(var i = startRow; i <= endRow; i++){
26462 this.selectRow(i, true);
26465 for(var i = startRow; i >= endRow; i--){
26466 this.selectRow(i, true);
26472 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26473 * @param {Number} startRow The index of the first row in the range
26474 * @param {Number} endRow The index of the last row in the range
26476 deselectRange : function(startRow, endRow, preventViewNotify){
26480 for(var i = startRow; i <= endRow; i++){
26481 this.deselectRow(i, preventViewNotify);
26487 * @param {Number} row The index of the row to select
26488 * @param {Boolean} keepExisting (optional) True to keep existing selections
26490 selectRow : function(index, keepExisting, preventViewNotify)
26492 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26495 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26496 if(!keepExisting || this.singleSelect){
26497 this.clearSelections();
26500 var r = this.grid.store.getAt(index);
26501 //console.log('selectRow - record id :' + r.id);
26503 this.selections.add(r);
26504 this.last = this.lastActive = index;
26505 if(!preventViewNotify){
26506 var proxy = new Roo.Element(
26507 this.grid.getRowDom(index)
26509 proxy.addClass('bg-info info');
26511 this.fireEvent("rowselect", this, index, r);
26512 this.fireEvent("selectionchange", this);
26518 * @param {Number} row The index of the row to deselect
26520 deselectRow : function(index, preventViewNotify)
26525 if(this.last == index){
26528 if(this.lastActive == index){
26529 this.lastActive = false;
26532 var r = this.grid.store.getAt(index);
26537 this.selections.remove(r);
26538 //.console.log('deselectRow - record id :' + r.id);
26539 if(!preventViewNotify){
26541 var proxy = new Roo.Element(
26542 this.grid.getRowDom(index)
26544 proxy.removeClass('bg-info info');
26546 this.fireEvent("rowdeselect", this, index);
26547 this.fireEvent("selectionchange", this);
26551 restoreLast : function(){
26553 this.last = this._last;
26558 acceptsNav : function(row, col, cm){
26559 return !cm.isHidden(col) && cm.isCellEditable(col, row);
26563 onEditorKey : function(field, e){
26564 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26569 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26571 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26573 }else if(k == e.ENTER && !e.ctrlKey){
26577 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26579 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26581 }else if(k == e.ESC){
26585 g.startEditing(newCell[0], newCell[1]);
26591 * Ext JS Library 1.1.1
26592 * Copyright(c) 2006-2007, Ext JS, LLC.
26594 * Originally Released Under LGPL - original licence link has changed is not relivant.
26597 * <script type="text/javascript">
26601 * @class Roo.bootstrap.PagingToolbar
26602 * @extends Roo.bootstrap.NavSimplebar
26603 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26605 * Create a new PagingToolbar
26606 * @param {Object} config The config object
26607 * @param {Roo.data.Store} store
26609 Roo.bootstrap.PagingToolbar = function(config)
26611 // old args format still supported... - xtype is prefered..
26612 // created from xtype...
26614 this.ds = config.dataSource;
26616 if (config.store && !this.ds) {
26617 this.store= Roo.factory(config.store, Roo.data);
26618 this.ds = this.store;
26619 this.ds.xmodule = this.xmodule || false;
26622 this.toolbarItems = [];
26623 if (config.items) {
26624 this.toolbarItems = config.items;
26627 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26632 this.bind(this.ds);
26635 if (Roo.bootstrap.version == 4) {
26636 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26638 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26643 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26645 * @cfg {Roo.data.Store} dataSource
26646 * The underlying data store providing the paged data
26649 * @cfg {String/HTMLElement/Element} container
26650 * container The id or element that will contain the toolbar
26653 * @cfg {Boolean} displayInfo
26654 * True to display the displayMsg (defaults to false)
26657 * @cfg {Number} pageSize
26658 * The number of records to display per page (defaults to 20)
26662 * @cfg {String} displayMsg
26663 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26665 displayMsg : 'Displaying {0} - {1} of {2}',
26667 * @cfg {String} emptyMsg
26668 * The message to display when no records are found (defaults to "No data to display")
26670 emptyMsg : 'No data to display',
26672 * Customizable piece of the default paging text (defaults to "Page")
26675 beforePageText : "Page",
26677 * Customizable piece of the default paging text (defaults to "of %0")
26680 afterPageText : "of {0}",
26682 * Customizable piece of the default paging text (defaults to "First Page")
26685 firstText : "First Page",
26687 * Customizable piece of the default paging text (defaults to "Previous Page")
26690 prevText : "Previous Page",
26692 * Customizable piece of the default paging text (defaults to "Next Page")
26695 nextText : "Next Page",
26697 * Customizable piece of the default paging text (defaults to "Last Page")
26700 lastText : "Last Page",
26702 * Customizable piece of the default paging text (defaults to "Refresh")
26705 refreshText : "Refresh",
26709 onRender : function(ct, position)
26711 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26712 this.navgroup.parentId = this.id;
26713 this.navgroup.onRender(this.el, null);
26714 // add the buttons to the navgroup
26716 if(this.displayInfo){
26717 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26718 this.displayEl = this.el.select('.x-paging-info', true).first();
26719 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26720 // this.displayEl = navel.el.select('span',true).first();
26726 Roo.each(_this.buttons, function(e){ // this might need to use render????
26727 Roo.factory(e).render(_this.el);
26731 Roo.each(_this.toolbarItems, function(e) {
26732 _this.navgroup.addItem(e);
26736 this.first = this.navgroup.addItem({
26737 tooltip: this.firstText,
26738 cls: "prev btn-outline-secondary",
26739 html : ' <i class="fa fa-step-backward"></i>',
26741 preventDefault: true,
26742 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26745 this.prev = this.navgroup.addItem({
26746 tooltip: this.prevText,
26747 cls: "prev btn-outline-secondary",
26748 html : ' <i class="fa fa-backward"></i>',
26750 preventDefault: true,
26751 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
26753 //this.addSeparator();
26756 var field = this.navgroup.addItem( {
26758 cls : 'x-paging-position btn-outline-secondary',
26760 html : this.beforePageText +
26761 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26762 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
26765 this.field = field.el.select('input', true).first();
26766 this.field.on("keydown", this.onPagingKeydown, this);
26767 this.field.on("focus", function(){this.dom.select();});
26770 this.afterTextEl = field.el.select('.x-paging-after',true).first();
26771 //this.field.setHeight(18);
26772 //this.addSeparator();
26773 this.next = this.navgroup.addItem({
26774 tooltip: this.nextText,
26775 cls: "next btn-outline-secondary",
26776 html : ' <i class="fa fa-forward"></i>',
26778 preventDefault: true,
26779 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
26781 this.last = this.navgroup.addItem({
26782 tooltip: this.lastText,
26783 html : ' <i class="fa fa-step-forward"></i>',
26784 cls: "next btn-outline-secondary",
26786 preventDefault: true,
26787 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
26789 //this.addSeparator();
26790 this.loading = this.navgroup.addItem({
26791 tooltip: this.refreshText,
26792 cls: "btn-outline-secondary",
26793 html : ' <i class="fa fa-refresh"></i>',
26794 preventDefault: true,
26795 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26801 updateInfo : function(){
26802 if(this.displayEl){
26803 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26804 var msg = count == 0 ?
26808 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
26810 this.displayEl.update(msg);
26815 onLoad : function(ds, r, o)
26817 this.cursor = o.params.start ? o.params.start : 0;
26819 var d = this.getPageData(),
26824 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26825 this.field.dom.value = ap;
26826 this.first.setDisabled(ap == 1);
26827 this.prev.setDisabled(ap == 1);
26828 this.next.setDisabled(ap == ps);
26829 this.last.setDisabled(ap == ps);
26830 this.loading.enable();
26835 getPageData : function(){
26836 var total = this.ds.getTotalCount();
26839 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26840 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26845 onLoadError : function(){
26846 this.loading.enable();
26850 onPagingKeydown : function(e){
26851 var k = e.getKey();
26852 var d = this.getPageData();
26854 var v = this.field.dom.value, pageNum;
26855 if(!v || isNaN(pageNum = parseInt(v, 10))){
26856 this.field.dom.value = d.activePage;
26859 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26860 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26863 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))
26865 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26866 this.field.dom.value = pageNum;
26867 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26870 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26872 var v = this.field.dom.value, pageNum;
26873 var increment = (e.shiftKey) ? 10 : 1;
26874 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26877 if(!v || isNaN(pageNum = parseInt(v, 10))) {
26878 this.field.dom.value = d.activePage;
26881 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26883 this.field.dom.value = parseInt(v, 10) + increment;
26884 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26885 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26892 beforeLoad : function(){
26894 this.loading.disable();
26899 onClick : function(which){
26908 ds.load({params:{start: 0, limit: this.pageSize}});
26911 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26914 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26917 var total = ds.getTotalCount();
26918 var extra = total % this.pageSize;
26919 var lastStart = extra ? (total - extra) : total-this.pageSize;
26920 ds.load({params:{start: lastStart, limit: this.pageSize}});
26923 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26929 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26930 * @param {Roo.data.Store} store The data store to unbind
26932 unbind : function(ds){
26933 ds.un("beforeload", this.beforeLoad, this);
26934 ds.un("load", this.onLoad, this);
26935 ds.un("loadexception", this.onLoadError, this);
26936 ds.un("remove", this.updateInfo, this);
26937 ds.un("add", this.updateInfo, this);
26938 this.ds = undefined;
26942 * Binds the paging toolbar to the specified {@link Roo.data.Store}
26943 * @param {Roo.data.Store} store The data store to bind
26945 bind : function(ds){
26946 ds.on("beforeload", this.beforeLoad, this);
26947 ds.on("load", this.onLoad, this);
26948 ds.on("loadexception", this.onLoadError, this);
26949 ds.on("remove", this.updateInfo, this);
26950 ds.on("add", this.updateInfo, this);
26961 * @class Roo.bootstrap.MessageBar
26962 * @extends Roo.bootstrap.Component
26963 * Bootstrap MessageBar class
26964 * @cfg {String} html contents of the MessageBar
26965 * @cfg {String} weight (info | success | warning | danger) default info
26966 * @cfg {String} beforeClass insert the bar before the given class
26967 * @cfg {Boolean} closable (true | false) default false
26968 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26971 * Create a new Element
26972 * @param {Object} config The config object
26975 Roo.bootstrap.MessageBar = function(config){
26976 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26979 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
26985 beforeClass: 'bootstrap-sticky-wrap',
26987 getAutoCreate : function(){
26991 cls: 'alert alert-dismissable alert-' + this.weight,
26996 html: this.html || ''
27002 cfg.cls += ' alert-messages-fixed';
27016 onRender : function(ct, position)
27018 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27021 var cfg = Roo.apply({}, this.getAutoCreate());
27025 cfg.cls += ' ' + this.cls;
27028 cfg.style = this.style;
27030 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27032 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27035 this.el.select('>button.close').on('click', this.hide, this);
27041 if (!this.rendered) {
27047 this.fireEvent('show', this);
27053 if (!this.rendered) {
27059 this.fireEvent('hide', this);
27062 update : function()
27064 // var e = this.el.dom.firstChild;
27066 // if(this.closable){
27067 // e = e.nextSibling;
27070 // e.data = this.html || '';
27072 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27088 * @class Roo.bootstrap.Graph
27089 * @extends Roo.bootstrap.Component
27090 * Bootstrap Graph class
27094 @cfg {String} graphtype bar | vbar | pie
27095 @cfg {number} g_x coodinator | centre x (pie)
27096 @cfg {number} g_y coodinator | centre y (pie)
27097 @cfg {number} g_r radius (pie)
27098 @cfg {number} g_height height of the chart (respected by all elements in the set)
27099 @cfg {number} g_width width of the chart (respected by all elements in the set)
27100 @cfg {Object} title The title of the chart
27103 -opts (object) options for the chart
27105 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27106 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27108 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.
27109 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27111 o stretch (boolean)
27113 -opts (object) options for the pie
27116 o startAngle (number)
27117 o endAngle (number)
27121 * Create a new Input
27122 * @param {Object} config The config object
27125 Roo.bootstrap.Graph = function(config){
27126 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27132 * The img click event for the img.
27133 * @param {Roo.EventObject} e
27139 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27150 //g_colors: this.colors,
27157 getAutoCreate : function(){
27168 onRender : function(ct,position){
27171 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27173 if (typeof(Raphael) == 'undefined') {
27174 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27178 this.raphael = Raphael(this.el.dom);
27180 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27181 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27182 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27183 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27185 r.text(160, 10, "Single Series Chart").attr(txtattr);
27186 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27187 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27188 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27190 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27191 r.barchart(330, 10, 300, 220, data1);
27192 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27193 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27196 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27197 // r.barchart(30, 30, 560, 250, xdata, {
27198 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27199 // axis : "0 0 1 1",
27200 // axisxlabels : xdata
27201 // //yvalues : cols,
27204 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27206 // this.load(null,xdata,{
27207 // axis : "0 0 1 1",
27208 // axisxlabels : xdata
27213 load : function(graphtype,xdata,opts)
27215 this.raphael.clear();
27217 graphtype = this.graphtype;
27222 var r = this.raphael,
27223 fin = function () {
27224 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27226 fout = function () {
27227 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27229 pfin = function() {
27230 this.sector.stop();
27231 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27234 this.label[0].stop();
27235 this.label[0].attr({ r: 7.5 });
27236 this.label[1].attr({ "font-weight": 800 });
27239 pfout = function() {
27240 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27243 this.label[0].animate({ r: 5 }, 500, "bounce");
27244 this.label[1].attr({ "font-weight": 400 });
27250 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27253 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27256 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27257 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27259 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27266 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27271 setTitle: function(o)
27276 initEvents: function() {
27279 this.el.on('click', this.onClick, this);
27283 onClick : function(e)
27285 Roo.log('img onclick');
27286 this.fireEvent('click', this, e);
27298 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27301 * @class Roo.bootstrap.dash.NumberBox
27302 * @extends Roo.bootstrap.Component
27303 * Bootstrap NumberBox class
27304 * @cfg {String} headline Box headline
27305 * @cfg {String} content Box content
27306 * @cfg {String} icon Box icon
27307 * @cfg {String} footer Footer text
27308 * @cfg {String} fhref Footer href
27311 * Create a new NumberBox
27312 * @param {Object} config The config object
27316 Roo.bootstrap.dash.NumberBox = function(config){
27317 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27321 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27330 getAutoCreate : function(){
27334 cls : 'small-box ',
27342 cls : 'roo-headline',
27343 html : this.headline
27347 cls : 'roo-content',
27348 html : this.content
27362 cls : 'ion ' + this.icon
27371 cls : 'small-box-footer',
27372 href : this.fhref || '#',
27376 cfg.cn.push(footer);
27383 onRender : function(ct,position){
27384 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27391 setHeadline: function (value)
27393 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27396 setFooter: function (value, href)
27398 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27401 this.el.select('a.small-box-footer',true).first().attr('href', href);
27406 setContent: function (value)
27408 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27411 initEvents: function()
27425 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27428 * @class Roo.bootstrap.dash.TabBox
27429 * @extends Roo.bootstrap.Component
27430 * Bootstrap TabBox class
27431 * @cfg {String} title Title of the TabBox
27432 * @cfg {String} icon Icon of the TabBox
27433 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27434 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27437 * Create a new TabBox
27438 * @param {Object} config The config object
27442 Roo.bootstrap.dash.TabBox = function(config){
27443 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27448 * When a pane is added
27449 * @param {Roo.bootstrap.dash.TabPane} pane
27453 * @event activatepane
27454 * When a pane is activated
27455 * @param {Roo.bootstrap.dash.TabPane} pane
27457 "activatepane" : true
27465 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27470 tabScrollable : false,
27472 getChildContainer : function()
27474 return this.el.select('.tab-content', true).first();
27477 getAutoCreate : function(){
27481 cls: 'pull-left header',
27489 cls: 'fa ' + this.icon
27495 cls: 'nav nav-tabs pull-right',
27501 if(this.tabScrollable){
27508 cls: 'nav nav-tabs pull-right',
27519 cls: 'nav-tabs-custom',
27524 cls: 'tab-content no-padding',
27532 initEvents : function()
27534 //Roo.log('add add pane handler');
27535 this.on('addpane', this.onAddPane, this);
27538 * Updates the box title
27539 * @param {String} html to set the title to.
27541 setTitle : function(value)
27543 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27545 onAddPane : function(pane)
27547 this.panes.push(pane);
27548 //Roo.log('addpane');
27550 // tabs are rendere left to right..
27551 if(!this.showtabs){
27555 var ctr = this.el.select('.nav-tabs', true).first();
27558 var existing = ctr.select('.nav-tab',true);
27559 var qty = existing.getCount();;
27562 var tab = ctr.createChild({
27564 cls : 'nav-tab' + (qty ? '' : ' active'),
27572 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27575 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27577 pane.el.addClass('active');
27582 onTabClick : function(ev,un,ob,pane)
27584 //Roo.log('tab - prev default');
27585 ev.preventDefault();
27588 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27589 pane.tab.addClass('active');
27590 //Roo.log(pane.title);
27591 this.getChildContainer().select('.tab-pane',true).removeClass('active');
27592 // technically we should have a deactivate event.. but maybe add later.
27593 // and it should not de-activate the selected tab...
27594 this.fireEvent('activatepane', pane);
27595 pane.el.addClass('active');
27596 pane.fireEvent('activate');
27601 getActivePane : function()
27604 Roo.each(this.panes, function(p) {
27605 if(p.el.hasClass('active')){
27626 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27628 * @class Roo.bootstrap.TabPane
27629 * @extends Roo.bootstrap.Component
27630 * Bootstrap TabPane class
27631 * @cfg {Boolean} active (false | true) Default false
27632 * @cfg {String} title title of panel
27636 * Create a new TabPane
27637 * @param {Object} config The config object
27640 Roo.bootstrap.dash.TabPane = function(config){
27641 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27647 * When a pane is activated
27648 * @param {Roo.bootstrap.dash.TabPane} pane
27655 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27660 // the tabBox that this is attached to.
27663 getAutoCreate : function()
27671 cfg.cls += ' active';
27676 initEvents : function()
27678 //Roo.log('trigger add pane handler');
27679 this.parent().fireEvent('addpane', this)
27683 * Updates the tab title
27684 * @param {String} html to set the title to.
27686 setTitle: function(str)
27692 this.tab.select('a', true).first().dom.innerHTML = str;
27709 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27712 * @class Roo.bootstrap.menu.Menu
27713 * @extends Roo.bootstrap.Component
27714 * Bootstrap Menu class - container for Menu
27715 * @cfg {String} html Text of the menu
27716 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27717 * @cfg {String} icon Font awesome icon
27718 * @cfg {String} pos Menu align to (top | bottom) default bottom
27722 * Create a new Menu
27723 * @param {Object} config The config object
27727 Roo.bootstrap.menu.Menu = function(config){
27728 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27732 * @event beforeshow
27733 * Fires before this menu is displayed
27734 * @param {Roo.bootstrap.menu.Menu} this
27738 * @event beforehide
27739 * Fires before this menu is hidden
27740 * @param {Roo.bootstrap.menu.Menu} this
27745 * Fires after this menu is displayed
27746 * @param {Roo.bootstrap.menu.Menu} this
27751 * Fires after this menu is hidden
27752 * @param {Roo.bootstrap.menu.Menu} this
27757 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27758 * @param {Roo.bootstrap.menu.Menu} this
27759 * @param {Roo.EventObject} e
27766 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
27770 weight : 'default',
27775 getChildContainer : function() {
27776 if(this.isSubMenu){
27780 return this.el.select('ul.dropdown-menu', true).first();
27783 getAutoCreate : function()
27788 cls : 'roo-menu-text',
27796 cls : 'fa ' + this.icon
27807 cls : 'dropdown-button btn btn-' + this.weight,
27812 cls : 'dropdown-toggle btn btn-' + this.weight,
27822 cls : 'dropdown-menu'
27828 if(this.pos == 'top'){
27829 cfg.cls += ' dropup';
27832 if(this.isSubMenu){
27835 cls : 'dropdown-menu'
27842 onRender : function(ct, position)
27844 this.isSubMenu = ct.hasClass('dropdown-submenu');
27846 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27849 initEvents : function()
27851 if(this.isSubMenu){
27855 this.hidden = true;
27857 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27858 this.triggerEl.on('click', this.onTriggerPress, this);
27860 this.buttonEl = this.el.select('button.dropdown-button', true).first();
27861 this.buttonEl.on('click', this.onClick, this);
27867 if(this.isSubMenu){
27871 return this.el.select('ul.dropdown-menu', true).first();
27874 onClick : function(e)
27876 this.fireEvent("click", this, e);
27879 onTriggerPress : function(e)
27881 if (this.isVisible()) {
27888 isVisible : function(){
27889 return !this.hidden;
27894 this.fireEvent("beforeshow", this);
27896 this.hidden = false;
27897 this.el.addClass('open');
27899 Roo.get(document).on("mouseup", this.onMouseUp, this);
27901 this.fireEvent("show", this);
27908 this.fireEvent("beforehide", this);
27910 this.hidden = true;
27911 this.el.removeClass('open');
27913 Roo.get(document).un("mouseup", this.onMouseUp);
27915 this.fireEvent("hide", this);
27918 onMouseUp : function()
27932 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27935 * @class Roo.bootstrap.menu.Item
27936 * @extends Roo.bootstrap.Component
27937 * Bootstrap MenuItem class
27938 * @cfg {Boolean} submenu (true | false) default false
27939 * @cfg {String} html text of the item
27940 * @cfg {String} href the link
27941 * @cfg {Boolean} disable (true | false) default false
27942 * @cfg {Boolean} preventDefault (true | false) default true
27943 * @cfg {String} icon Font awesome icon
27944 * @cfg {String} pos Submenu align to (left | right) default right
27948 * Create a new Item
27949 * @param {Object} config The config object
27953 Roo.bootstrap.menu.Item = function(config){
27954 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27958 * Fires when the mouse is hovering over this menu
27959 * @param {Roo.bootstrap.menu.Item} this
27960 * @param {Roo.EventObject} e
27965 * Fires when the mouse exits this menu
27966 * @param {Roo.bootstrap.menu.Item} this
27967 * @param {Roo.EventObject} e
27973 * The raw click event for the entire grid.
27974 * @param {Roo.EventObject} e
27980 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
27985 preventDefault: true,
27990 getAutoCreate : function()
27995 cls : 'roo-menu-item-text',
28003 cls : 'fa ' + this.icon
28012 href : this.href || '#',
28019 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28023 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28025 if(this.pos == 'left'){
28026 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28033 initEvents : function()
28035 this.el.on('mouseover', this.onMouseOver, this);
28036 this.el.on('mouseout', this.onMouseOut, this);
28038 this.el.select('a', true).first().on('click', this.onClick, this);
28042 onClick : function(e)
28044 if(this.preventDefault){
28045 e.preventDefault();
28048 this.fireEvent("click", this, e);
28051 onMouseOver : function(e)
28053 if(this.submenu && this.pos == 'left'){
28054 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28057 this.fireEvent("mouseover", this, e);
28060 onMouseOut : function(e)
28062 this.fireEvent("mouseout", this, e);
28074 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28077 * @class Roo.bootstrap.menu.Separator
28078 * @extends Roo.bootstrap.Component
28079 * Bootstrap Separator class
28082 * Create a new Separator
28083 * @param {Object} config The config object
28087 Roo.bootstrap.menu.Separator = function(config){
28088 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28091 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28093 getAutoCreate : function(){
28114 * @class Roo.bootstrap.Tooltip
28115 * Bootstrap Tooltip class
28116 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28117 * to determine which dom element triggers the tooltip.
28119 * It needs to add support for additional attributes like tooltip-position
28122 * Create a new Toolti
28123 * @param {Object} config The config object
28126 Roo.bootstrap.Tooltip = function(config){
28127 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28129 this.alignment = Roo.bootstrap.Tooltip.alignment;
28131 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28132 this.alignment = config.alignment;
28137 Roo.apply(Roo.bootstrap.Tooltip, {
28139 * @function init initialize tooltip monitoring.
28143 currentTip : false,
28144 currentRegion : false,
28150 Roo.get(document).on('mouseover', this.enter ,this);
28151 Roo.get(document).on('mouseout', this.leave, this);
28154 this.currentTip = new Roo.bootstrap.Tooltip();
28157 enter : function(ev)
28159 var dom = ev.getTarget();
28161 //Roo.log(['enter',dom]);
28162 var el = Roo.fly(dom);
28163 if (this.currentEl) {
28165 //Roo.log(this.currentEl);
28166 //Roo.log(this.currentEl.contains(dom));
28167 if (this.currentEl == el) {
28170 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28176 if (this.currentTip.el) {
28177 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28181 if(!el || el.dom == document){
28187 // you can not look for children, as if el is the body.. then everythign is the child..
28188 if (!el.attr('tooltip')) { //
28189 if (!el.select("[tooltip]").elements.length) {
28192 // is the mouse over this child...?
28193 bindEl = el.select("[tooltip]").first();
28194 var xy = ev.getXY();
28195 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28196 //Roo.log("not in region.");
28199 //Roo.log("child element over..");
28202 this.currentEl = bindEl;
28203 this.currentTip.bind(bindEl);
28204 this.currentRegion = Roo.lib.Region.getRegion(dom);
28205 this.currentTip.enter();
28208 leave : function(ev)
28210 var dom = ev.getTarget();
28211 //Roo.log(['leave',dom]);
28212 if (!this.currentEl) {
28217 if (dom != this.currentEl.dom) {
28220 var xy = ev.getXY();
28221 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28224 // only activate leave if mouse cursor is outside... bounding box..
28229 if (this.currentTip) {
28230 this.currentTip.leave();
28232 //Roo.log('clear currentEl');
28233 this.currentEl = false;
28238 'left' : ['r-l', [-2,0], 'right'],
28239 'right' : ['l-r', [2,0], 'left'],
28240 'bottom' : ['t-b', [0,2], 'top'],
28241 'top' : [ 'b-t', [0,-2], 'bottom']
28247 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28252 delay : null, // can be { show : 300 , hide: 500}
28256 hoverState : null, //???
28258 placement : 'bottom',
28262 getAutoCreate : function(){
28269 cls : 'tooltip-arrow'
28272 cls : 'tooltip-inner'
28279 bind : function(el)
28285 enter : function () {
28287 if (this.timeout != null) {
28288 clearTimeout(this.timeout);
28291 this.hoverState = 'in';
28292 //Roo.log("enter - show");
28293 if (!this.delay || !this.delay.show) {
28298 this.timeout = setTimeout(function () {
28299 if (_t.hoverState == 'in') {
28302 }, this.delay.show);
28306 clearTimeout(this.timeout);
28308 this.hoverState = 'out';
28309 if (!this.delay || !this.delay.hide) {
28315 this.timeout = setTimeout(function () {
28316 //Roo.log("leave - timeout");
28318 if (_t.hoverState == 'out') {
28320 Roo.bootstrap.Tooltip.currentEl = false;
28325 show : function (msg)
28328 this.render(document.body);
28331 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28333 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28335 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28337 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
28339 var placement = typeof this.placement == 'function' ?
28340 this.placement.call(this, this.el, on_el) :
28343 var autoToken = /\s?auto?\s?/i;
28344 var autoPlace = autoToken.test(placement);
28346 placement = placement.replace(autoToken, '') || 'top';
28350 //this.el.setXY([0,0]);
28352 //this.el.dom.style.display='block';
28354 //this.el.appendTo(on_el);
28356 var p = this.getPosition();
28357 var box = this.el.getBox();
28363 var align = this.alignment[placement];
28365 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28367 if(placement == 'top' || placement == 'bottom'){
28369 placement = 'right';
28372 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28373 placement = 'left';
28376 var scroll = Roo.select('body', true).first().getScroll();
28378 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28382 align = this.alignment[placement];
28385 this.el.alignTo(this.bindEl, align[0],align[1]);
28386 //var arrow = this.el.select('.arrow',true).first();
28387 //arrow.set(align[2],
28389 this.el.addClass(placement);
28391 this.el.addClass('in fade');
28393 this.hoverState = null;
28395 if (this.el.hasClass('fade')) {
28406 //this.el.setXY([0,0]);
28407 this.el.removeClass('in');
28423 * @class Roo.bootstrap.LocationPicker
28424 * @extends Roo.bootstrap.Component
28425 * Bootstrap LocationPicker class
28426 * @cfg {Number} latitude Position when init default 0
28427 * @cfg {Number} longitude Position when init default 0
28428 * @cfg {Number} zoom default 15
28429 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28430 * @cfg {Boolean} mapTypeControl default false
28431 * @cfg {Boolean} disableDoubleClickZoom default false
28432 * @cfg {Boolean} scrollwheel default true
28433 * @cfg {Boolean} streetViewControl default false
28434 * @cfg {Number} radius default 0
28435 * @cfg {String} locationName
28436 * @cfg {Boolean} draggable default true
28437 * @cfg {Boolean} enableAutocomplete default false
28438 * @cfg {Boolean} enableReverseGeocode default true
28439 * @cfg {String} markerTitle
28442 * Create a new LocationPicker
28443 * @param {Object} config The config object
28447 Roo.bootstrap.LocationPicker = function(config){
28449 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28454 * Fires when the picker initialized.
28455 * @param {Roo.bootstrap.LocationPicker} this
28456 * @param {Google Location} location
28460 * @event positionchanged
28461 * Fires when the picker position changed.
28462 * @param {Roo.bootstrap.LocationPicker} this
28463 * @param {Google Location} location
28465 positionchanged : true,
28468 * Fires when the map resize.
28469 * @param {Roo.bootstrap.LocationPicker} this
28474 * Fires when the map show.
28475 * @param {Roo.bootstrap.LocationPicker} this
28480 * Fires when the map hide.
28481 * @param {Roo.bootstrap.LocationPicker} this
28486 * Fires when click the map.
28487 * @param {Roo.bootstrap.LocationPicker} this
28488 * @param {Map event} e
28492 * @event mapRightClick
28493 * Fires when right click the map.
28494 * @param {Roo.bootstrap.LocationPicker} this
28495 * @param {Map event} e
28497 mapRightClick : true,
28499 * @event markerClick
28500 * Fires when click the marker.
28501 * @param {Roo.bootstrap.LocationPicker} this
28502 * @param {Map event} e
28504 markerClick : true,
28506 * @event markerRightClick
28507 * Fires when right click the marker.
28508 * @param {Roo.bootstrap.LocationPicker} this
28509 * @param {Map event} e
28511 markerRightClick : true,
28513 * @event OverlayViewDraw
28514 * Fires when OverlayView Draw
28515 * @param {Roo.bootstrap.LocationPicker} this
28517 OverlayViewDraw : true,
28519 * @event OverlayViewOnAdd
28520 * Fires when OverlayView Draw
28521 * @param {Roo.bootstrap.LocationPicker} this
28523 OverlayViewOnAdd : true,
28525 * @event OverlayViewOnRemove
28526 * Fires when OverlayView Draw
28527 * @param {Roo.bootstrap.LocationPicker} this
28529 OverlayViewOnRemove : true,
28531 * @event OverlayViewShow
28532 * Fires when OverlayView Draw
28533 * @param {Roo.bootstrap.LocationPicker} this
28534 * @param {Pixel} cpx
28536 OverlayViewShow : true,
28538 * @event OverlayViewHide
28539 * Fires when OverlayView Draw
28540 * @param {Roo.bootstrap.LocationPicker} this
28542 OverlayViewHide : true,
28544 * @event loadexception
28545 * Fires when load google lib failed.
28546 * @param {Roo.bootstrap.LocationPicker} this
28548 loadexception : true
28553 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
28555 gMapContext: false,
28561 mapTypeControl: false,
28562 disableDoubleClickZoom: false,
28564 streetViewControl: false,
28568 enableAutocomplete: false,
28569 enableReverseGeocode: true,
28572 getAutoCreate: function()
28577 cls: 'roo-location-picker'
28583 initEvents: function(ct, position)
28585 if(!this.el.getWidth() || this.isApplied()){
28589 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28594 initial: function()
28596 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28597 this.fireEvent('loadexception', this);
28601 if(!this.mapTypeId){
28602 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28605 this.gMapContext = this.GMapContext();
28607 this.initOverlayView();
28609 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28613 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28614 _this.setPosition(_this.gMapContext.marker.position);
28617 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28618 _this.fireEvent('mapClick', this, event);
28622 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28623 _this.fireEvent('mapRightClick', this, event);
28627 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28628 _this.fireEvent('markerClick', this, event);
28632 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28633 _this.fireEvent('markerRightClick', this, event);
28637 this.setPosition(this.gMapContext.location);
28639 this.fireEvent('initial', this, this.gMapContext.location);
28642 initOverlayView: function()
28646 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28650 _this.fireEvent('OverlayViewDraw', _this);
28655 _this.fireEvent('OverlayViewOnAdd', _this);
28658 onRemove: function()
28660 _this.fireEvent('OverlayViewOnRemove', _this);
28663 show: function(cpx)
28665 _this.fireEvent('OverlayViewShow', _this, cpx);
28670 _this.fireEvent('OverlayViewHide', _this);
28676 fromLatLngToContainerPixel: function(event)
28678 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28681 isApplied: function()
28683 return this.getGmapContext() == false ? false : true;
28686 getGmapContext: function()
28688 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28691 GMapContext: function()
28693 var position = new google.maps.LatLng(this.latitude, this.longitude);
28695 var _map = new google.maps.Map(this.el.dom, {
28698 mapTypeId: this.mapTypeId,
28699 mapTypeControl: this.mapTypeControl,
28700 disableDoubleClickZoom: this.disableDoubleClickZoom,
28701 scrollwheel: this.scrollwheel,
28702 streetViewControl: this.streetViewControl,
28703 locationName: this.locationName,
28704 draggable: this.draggable,
28705 enableAutocomplete: this.enableAutocomplete,
28706 enableReverseGeocode: this.enableReverseGeocode
28709 var _marker = new google.maps.Marker({
28710 position: position,
28712 title: this.markerTitle,
28713 draggable: this.draggable
28720 location: position,
28721 radius: this.radius,
28722 locationName: this.locationName,
28723 addressComponents: {
28724 formatted_address: null,
28725 addressLine1: null,
28726 addressLine2: null,
28728 streetNumber: null,
28732 stateOrProvince: null
28735 domContainer: this.el.dom,
28736 geodecoder: new google.maps.Geocoder()
28740 drawCircle: function(center, radius, options)
28742 if (this.gMapContext.circle != null) {
28743 this.gMapContext.circle.setMap(null);
28747 options = Roo.apply({}, options, {
28748 strokeColor: "#0000FF",
28749 strokeOpacity: .35,
28751 fillColor: "#0000FF",
28755 options.map = this.gMapContext.map;
28756 options.radius = radius;
28757 options.center = center;
28758 this.gMapContext.circle = new google.maps.Circle(options);
28759 return this.gMapContext.circle;
28765 setPosition: function(location)
28767 this.gMapContext.location = location;
28768 this.gMapContext.marker.setPosition(location);
28769 this.gMapContext.map.panTo(location);
28770 this.drawCircle(location, this.gMapContext.radius, {});
28774 if (this.gMapContext.settings.enableReverseGeocode) {
28775 this.gMapContext.geodecoder.geocode({
28776 latLng: this.gMapContext.location
28777 }, function(results, status) {
28779 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28780 _this.gMapContext.locationName = results[0].formatted_address;
28781 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28783 _this.fireEvent('positionchanged', this, location);
28790 this.fireEvent('positionchanged', this, location);
28795 google.maps.event.trigger(this.gMapContext.map, "resize");
28797 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28799 this.fireEvent('resize', this);
28802 setPositionByLatLng: function(latitude, longitude)
28804 this.setPosition(new google.maps.LatLng(latitude, longitude));
28807 getCurrentPosition: function()
28810 latitude: this.gMapContext.location.lat(),
28811 longitude: this.gMapContext.location.lng()
28815 getAddressName: function()
28817 return this.gMapContext.locationName;
28820 getAddressComponents: function()
28822 return this.gMapContext.addressComponents;
28825 address_component_from_google_geocode: function(address_components)
28829 for (var i = 0; i < address_components.length; i++) {
28830 var component = address_components[i];
28831 if (component.types.indexOf("postal_code") >= 0) {
28832 result.postalCode = component.short_name;
28833 } else if (component.types.indexOf("street_number") >= 0) {
28834 result.streetNumber = component.short_name;
28835 } else if (component.types.indexOf("route") >= 0) {
28836 result.streetName = component.short_name;
28837 } else if (component.types.indexOf("neighborhood") >= 0) {
28838 result.city = component.short_name;
28839 } else if (component.types.indexOf("locality") >= 0) {
28840 result.city = component.short_name;
28841 } else if (component.types.indexOf("sublocality") >= 0) {
28842 result.district = component.short_name;
28843 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28844 result.stateOrProvince = component.short_name;
28845 } else if (component.types.indexOf("country") >= 0) {
28846 result.country = component.short_name;
28850 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28851 result.addressLine2 = "";
28855 setZoomLevel: function(zoom)
28857 this.gMapContext.map.setZoom(zoom);
28870 this.fireEvent('show', this);
28881 this.fireEvent('hide', this);
28886 Roo.apply(Roo.bootstrap.LocationPicker, {
28888 OverlayView : function(map, options)
28890 options = options || {};
28897 * @class Roo.bootstrap.Alert
28898 * @extends Roo.bootstrap.Component
28899 * Bootstrap Alert class - shows an alert area box
28901 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28902 Enter a valid email address
28905 * @cfg {String} title The title of alert
28906 * @cfg {String} html The content of alert
28907 * @cfg {String} weight ( success | info | warning | danger )
28908 * @cfg {String} faicon font-awesomeicon
28911 * Create a new alert
28912 * @param {Object} config The config object
28916 Roo.bootstrap.Alert = function(config){
28917 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28921 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
28928 getAutoCreate : function()
28937 cls : 'roo-alert-icon'
28942 cls : 'roo-alert-title',
28947 cls : 'roo-alert-text',
28954 cfg.cn[0].cls += ' fa ' + this.faicon;
28958 cfg.cls += ' alert-' + this.weight;
28964 initEvents: function()
28966 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28969 setTitle : function(str)
28971 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28974 setText : function(str)
28976 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28979 setWeight : function(weight)
28982 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28985 this.weight = weight;
28987 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28990 setIcon : function(icon)
28993 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28996 this.faicon = icon;
28998 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29019 * @class Roo.bootstrap.UploadCropbox
29020 * @extends Roo.bootstrap.Component
29021 * Bootstrap UploadCropbox class
29022 * @cfg {String} emptyText show when image has been loaded
29023 * @cfg {String} rotateNotify show when image too small to rotate
29024 * @cfg {Number} errorTimeout default 3000
29025 * @cfg {Number} minWidth default 300
29026 * @cfg {Number} minHeight default 300
29027 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29028 * @cfg {Boolean} isDocument (true|false) default false
29029 * @cfg {String} url action url
29030 * @cfg {String} paramName default 'imageUpload'
29031 * @cfg {String} method default POST
29032 * @cfg {Boolean} loadMask (true|false) default true
29033 * @cfg {Boolean} loadingText default 'Loading...'
29036 * Create a new UploadCropbox
29037 * @param {Object} config The config object
29040 Roo.bootstrap.UploadCropbox = function(config){
29041 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29045 * @event beforeselectfile
29046 * Fire before select file
29047 * @param {Roo.bootstrap.UploadCropbox} this
29049 "beforeselectfile" : true,
29052 * Fire after initEvent
29053 * @param {Roo.bootstrap.UploadCropbox} this
29058 * Fire after initEvent
29059 * @param {Roo.bootstrap.UploadCropbox} this
29060 * @param {String} data
29065 * Fire when preparing the file data
29066 * @param {Roo.bootstrap.UploadCropbox} this
29067 * @param {Object} file
29072 * Fire when get exception
29073 * @param {Roo.bootstrap.UploadCropbox} this
29074 * @param {XMLHttpRequest} xhr
29076 "exception" : true,
29078 * @event beforeloadcanvas
29079 * Fire before load the canvas
29080 * @param {Roo.bootstrap.UploadCropbox} this
29081 * @param {String} src
29083 "beforeloadcanvas" : true,
29086 * Fire when trash image
29087 * @param {Roo.bootstrap.UploadCropbox} this
29092 * Fire when download the image
29093 * @param {Roo.bootstrap.UploadCropbox} this
29097 * @event footerbuttonclick
29098 * Fire when footerbuttonclick
29099 * @param {Roo.bootstrap.UploadCropbox} this
29100 * @param {String} type
29102 "footerbuttonclick" : true,
29106 * @param {Roo.bootstrap.UploadCropbox} this
29111 * Fire when rotate the image
29112 * @param {Roo.bootstrap.UploadCropbox} this
29113 * @param {String} pos
29118 * Fire when inspect the file
29119 * @param {Roo.bootstrap.UploadCropbox} this
29120 * @param {Object} file
29125 * Fire when xhr upload the file
29126 * @param {Roo.bootstrap.UploadCropbox} this
29127 * @param {Object} data
29132 * Fire when arrange the file data
29133 * @param {Roo.bootstrap.UploadCropbox} this
29134 * @param {Object} formData
29139 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29142 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29144 emptyText : 'Click to upload image',
29145 rotateNotify : 'Image is too small to rotate',
29146 errorTimeout : 3000,
29160 cropType : 'image/jpeg',
29162 canvasLoaded : false,
29163 isDocument : false,
29165 paramName : 'imageUpload',
29167 loadingText : 'Loading...',
29170 getAutoCreate : function()
29174 cls : 'roo-upload-cropbox',
29178 cls : 'roo-upload-cropbox-selector',
29183 cls : 'roo-upload-cropbox-body',
29184 style : 'cursor:pointer',
29188 cls : 'roo-upload-cropbox-preview'
29192 cls : 'roo-upload-cropbox-thumb'
29196 cls : 'roo-upload-cropbox-empty-notify',
29197 html : this.emptyText
29201 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29202 html : this.rotateNotify
29208 cls : 'roo-upload-cropbox-footer',
29211 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29221 onRender : function(ct, position)
29223 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29225 if (this.buttons.length) {
29227 Roo.each(this.buttons, function(bb) {
29229 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29231 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29237 this.maskEl = this.el;
29241 initEvents : function()
29243 this.urlAPI = (window.createObjectURL && window) ||
29244 (window.URL && URL.revokeObjectURL && URL) ||
29245 (window.webkitURL && webkitURL);
29247 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29248 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29250 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29251 this.selectorEl.hide();
29253 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29254 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29256 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29257 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29258 this.thumbEl.hide();
29260 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29261 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29263 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29264 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29265 this.errorEl.hide();
29267 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29268 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29269 this.footerEl.hide();
29271 this.setThumbBoxSize();
29277 this.fireEvent('initial', this);
29284 window.addEventListener("resize", function() { _this.resize(); } );
29286 this.bodyEl.on('click', this.beforeSelectFile, this);
29289 this.bodyEl.on('touchstart', this.onTouchStart, this);
29290 this.bodyEl.on('touchmove', this.onTouchMove, this);
29291 this.bodyEl.on('touchend', this.onTouchEnd, this);
29295 this.bodyEl.on('mousedown', this.onMouseDown, this);
29296 this.bodyEl.on('mousemove', this.onMouseMove, this);
29297 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29298 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29299 Roo.get(document).on('mouseup', this.onMouseUp, this);
29302 this.selectorEl.on('change', this.onFileSelected, this);
29308 this.baseScale = 1;
29310 this.baseRotate = 1;
29311 this.dragable = false;
29312 this.pinching = false;
29315 this.cropData = false;
29316 this.notifyEl.dom.innerHTML = this.emptyText;
29318 this.selectorEl.dom.value = '';
29322 resize : function()
29324 if(this.fireEvent('resize', this) != false){
29325 this.setThumbBoxPosition();
29326 this.setCanvasPosition();
29330 onFooterButtonClick : function(e, el, o, type)
29333 case 'rotate-left' :
29334 this.onRotateLeft(e);
29336 case 'rotate-right' :
29337 this.onRotateRight(e);
29340 this.beforeSelectFile(e);
29355 this.fireEvent('footerbuttonclick', this, type);
29358 beforeSelectFile : function(e)
29360 e.preventDefault();
29362 if(this.fireEvent('beforeselectfile', this) != false){
29363 this.selectorEl.dom.click();
29367 onFileSelected : function(e)
29369 e.preventDefault();
29371 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29375 var file = this.selectorEl.dom.files[0];
29377 if(this.fireEvent('inspect', this, file) != false){
29378 this.prepare(file);
29383 trash : function(e)
29385 this.fireEvent('trash', this);
29388 download : function(e)
29390 this.fireEvent('download', this);
29393 loadCanvas : function(src)
29395 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29399 this.imageEl = document.createElement('img');
29403 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29405 this.imageEl.src = src;
29409 onLoadCanvas : function()
29411 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29412 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29414 this.bodyEl.un('click', this.beforeSelectFile, this);
29416 this.notifyEl.hide();
29417 this.thumbEl.show();
29418 this.footerEl.show();
29420 this.baseRotateLevel();
29422 if(this.isDocument){
29423 this.setThumbBoxSize();
29426 this.setThumbBoxPosition();
29428 this.baseScaleLevel();
29434 this.canvasLoaded = true;
29437 this.maskEl.unmask();
29442 setCanvasPosition : function()
29444 if(!this.canvasEl){
29448 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29449 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29451 this.previewEl.setLeft(pw);
29452 this.previewEl.setTop(ph);
29456 onMouseDown : function(e)
29460 this.dragable = true;
29461 this.pinching = false;
29463 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29464 this.dragable = false;
29468 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29469 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29473 onMouseMove : function(e)
29477 if(!this.canvasLoaded){
29481 if (!this.dragable){
29485 var minX = Math.ceil(this.thumbEl.getLeft(true));
29486 var minY = Math.ceil(this.thumbEl.getTop(true));
29488 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29489 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29491 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29492 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29494 x = x - this.mouseX;
29495 y = y - this.mouseY;
29497 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29498 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29500 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29501 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29503 this.previewEl.setLeft(bgX);
29504 this.previewEl.setTop(bgY);
29506 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29507 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29510 onMouseUp : function(e)
29514 this.dragable = false;
29517 onMouseWheel : function(e)
29521 this.startScale = this.scale;
29523 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29525 if(!this.zoomable()){
29526 this.scale = this.startScale;
29535 zoomable : function()
29537 var minScale = this.thumbEl.getWidth() / this.minWidth;
29539 if(this.minWidth < this.minHeight){
29540 minScale = this.thumbEl.getHeight() / this.minHeight;
29543 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29544 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29548 (this.rotate == 0 || this.rotate == 180) &&
29550 width > this.imageEl.OriginWidth ||
29551 height > this.imageEl.OriginHeight ||
29552 (width < this.minWidth && height < this.minHeight)
29560 (this.rotate == 90 || this.rotate == 270) &&
29562 width > this.imageEl.OriginWidth ||
29563 height > this.imageEl.OriginHeight ||
29564 (width < this.minHeight && height < this.minWidth)
29571 !this.isDocument &&
29572 (this.rotate == 0 || this.rotate == 180) &&
29574 width < this.minWidth ||
29575 width > this.imageEl.OriginWidth ||
29576 height < this.minHeight ||
29577 height > this.imageEl.OriginHeight
29584 !this.isDocument &&
29585 (this.rotate == 90 || this.rotate == 270) &&
29587 width < this.minHeight ||
29588 width > this.imageEl.OriginWidth ||
29589 height < this.minWidth ||
29590 height > this.imageEl.OriginHeight
29600 onRotateLeft : function(e)
29602 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29604 var minScale = this.thumbEl.getWidth() / this.minWidth;
29606 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29607 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29609 this.startScale = this.scale;
29611 while (this.getScaleLevel() < minScale){
29613 this.scale = this.scale + 1;
29615 if(!this.zoomable()){
29620 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29621 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29626 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29633 this.scale = this.startScale;
29635 this.onRotateFail();
29640 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29642 if(this.isDocument){
29643 this.setThumbBoxSize();
29644 this.setThumbBoxPosition();
29645 this.setCanvasPosition();
29650 this.fireEvent('rotate', this, 'left');
29654 onRotateRight : function(e)
29656 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29658 var minScale = this.thumbEl.getWidth() / this.minWidth;
29660 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29661 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29663 this.startScale = this.scale;
29665 while (this.getScaleLevel() < minScale){
29667 this.scale = this.scale + 1;
29669 if(!this.zoomable()){
29674 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29675 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29680 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29687 this.scale = this.startScale;
29689 this.onRotateFail();
29694 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29696 if(this.isDocument){
29697 this.setThumbBoxSize();
29698 this.setThumbBoxPosition();
29699 this.setCanvasPosition();
29704 this.fireEvent('rotate', this, 'right');
29707 onRotateFail : function()
29709 this.errorEl.show(true);
29713 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29718 this.previewEl.dom.innerHTML = '';
29720 var canvasEl = document.createElement("canvas");
29722 var contextEl = canvasEl.getContext("2d");
29724 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29725 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29726 var center = this.imageEl.OriginWidth / 2;
29728 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29729 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29730 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29731 center = this.imageEl.OriginHeight / 2;
29734 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29736 contextEl.translate(center, center);
29737 contextEl.rotate(this.rotate * Math.PI / 180);
29739 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29741 this.canvasEl = document.createElement("canvas");
29743 this.contextEl = this.canvasEl.getContext("2d");
29745 switch (this.rotate) {
29748 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29749 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29751 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29756 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29757 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29759 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29760 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);
29764 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29769 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29770 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29772 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29773 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);
29777 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);
29782 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29783 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29785 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29786 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29790 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);
29797 this.previewEl.appendChild(this.canvasEl);
29799 this.setCanvasPosition();
29804 if(!this.canvasLoaded){
29808 var imageCanvas = document.createElement("canvas");
29810 var imageContext = imageCanvas.getContext("2d");
29812 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29813 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29815 var center = imageCanvas.width / 2;
29817 imageContext.translate(center, center);
29819 imageContext.rotate(this.rotate * Math.PI / 180);
29821 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29823 var canvas = document.createElement("canvas");
29825 var context = canvas.getContext("2d");
29827 canvas.width = this.minWidth;
29828 canvas.height = this.minHeight;
29830 switch (this.rotate) {
29833 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29834 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29836 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29837 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29839 var targetWidth = this.minWidth - 2 * x;
29840 var targetHeight = this.minHeight - 2 * y;
29844 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29845 scale = targetWidth / width;
29848 if(x > 0 && y == 0){
29849 scale = targetHeight / height;
29852 if(x > 0 && y > 0){
29853 scale = targetWidth / width;
29855 if(width < height){
29856 scale = targetHeight / height;
29860 context.scale(scale, scale);
29862 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29863 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29865 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29866 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29868 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29873 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29874 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29876 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29877 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29879 var targetWidth = this.minWidth - 2 * x;
29880 var targetHeight = this.minHeight - 2 * y;
29884 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29885 scale = targetWidth / width;
29888 if(x > 0 && y == 0){
29889 scale = targetHeight / height;
29892 if(x > 0 && y > 0){
29893 scale = targetWidth / width;
29895 if(width < height){
29896 scale = targetHeight / height;
29900 context.scale(scale, scale);
29902 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29903 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29905 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29906 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29908 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29910 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29915 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29916 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29918 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29919 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29921 var targetWidth = this.minWidth - 2 * x;
29922 var targetHeight = this.minHeight - 2 * y;
29926 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29927 scale = targetWidth / width;
29930 if(x > 0 && y == 0){
29931 scale = targetHeight / height;
29934 if(x > 0 && y > 0){
29935 scale = targetWidth / width;
29937 if(width < height){
29938 scale = targetHeight / height;
29942 context.scale(scale, scale);
29944 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29945 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29947 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29948 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29950 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29951 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29953 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29958 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29959 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29961 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29962 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29964 var targetWidth = this.minWidth - 2 * x;
29965 var targetHeight = this.minHeight - 2 * y;
29969 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29970 scale = targetWidth / width;
29973 if(x > 0 && y == 0){
29974 scale = targetHeight / height;
29977 if(x > 0 && y > 0){
29978 scale = targetWidth / width;
29980 if(width < height){
29981 scale = targetHeight / height;
29985 context.scale(scale, scale);
29987 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29988 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29990 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29991 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29993 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29995 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30002 this.cropData = canvas.toDataURL(this.cropType);
30004 if(this.fireEvent('crop', this, this.cropData) !== false){
30005 this.process(this.file, this.cropData);
30012 setThumbBoxSize : function()
30016 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30017 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30018 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30020 this.minWidth = width;
30021 this.minHeight = height;
30023 if(this.rotate == 90 || this.rotate == 270){
30024 this.minWidth = height;
30025 this.minHeight = width;
30030 width = Math.ceil(this.minWidth * height / this.minHeight);
30032 if(this.minWidth > this.minHeight){
30034 height = Math.ceil(this.minHeight * width / this.minWidth);
30037 this.thumbEl.setStyle({
30038 width : width + 'px',
30039 height : height + 'px'
30046 setThumbBoxPosition : function()
30048 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30049 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30051 this.thumbEl.setLeft(x);
30052 this.thumbEl.setTop(y);
30056 baseRotateLevel : function()
30058 this.baseRotate = 1;
30061 typeof(this.exif) != 'undefined' &&
30062 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30063 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30065 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30068 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30072 baseScaleLevel : function()
30076 if(this.isDocument){
30078 if(this.baseRotate == 6 || this.baseRotate == 8){
30080 height = this.thumbEl.getHeight();
30081 this.baseScale = height / this.imageEl.OriginWidth;
30083 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30084 width = this.thumbEl.getWidth();
30085 this.baseScale = width / this.imageEl.OriginHeight;
30091 height = this.thumbEl.getHeight();
30092 this.baseScale = height / this.imageEl.OriginHeight;
30094 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30095 width = this.thumbEl.getWidth();
30096 this.baseScale = width / this.imageEl.OriginWidth;
30102 if(this.baseRotate == 6 || this.baseRotate == 8){
30104 width = this.thumbEl.getHeight();
30105 this.baseScale = width / this.imageEl.OriginHeight;
30107 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30108 height = this.thumbEl.getWidth();
30109 this.baseScale = height / this.imageEl.OriginHeight;
30112 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30113 height = this.thumbEl.getWidth();
30114 this.baseScale = height / this.imageEl.OriginHeight;
30116 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30117 width = this.thumbEl.getHeight();
30118 this.baseScale = width / this.imageEl.OriginWidth;
30125 width = this.thumbEl.getWidth();
30126 this.baseScale = width / this.imageEl.OriginWidth;
30128 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30129 height = this.thumbEl.getHeight();
30130 this.baseScale = height / this.imageEl.OriginHeight;
30133 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30135 height = this.thumbEl.getHeight();
30136 this.baseScale = height / this.imageEl.OriginHeight;
30138 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30139 width = this.thumbEl.getWidth();
30140 this.baseScale = width / this.imageEl.OriginWidth;
30148 getScaleLevel : function()
30150 return this.baseScale * Math.pow(1.1, this.scale);
30153 onTouchStart : function(e)
30155 if(!this.canvasLoaded){
30156 this.beforeSelectFile(e);
30160 var touches = e.browserEvent.touches;
30166 if(touches.length == 1){
30167 this.onMouseDown(e);
30171 if(touches.length != 2){
30177 for(var i = 0, finger; finger = touches[i]; i++){
30178 coords.push(finger.pageX, finger.pageY);
30181 var x = Math.pow(coords[0] - coords[2], 2);
30182 var y = Math.pow(coords[1] - coords[3], 2);
30184 this.startDistance = Math.sqrt(x + y);
30186 this.startScale = this.scale;
30188 this.pinching = true;
30189 this.dragable = false;
30193 onTouchMove : function(e)
30195 if(!this.pinching && !this.dragable){
30199 var touches = e.browserEvent.touches;
30206 this.onMouseMove(e);
30212 for(var i = 0, finger; finger = touches[i]; i++){
30213 coords.push(finger.pageX, finger.pageY);
30216 var x = Math.pow(coords[0] - coords[2], 2);
30217 var y = Math.pow(coords[1] - coords[3], 2);
30219 this.endDistance = Math.sqrt(x + y);
30221 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30223 if(!this.zoomable()){
30224 this.scale = this.startScale;
30232 onTouchEnd : function(e)
30234 this.pinching = false;
30235 this.dragable = false;
30239 process : function(file, crop)
30242 this.maskEl.mask(this.loadingText);
30245 this.xhr = new XMLHttpRequest();
30247 file.xhr = this.xhr;
30249 this.xhr.open(this.method, this.url, true);
30252 "Accept": "application/json",
30253 "Cache-Control": "no-cache",
30254 "X-Requested-With": "XMLHttpRequest"
30257 for (var headerName in headers) {
30258 var headerValue = headers[headerName];
30260 this.xhr.setRequestHeader(headerName, headerValue);
30266 this.xhr.onload = function()
30268 _this.xhrOnLoad(_this.xhr);
30271 this.xhr.onerror = function()
30273 _this.xhrOnError(_this.xhr);
30276 var formData = new FormData();
30278 formData.append('returnHTML', 'NO');
30281 formData.append('crop', crop);
30284 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30285 formData.append(this.paramName, file, file.name);
30288 if(typeof(file.filename) != 'undefined'){
30289 formData.append('filename', file.filename);
30292 if(typeof(file.mimetype) != 'undefined'){
30293 formData.append('mimetype', file.mimetype);
30296 if(this.fireEvent('arrange', this, formData) != false){
30297 this.xhr.send(formData);
30301 xhrOnLoad : function(xhr)
30304 this.maskEl.unmask();
30307 if (xhr.readyState !== 4) {
30308 this.fireEvent('exception', this, xhr);
30312 var response = Roo.decode(xhr.responseText);
30314 if(!response.success){
30315 this.fireEvent('exception', this, xhr);
30319 var response = Roo.decode(xhr.responseText);
30321 this.fireEvent('upload', this, response);
30325 xhrOnError : function()
30328 this.maskEl.unmask();
30331 Roo.log('xhr on error');
30333 var response = Roo.decode(xhr.responseText);
30339 prepare : function(file)
30342 this.maskEl.mask(this.loadingText);
30348 if(typeof(file) === 'string'){
30349 this.loadCanvas(file);
30353 if(!file || !this.urlAPI){
30358 this.cropType = file.type;
30362 if(this.fireEvent('prepare', this, this.file) != false){
30364 var reader = new FileReader();
30366 reader.onload = function (e) {
30367 if (e.target.error) {
30368 Roo.log(e.target.error);
30372 var buffer = e.target.result,
30373 dataView = new DataView(buffer),
30375 maxOffset = dataView.byteLength - 4,
30379 if (dataView.getUint16(0) === 0xffd8) {
30380 while (offset < maxOffset) {
30381 markerBytes = dataView.getUint16(offset);
30383 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30384 markerLength = dataView.getUint16(offset + 2) + 2;
30385 if (offset + markerLength > dataView.byteLength) {
30386 Roo.log('Invalid meta data: Invalid segment size.');
30390 if(markerBytes == 0xffe1){
30391 _this.parseExifData(
30398 offset += markerLength;
30408 var url = _this.urlAPI.createObjectURL(_this.file);
30410 _this.loadCanvas(url);
30415 reader.readAsArrayBuffer(this.file);
30421 parseExifData : function(dataView, offset, length)
30423 var tiffOffset = offset + 10,
30427 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30428 // No Exif data, might be XMP data instead
30432 // Check for the ASCII code for "Exif" (0x45786966):
30433 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30434 // No Exif data, might be XMP data instead
30437 if (tiffOffset + 8 > dataView.byteLength) {
30438 Roo.log('Invalid Exif data: Invalid segment size.');
30441 // Check for the two null bytes:
30442 if (dataView.getUint16(offset + 8) !== 0x0000) {
30443 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30446 // Check the byte alignment:
30447 switch (dataView.getUint16(tiffOffset)) {
30449 littleEndian = true;
30452 littleEndian = false;
30455 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30458 // Check for the TIFF tag marker (0x002A):
30459 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30460 Roo.log('Invalid Exif data: Missing TIFF marker.');
30463 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30464 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30466 this.parseExifTags(
30469 tiffOffset + dirOffset,
30474 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30479 if (dirOffset + 6 > dataView.byteLength) {
30480 Roo.log('Invalid Exif data: Invalid directory offset.');
30483 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30484 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30485 if (dirEndOffset + 4 > dataView.byteLength) {
30486 Roo.log('Invalid Exif data: Invalid directory size.');
30489 for (i = 0; i < tagsNumber; i += 1) {
30493 dirOffset + 2 + 12 * i, // tag offset
30497 // Return the offset to the next directory:
30498 return dataView.getUint32(dirEndOffset, littleEndian);
30501 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30503 var tag = dataView.getUint16(offset, littleEndian);
30505 this.exif[tag] = this.getExifValue(
30509 dataView.getUint16(offset + 2, littleEndian), // tag type
30510 dataView.getUint32(offset + 4, littleEndian), // tag length
30515 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30517 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30526 Roo.log('Invalid Exif data: Invalid tag type.');
30530 tagSize = tagType.size * length;
30531 // Determine if the value is contained in the dataOffset bytes,
30532 // or if the value at the dataOffset is a pointer to the actual data:
30533 dataOffset = tagSize > 4 ?
30534 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30535 if (dataOffset + tagSize > dataView.byteLength) {
30536 Roo.log('Invalid Exif data: Invalid data offset.');
30539 if (length === 1) {
30540 return tagType.getValue(dataView, dataOffset, littleEndian);
30543 for (i = 0; i < length; i += 1) {
30544 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30547 if (tagType.ascii) {
30549 // Concatenate the chars:
30550 for (i = 0; i < values.length; i += 1) {
30552 // Ignore the terminating NULL byte(s):
30553 if (c === '\u0000') {
30565 Roo.apply(Roo.bootstrap.UploadCropbox, {
30567 'Orientation': 0x0112
30571 1: 0, //'top-left',
30573 3: 180, //'bottom-right',
30574 // 4: 'bottom-left',
30576 6: 90, //'right-top',
30577 // 7: 'right-bottom',
30578 8: 270 //'left-bottom'
30582 // byte, 8-bit unsigned int:
30584 getValue: function (dataView, dataOffset) {
30585 return dataView.getUint8(dataOffset);
30589 // ascii, 8-bit byte:
30591 getValue: function (dataView, dataOffset) {
30592 return String.fromCharCode(dataView.getUint8(dataOffset));
30597 // short, 16 bit int:
30599 getValue: function (dataView, dataOffset, littleEndian) {
30600 return dataView.getUint16(dataOffset, littleEndian);
30604 // long, 32 bit int:
30606 getValue: function (dataView, dataOffset, littleEndian) {
30607 return dataView.getUint32(dataOffset, littleEndian);
30611 // rational = two long values, first is numerator, second is denominator:
30613 getValue: function (dataView, dataOffset, littleEndian) {
30614 return dataView.getUint32(dataOffset, littleEndian) /
30615 dataView.getUint32(dataOffset + 4, littleEndian);
30619 // slong, 32 bit signed int:
30621 getValue: function (dataView, dataOffset, littleEndian) {
30622 return dataView.getInt32(dataOffset, littleEndian);
30626 // srational, two slongs, first is numerator, second is denominator:
30628 getValue: function (dataView, dataOffset, littleEndian) {
30629 return dataView.getInt32(dataOffset, littleEndian) /
30630 dataView.getInt32(dataOffset + 4, littleEndian);
30640 cls : 'btn-group roo-upload-cropbox-rotate-left',
30641 action : 'rotate-left',
30645 cls : 'btn btn-default',
30646 html : '<i class="fa fa-undo"></i>'
30652 cls : 'btn-group roo-upload-cropbox-picture',
30653 action : 'picture',
30657 cls : 'btn btn-default',
30658 html : '<i class="fa fa-picture-o"></i>'
30664 cls : 'btn-group roo-upload-cropbox-rotate-right',
30665 action : 'rotate-right',
30669 cls : 'btn btn-default',
30670 html : '<i class="fa fa-repeat"></i>'
30678 cls : 'btn-group roo-upload-cropbox-rotate-left',
30679 action : 'rotate-left',
30683 cls : 'btn btn-default',
30684 html : '<i class="fa fa-undo"></i>'
30690 cls : 'btn-group roo-upload-cropbox-download',
30691 action : 'download',
30695 cls : 'btn btn-default',
30696 html : '<i class="fa fa-download"></i>'
30702 cls : 'btn-group roo-upload-cropbox-crop',
30707 cls : 'btn btn-default',
30708 html : '<i class="fa fa-crop"></i>'
30714 cls : 'btn-group roo-upload-cropbox-trash',
30719 cls : 'btn btn-default',
30720 html : '<i class="fa fa-trash"></i>'
30726 cls : 'btn-group roo-upload-cropbox-rotate-right',
30727 action : 'rotate-right',
30731 cls : 'btn btn-default',
30732 html : '<i class="fa fa-repeat"></i>'
30740 cls : 'btn-group roo-upload-cropbox-rotate-left',
30741 action : 'rotate-left',
30745 cls : 'btn btn-default',
30746 html : '<i class="fa fa-undo"></i>'
30752 cls : 'btn-group roo-upload-cropbox-rotate-right',
30753 action : 'rotate-right',
30757 cls : 'btn btn-default',
30758 html : '<i class="fa fa-repeat"></i>'
30771 * @class Roo.bootstrap.DocumentManager
30772 * @extends Roo.bootstrap.Component
30773 * Bootstrap DocumentManager class
30774 * @cfg {String} paramName default 'imageUpload'
30775 * @cfg {String} toolTipName default 'filename'
30776 * @cfg {String} method default POST
30777 * @cfg {String} url action url
30778 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30779 * @cfg {Boolean} multiple multiple upload default true
30780 * @cfg {Number} thumbSize default 300
30781 * @cfg {String} fieldLabel
30782 * @cfg {Number} labelWidth default 4
30783 * @cfg {String} labelAlign (left|top) default left
30784 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30785 * @cfg {Number} labellg set the width of label (1-12)
30786 * @cfg {Number} labelmd set the width of label (1-12)
30787 * @cfg {Number} labelsm set the width of label (1-12)
30788 * @cfg {Number} labelxs set the width of label (1-12)
30791 * Create a new DocumentManager
30792 * @param {Object} config The config object
30795 Roo.bootstrap.DocumentManager = function(config){
30796 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30799 this.delegates = [];
30804 * Fire when initial the DocumentManager
30805 * @param {Roo.bootstrap.DocumentManager} this
30810 * inspect selected file
30811 * @param {Roo.bootstrap.DocumentManager} this
30812 * @param {File} file
30817 * Fire when xhr load exception
30818 * @param {Roo.bootstrap.DocumentManager} this
30819 * @param {XMLHttpRequest} xhr
30821 "exception" : true,
30823 * @event afterupload
30824 * Fire when xhr load exception
30825 * @param {Roo.bootstrap.DocumentManager} this
30826 * @param {XMLHttpRequest} xhr
30828 "afterupload" : true,
30831 * prepare the form data
30832 * @param {Roo.bootstrap.DocumentManager} this
30833 * @param {Object} formData
30838 * Fire when remove the file
30839 * @param {Roo.bootstrap.DocumentManager} this
30840 * @param {Object} file
30845 * Fire after refresh the file
30846 * @param {Roo.bootstrap.DocumentManager} this
30851 * Fire after click the image
30852 * @param {Roo.bootstrap.DocumentManager} this
30853 * @param {Object} file
30858 * Fire when upload a image and editable set to true
30859 * @param {Roo.bootstrap.DocumentManager} this
30860 * @param {Object} file
30864 * @event beforeselectfile
30865 * Fire before select file
30866 * @param {Roo.bootstrap.DocumentManager} this
30868 "beforeselectfile" : true,
30871 * Fire before process file
30872 * @param {Roo.bootstrap.DocumentManager} this
30873 * @param {Object} file
30877 * @event previewrendered
30878 * Fire when preview rendered
30879 * @param {Roo.bootstrap.DocumentManager} this
30880 * @param {Object} file
30882 "previewrendered" : true,
30885 "previewResize" : true
30890 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
30899 paramName : 'imageUpload',
30900 toolTipName : 'filename',
30903 labelAlign : 'left',
30913 getAutoCreate : function()
30915 var managerWidget = {
30917 cls : 'roo-document-manager',
30921 cls : 'roo-document-manager-selector',
30926 cls : 'roo-document-manager-uploader',
30930 cls : 'roo-document-manager-upload-btn',
30931 html : '<i class="fa fa-plus"></i>'
30942 cls : 'column col-md-12',
30947 if(this.fieldLabel.length){
30952 cls : 'column col-md-12',
30953 html : this.fieldLabel
30957 cls : 'column col-md-12',
30962 if(this.labelAlign == 'left'){
30967 html : this.fieldLabel
30976 if(this.labelWidth > 12){
30977 content[0].style = "width: " + this.labelWidth + 'px';
30980 if(this.labelWidth < 13 && this.labelmd == 0){
30981 this.labelmd = this.labelWidth;
30984 if(this.labellg > 0){
30985 content[0].cls += ' col-lg-' + this.labellg;
30986 content[1].cls += ' col-lg-' + (12 - this.labellg);
30989 if(this.labelmd > 0){
30990 content[0].cls += ' col-md-' + this.labelmd;
30991 content[1].cls += ' col-md-' + (12 - this.labelmd);
30994 if(this.labelsm > 0){
30995 content[0].cls += ' col-sm-' + this.labelsm;
30996 content[1].cls += ' col-sm-' + (12 - this.labelsm);
30999 if(this.labelxs > 0){
31000 content[0].cls += ' col-xs-' + this.labelxs;
31001 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31009 cls : 'row clearfix',
31017 initEvents : function()
31019 this.managerEl = this.el.select('.roo-document-manager', true).first();
31020 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31022 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31023 this.selectorEl.hide();
31026 this.selectorEl.attr('multiple', 'multiple');
31029 this.selectorEl.on('change', this.onFileSelected, this);
31031 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31032 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31034 this.uploader.on('click', this.onUploaderClick, this);
31036 this.renderProgressDialog();
31040 window.addEventListener("resize", function() { _this.refresh(); } );
31042 this.fireEvent('initial', this);
31045 renderProgressDialog : function()
31049 this.progressDialog = new Roo.bootstrap.Modal({
31050 cls : 'roo-document-manager-progress-dialog',
31051 allow_close : false,
31062 btnclick : function() {
31063 _this.uploadCancel();
31069 this.progressDialog.render(Roo.get(document.body));
31071 this.progress = new Roo.bootstrap.Progress({
31072 cls : 'roo-document-manager-progress',
31077 this.progress.render(this.progressDialog.getChildContainer());
31079 this.progressBar = new Roo.bootstrap.ProgressBar({
31080 cls : 'roo-document-manager-progress-bar',
31083 aria_valuemax : 12,
31087 this.progressBar.render(this.progress.getChildContainer());
31090 onUploaderClick : function(e)
31092 e.preventDefault();
31094 if(this.fireEvent('beforeselectfile', this) != false){
31095 this.selectorEl.dom.click();
31100 onFileSelected : function(e)
31102 e.preventDefault();
31104 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31108 Roo.each(this.selectorEl.dom.files, function(file){
31109 if(this.fireEvent('inspect', this, file) != false){
31110 this.files.push(file);
31120 this.selectorEl.dom.value = '';
31122 if(!this.files || !this.files.length){
31126 if(this.boxes > 0 && this.files.length > this.boxes){
31127 this.files = this.files.slice(0, this.boxes);
31130 this.uploader.show();
31132 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31133 this.uploader.hide();
31142 Roo.each(this.files, function(file){
31144 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31145 var f = this.renderPreview(file);
31150 if(file.type.indexOf('image') != -1){
31151 this.delegates.push(
31153 _this.process(file);
31154 }).createDelegate(this)
31162 _this.process(file);
31163 }).createDelegate(this)
31168 this.files = files;
31170 this.delegates = this.delegates.concat(docs);
31172 if(!this.delegates.length){
31177 this.progressBar.aria_valuemax = this.delegates.length;
31184 arrange : function()
31186 if(!this.delegates.length){
31187 this.progressDialog.hide();
31192 var delegate = this.delegates.shift();
31194 this.progressDialog.show();
31196 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31198 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31203 refresh : function()
31205 this.uploader.show();
31207 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31208 this.uploader.hide();
31211 Roo.isTouch ? this.closable(false) : this.closable(true);
31213 this.fireEvent('refresh', this);
31216 onRemove : function(e, el, o)
31218 e.preventDefault();
31220 this.fireEvent('remove', this, o);
31224 remove : function(o)
31228 Roo.each(this.files, function(file){
31229 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31238 this.files = files;
31245 Roo.each(this.files, function(file){
31250 file.target.remove();
31259 onClick : function(e, el, o)
31261 e.preventDefault();
31263 this.fireEvent('click', this, o);
31267 closable : function(closable)
31269 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31271 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31283 xhrOnLoad : function(xhr)
31285 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31289 if (xhr.readyState !== 4) {
31291 this.fireEvent('exception', this, xhr);
31295 var response = Roo.decode(xhr.responseText);
31297 if(!response.success){
31299 this.fireEvent('exception', this, xhr);
31303 var file = this.renderPreview(response.data);
31305 this.files.push(file);
31309 this.fireEvent('afterupload', this, xhr);
31313 xhrOnError : function(xhr)
31315 Roo.log('xhr on error');
31317 var response = Roo.decode(xhr.responseText);
31324 process : function(file)
31326 if(this.fireEvent('process', this, file) !== false){
31327 if(this.editable && file.type.indexOf('image') != -1){
31328 this.fireEvent('edit', this, file);
31332 this.uploadStart(file, false);
31339 uploadStart : function(file, crop)
31341 this.xhr = new XMLHttpRequest();
31343 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31348 file.xhr = this.xhr;
31350 this.managerEl.createChild({
31352 cls : 'roo-document-manager-loading',
31356 tooltip : file.name,
31357 cls : 'roo-document-manager-thumb',
31358 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31364 this.xhr.open(this.method, this.url, true);
31367 "Accept": "application/json",
31368 "Cache-Control": "no-cache",
31369 "X-Requested-With": "XMLHttpRequest"
31372 for (var headerName in headers) {
31373 var headerValue = headers[headerName];
31375 this.xhr.setRequestHeader(headerName, headerValue);
31381 this.xhr.onload = function()
31383 _this.xhrOnLoad(_this.xhr);
31386 this.xhr.onerror = function()
31388 _this.xhrOnError(_this.xhr);
31391 var formData = new FormData();
31393 formData.append('returnHTML', 'NO');
31396 formData.append('crop', crop);
31399 formData.append(this.paramName, file, file.name);
31406 if(this.fireEvent('prepare', this, formData, options) != false){
31408 if(options.manually){
31412 this.xhr.send(formData);
31416 this.uploadCancel();
31419 uploadCancel : function()
31425 this.delegates = [];
31427 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31434 renderPreview : function(file)
31436 if(typeof(file.target) != 'undefined' && file.target){
31440 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31442 var previewEl = this.managerEl.createChild({
31444 cls : 'roo-document-manager-preview',
31448 tooltip : file[this.toolTipName],
31449 cls : 'roo-document-manager-thumb',
31450 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31455 html : '<i class="fa fa-times-circle"></i>'
31460 var close = previewEl.select('button.close', true).first();
31462 close.on('click', this.onRemove, this, file);
31464 file.target = previewEl;
31466 var image = previewEl.select('img', true).first();
31470 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31472 image.on('click', this.onClick, this, file);
31474 this.fireEvent('previewrendered', this, file);
31480 onPreviewLoad : function(file, image)
31482 if(typeof(file.target) == 'undefined' || !file.target){
31486 var width = image.dom.naturalWidth || image.dom.width;
31487 var height = image.dom.naturalHeight || image.dom.height;
31489 if(!this.previewResize) {
31493 if(width > height){
31494 file.target.addClass('wide');
31498 file.target.addClass('tall');
31503 uploadFromSource : function(file, crop)
31505 this.xhr = new XMLHttpRequest();
31507 this.managerEl.createChild({
31509 cls : 'roo-document-manager-loading',
31513 tooltip : file.name,
31514 cls : 'roo-document-manager-thumb',
31515 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31521 this.xhr.open(this.method, this.url, true);
31524 "Accept": "application/json",
31525 "Cache-Control": "no-cache",
31526 "X-Requested-With": "XMLHttpRequest"
31529 for (var headerName in headers) {
31530 var headerValue = headers[headerName];
31532 this.xhr.setRequestHeader(headerName, headerValue);
31538 this.xhr.onload = function()
31540 _this.xhrOnLoad(_this.xhr);
31543 this.xhr.onerror = function()
31545 _this.xhrOnError(_this.xhr);
31548 var formData = new FormData();
31550 formData.append('returnHTML', 'NO');
31552 formData.append('crop', crop);
31554 if(typeof(file.filename) != 'undefined'){
31555 formData.append('filename', file.filename);
31558 if(typeof(file.mimetype) != 'undefined'){
31559 formData.append('mimetype', file.mimetype);
31564 if(this.fireEvent('prepare', this, formData) != false){
31565 this.xhr.send(formData);
31575 * @class Roo.bootstrap.DocumentViewer
31576 * @extends Roo.bootstrap.Component
31577 * Bootstrap DocumentViewer class
31578 * @cfg {Boolean} showDownload (true|false) show download button (default true)
31579 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31582 * Create a new DocumentViewer
31583 * @param {Object} config The config object
31586 Roo.bootstrap.DocumentViewer = function(config){
31587 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31592 * Fire after initEvent
31593 * @param {Roo.bootstrap.DocumentViewer} this
31599 * @param {Roo.bootstrap.DocumentViewer} this
31604 * Fire after download button
31605 * @param {Roo.bootstrap.DocumentViewer} this
31610 * Fire after trash button
31611 * @param {Roo.bootstrap.DocumentViewer} this
31618 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
31620 showDownload : true,
31624 getAutoCreate : function()
31628 cls : 'roo-document-viewer',
31632 cls : 'roo-document-viewer-body',
31636 cls : 'roo-document-viewer-thumb',
31640 cls : 'roo-document-viewer-image'
31648 cls : 'roo-document-viewer-footer',
31651 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31655 cls : 'btn-group roo-document-viewer-download',
31659 cls : 'btn btn-default',
31660 html : '<i class="fa fa-download"></i>'
31666 cls : 'btn-group roo-document-viewer-trash',
31670 cls : 'btn btn-default',
31671 html : '<i class="fa fa-trash"></i>'
31684 initEvents : function()
31686 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31687 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31689 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31690 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31692 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31693 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31695 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31696 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31698 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31699 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31701 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31702 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31704 this.bodyEl.on('click', this.onClick, this);
31705 this.downloadBtn.on('click', this.onDownload, this);
31706 this.trashBtn.on('click', this.onTrash, this);
31708 this.downloadBtn.hide();
31709 this.trashBtn.hide();
31711 if(this.showDownload){
31712 this.downloadBtn.show();
31715 if(this.showTrash){
31716 this.trashBtn.show();
31719 if(!this.showDownload && !this.showTrash) {
31720 this.footerEl.hide();
31725 initial : function()
31727 this.fireEvent('initial', this);
31731 onClick : function(e)
31733 e.preventDefault();
31735 this.fireEvent('click', this);
31738 onDownload : function(e)
31740 e.preventDefault();
31742 this.fireEvent('download', this);
31745 onTrash : function(e)
31747 e.preventDefault();
31749 this.fireEvent('trash', this);
31761 * @class Roo.bootstrap.NavProgressBar
31762 * @extends Roo.bootstrap.Component
31763 * Bootstrap NavProgressBar class
31766 * Create a new nav progress bar
31767 * @param {Object} config The config object
31770 Roo.bootstrap.NavProgressBar = function(config){
31771 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31773 this.bullets = this.bullets || [];
31775 // Roo.bootstrap.NavProgressBar.register(this);
31779 * Fires when the active item changes
31780 * @param {Roo.bootstrap.NavProgressBar} this
31781 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31782 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
31789 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
31794 getAutoCreate : function()
31796 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31800 cls : 'roo-navigation-bar-group',
31804 cls : 'roo-navigation-top-bar'
31808 cls : 'roo-navigation-bullets-bar',
31812 cls : 'roo-navigation-bar'
31819 cls : 'roo-navigation-bottom-bar'
31829 initEvents: function()
31834 onRender : function(ct, position)
31836 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31838 if(this.bullets.length){
31839 Roo.each(this.bullets, function(b){
31848 addItem : function(cfg)
31850 var item = new Roo.bootstrap.NavProgressItem(cfg);
31852 item.parentId = this.id;
31853 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31856 var top = new Roo.bootstrap.Element({
31858 cls : 'roo-navigation-bar-text'
31861 var bottom = new Roo.bootstrap.Element({
31863 cls : 'roo-navigation-bar-text'
31866 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31867 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31869 var topText = new Roo.bootstrap.Element({
31871 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31874 var bottomText = new Roo.bootstrap.Element({
31876 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31879 topText.onRender(top.el, null);
31880 bottomText.onRender(bottom.el, null);
31883 item.bottomEl = bottom;
31886 this.barItems.push(item);
31891 getActive : function()
31893 var active = false;
31895 Roo.each(this.barItems, function(v){
31897 if (!v.isActive()) {
31909 setActiveItem : function(item)
31913 Roo.each(this.barItems, function(v){
31914 if (v.rid == item.rid) {
31918 if (v.isActive()) {
31919 v.setActive(false);
31924 item.setActive(true);
31926 this.fireEvent('changed', this, item, prev);
31929 getBarItem: function(rid)
31933 Roo.each(this.barItems, function(e) {
31934 if (e.rid != rid) {
31945 indexOfItem : function(item)
31949 Roo.each(this.barItems, function(v, i){
31951 if (v.rid != item.rid) {
31962 setActiveNext : function()
31964 var i = this.indexOfItem(this.getActive());
31966 if (i > this.barItems.length) {
31970 this.setActiveItem(this.barItems[i+1]);
31973 setActivePrev : function()
31975 var i = this.indexOfItem(this.getActive());
31981 this.setActiveItem(this.barItems[i-1]);
31984 format : function()
31986 if(!this.barItems.length){
31990 var width = 100 / this.barItems.length;
31992 Roo.each(this.barItems, function(i){
31993 i.el.setStyle('width', width + '%');
31994 i.topEl.el.setStyle('width', width + '%');
31995 i.bottomEl.el.setStyle('width', width + '%');
32004 * Nav Progress Item
32009 * @class Roo.bootstrap.NavProgressItem
32010 * @extends Roo.bootstrap.Component
32011 * Bootstrap NavProgressItem class
32012 * @cfg {String} rid the reference id
32013 * @cfg {Boolean} active (true|false) Is item active default false
32014 * @cfg {Boolean} disabled (true|false) Is item active default false
32015 * @cfg {String} html
32016 * @cfg {String} position (top|bottom) text position default bottom
32017 * @cfg {String} icon show icon instead of number
32020 * Create a new NavProgressItem
32021 * @param {Object} config The config object
32023 Roo.bootstrap.NavProgressItem = function(config){
32024 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32029 * The raw click event for the entire grid.
32030 * @param {Roo.bootstrap.NavProgressItem} this
32031 * @param {Roo.EventObject} e
32038 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32044 position : 'bottom',
32047 getAutoCreate : function()
32049 var iconCls = 'roo-navigation-bar-item-icon';
32051 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32055 cls: 'roo-navigation-bar-item',
32065 cfg.cls += ' active';
32068 cfg.cls += ' disabled';
32074 disable : function()
32076 this.setDisabled(true);
32079 enable : function()
32081 this.setDisabled(false);
32084 initEvents: function()
32086 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32088 this.iconEl.on('click', this.onClick, this);
32091 onClick : function(e)
32093 e.preventDefault();
32099 if(this.fireEvent('click', this, e) === false){
32103 this.parent().setActiveItem(this);
32106 isActive: function ()
32108 return this.active;
32111 setActive : function(state)
32113 if(this.active == state){
32117 this.active = state;
32120 this.el.addClass('active');
32124 this.el.removeClass('active');
32129 setDisabled : function(state)
32131 if(this.disabled == state){
32135 this.disabled = state;
32138 this.el.addClass('disabled');
32142 this.el.removeClass('disabled');
32145 tooltipEl : function()
32147 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32160 * @class Roo.bootstrap.FieldLabel
32161 * @extends Roo.bootstrap.Component
32162 * Bootstrap FieldLabel class
32163 * @cfg {String} html contents of the element
32164 * @cfg {String} tag tag of the element default label
32165 * @cfg {String} cls class of the element
32166 * @cfg {String} target label target
32167 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32168 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32169 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32170 * @cfg {String} iconTooltip default "This field is required"
32171 * @cfg {String} indicatorpos (left|right) default left
32174 * Create a new FieldLabel
32175 * @param {Object} config The config object
32178 Roo.bootstrap.FieldLabel = function(config){
32179 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32184 * Fires after the field has been marked as invalid.
32185 * @param {Roo.form.FieldLabel} this
32186 * @param {String} msg The validation message
32191 * Fires after the field has been validated with no errors.
32192 * @param {Roo.form.FieldLabel} this
32198 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32205 invalidClass : 'has-warning',
32206 validClass : 'has-success',
32207 iconTooltip : 'This field is required',
32208 indicatorpos : 'left',
32210 getAutoCreate : function(){
32213 if (!this.allowBlank) {
32219 cls : 'roo-bootstrap-field-label ' + this.cls,
32224 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32225 tooltip : this.iconTooltip
32234 if(this.indicatorpos == 'right'){
32237 cls : 'roo-bootstrap-field-label ' + this.cls,
32246 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32247 tooltip : this.iconTooltip
32256 initEvents: function()
32258 Roo.bootstrap.Element.superclass.initEvents.call(this);
32260 this.indicator = this.indicatorEl();
32262 if(this.indicator){
32263 this.indicator.removeClass('visible');
32264 this.indicator.addClass('invisible');
32267 Roo.bootstrap.FieldLabel.register(this);
32270 indicatorEl : function()
32272 var indicator = this.el.select('i.roo-required-indicator',true).first();
32283 * Mark this field as valid
32285 markValid : function()
32287 if(this.indicator){
32288 this.indicator.removeClass('visible');
32289 this.indicator.addClass('invisible');
32291 if (Roo.bootstrap.version == 3) {
32292 this.el.removeClass(this.invalidClass);
32293 this.el.addClass(this.validClass);
32295 this.el.removeClass('is-invalid');
32296 this.el.addClass('is-valid');
32300 this.fireEvent('valid', this);
32304 * Mark this field as invalid
32305 * @param {String} msg The validation message
32307 markInvalid : function(msg)
32309 if(this.indicator){
32310 this.indicator.removeClass('invisible');
32311 this.indicator.addClass('visible');
32313 if (Roo.bootstrap.version == 3) {
32314 this.el.removeClass(this.validClass);
32315 this.el.addClass(this.invalidClass);
32317 this.el.removeClass('is-valid');
32318 this.el.addClass('is-invalid');
32322 this.fireEvent('invalid', this, msg);
32328 Roo.apply(Roo.bootstrap.FieldLabel, {
32333 * register a FieldLabel Group
32334 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32336 register : function(label)
32338 if(this.groups.hasOwnProperty(label.target)){
32342 this.groups[label.target] = label;
32346 * fetch a FieldLabel Group based on the target
32347 * @param {string} target
32348 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32350 get: function(target) {
32351 if (typeof(this.groups[target]) == 'undefined') {
32355 return this.groups[target] ;
32364 * page DateSplitField.
32370 * @class Roo.bootstrap.DateSplitField
32371 * @extends Roo.bootstrap.Component
32372 * Bootstrap DateSplitField class
32373 * @cfg {string} fieldLabel - the label associated
32374 * @cfg {Number} labelWidth set the width of label (0-12)
32375 * @cfg {String} labelAlign (top|left)
32376 * @cfg {Boolean} dayAllowBlank (true|false) default false
32377 * @cfg {Boolean} monthAllowBlank (true|false) default false
32378 * @cfg {Boolean} yearAllowBlank (true|false) default false
32379 * @cfg {string} dayPlaceholder
32380 * @cfg {string} monthPlaceholder
32381 * @cfg {string} yearPlaceholder
32382 * @cfg {string} dayFormat default 'd'
32383 * @cfg {string} monthFormat default 'm'
32384 * @cfg {string} yearFormat default 'Y'
32385 * @cfg {Number} labellg set the width of label (1-12)
32386 * @cfg {Number} labelmd set the width of label (1-12)
32387 * @cfg {Number} labelsm set the width of label (1-12)
32388 * @cfg {Number} labelxs set the width of label (1-12)
32392 * Create a new DateSplitField
32393 * @param {Object} config The config object
32396 Roo.bootstrap.DateSplitField = function(config){
32397 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32403 * getting the data of years
32404 * @param {Roo.bootstrap.DateSplitField} this
32405 * @param {Object} years
32410 * getting the data of days
32411 * @param {Roo.bootstrap.DateSplitField} this
32412 * @param {Object} days
32417 * Fires after the field has been marked as invalid.
32418 * @param {Roo.form.Field} this
32419 * @param {String} msg The validation message
32424 * Fires after the field has been validated with no errors.
32425 * @param {Roo.form.Field} this
32431 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32434 labelAlign : 'top',
32436 dayAllowBlank : false,
32437 monthAllowBlank : false,
32438 yearAllowBlank : false,
32439 dayPlaceholder : '',
32440 monthPlaceholder : '',
32441 yearPlaceholder : '',
32445 isFormField : true,
32451 getAutoCreate : function()
32455 cls : 'row roo-date-split-field-group',
32460 cls : 'form-hidden-field roo-date-split-field-group-value',
32466 var labelCls = 'col-md-12';
32467 var contentCls = 'col-md-4';
32469 if(this.fieldLabel){
32473 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32477 html : this.fieldLabel
32482 if(this.labelAlign == 'left'){
32484 if(this.labelWidth > 12){
32485 label.style = "width: " + this.labelWidth + 'px';
32488 if(this.labelWidth < 13 && this.labelmd == 0){
32489 this.labelmd = this.labelWidth;
32492 if(this.labellg > 0){
32493 labelCls = ' col-lg-' + this.labellg;
32494 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32497 if(this.labelmd > 0){
32498 labelCls = ' col-md-' + this.labelmd;
32499 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32502 if(this.labelsm > 0){
32503 labelCls = ' col-sm-' + this.labelsm;
32504 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32507 if(this.labelxs > 0){
32508 labelCls = ' col-xs-' + this.labelxs;
32509 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32513 label.cls += ' ' + labelCls;
32515 cfg.cn.push(label);
32518 Roo.each(['day', 'month', 'year'], function(t){
32521 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32528 inputEl: function ()
32530 return this.el.select('.roo-date-split-field-group-value', true).first();
32533 onRender : function(ct, position)
32537 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32539 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32541 this.dayField = new Roo.bootstrap.ComboBox({
32542 allowBlank : this.dayAllowBlank,
32543 alwaysQuery : true,
32544 displayField : 'value',
32547 forceSelection : true,
32549 placeholder : this.dayPlaceholder,
32550 selectOnFocus : true,
32551 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32552 triggerAction : 'all',
32554 valueField : 'value',
32555 store : new Roo.data.SimpleStore({
32556 data : (function() {
32558 _this.fireEvent('days', _this, days);
32561 fields : [ 'value' ]
32564 select : function (_self, record, index)
32566 _this.setValue(_this.getValue());
32571 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32573 this.monthField = new Roo.bootstrap.MonthField({
32574 after : '<i class=\"fa fa-calendar\"></i>',
32575 allowBlank : this.monthAllowBlank,
32576 placeholder : this.monthPlaceholder,
32579 render : function (_self)
32581 this.el.select('span.input-group-addon', true).first().on('click', function(e){
32582 e.preventDefault();
32586 select : function (_self, oldvalue, newvalue)
32588 _this.setValue(_this.getValue());
32593 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32595 this.yearField = new Roo.bootstrap.ComboBox({
32596 allowBlank : this.yearAllowBlank,
32597 alwaysQuery : true,
32598 displayField : 'value',
32601 forceSelection : true,
32603 placeholder : this.yearPlaceholder,
32604 selectOnFocus : true,
32605 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32606 triggerAction : 'all',
32608 valueField : 'value',
32609 store : new Roo.data.SimpleStore({
32610 data : (function() {
32612 _this.fireEvent('years', _this, years);
32615 fields : [ 'value' ]
32618 select : function (_self, record, index)
32620 _this.setValue(_this.getValue());
32625 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32628 setValue : function(v, format)
32630 this.inputEl.dom.value = v;
32632 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32634 var d = Date.parseDate(v, f);
32641 this.setDay(d.format(this.dayFormat));
32642 this.setMonth(d.format(this.monthFormat));
32643 this.setYear(d.format(this.yearFormat));
32650 setDay : function(v)
32652 this.dayField.setValue(v);
32653 this.inputEl.dom.value = this.getValue();
32658 setMonth : function(v)
32660 this.monthField.setValue(v, true);
32661 this.inputEl.dom.value = this.getValue();
32666 setYear : function(v)
32668 this.yearField.setValue(v);
32669 this.inputEl.dom.value = this.getValue();
32674 getDay : function()
32676 return this.dayField.getValue();
32679 getMonth : function()
32681 return this.monthField.getValue();
32684 getYear : function()
32686 return this.yearField.getValue();
32689 getValue : function()
32691 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32693 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32703 this.inputEl.dom.value = '';
32708 validate : function()
32710 var d = this.dayField.validate();
32711 var m = this.monthField.validate();
32712 var y = this.yearField.validate();
32717 (!this.dayAllowBlank && !d) ||
32718 (!this.monthAllowBlank && !m) ||
32719 (!this.yearAllowBlank && !y)
32724 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32733 this.markInvalid();
32738 markValid : function()
32741 var label = this.el.select('label', true).first();
32742 var icon = this.el.select('i.fa-star', true).first();
32748 this.fireEvent('valid', this);
32752 * Mark this field as invalid
32753 * @param {String} msg The validation message
32755 markInvalid : function(msg)
32758 var label = this.el.select('label', true).first();
32759 var icon = this.el.select('i.fa-star', true).first();
32761 if(label && !icon){
32762 this.el.select('.roo-date-split-field-label', true).createChild({
32764 cls : 'text-danger fa fa-lg fa-star',
32765 tooltip : 'This field is required',
32766 style : 'margin-right:5px;'
32770 this.fireEvent('invalid', this, msg);
32773 clearInvalid : function()
32775 var label = this.el.select('label', true).first();
32776 var icon = this.el.select('i.fa-star', true).first();
32782 this.fireEvent('valid', this);
32785 getName: function()
32795 * http://masonry.desandro.com
32797 * The idea is to render all the bricks based on vertical width...
32799 * The original code extends 'outlayer' - we might need to use that....
32805 * @class Roo.bootstrap.LayoutMasonry
32806 * @extends Roo.bootstrap.Component
32807 * Bootstrap Layout Masonry class
32810 * Create a new Element
32811 * @param {Object} config The config object
32814 Roo.bootstrap.LayoutMasonry = function(config){
32816 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32820 Roo.bootstrap.LayoutMasonry.register(this);
32826 * Fire after layout the items
32827 * @param {Roo.bootstrap.LayoutMasonry} this
32828 * @param {Roo.EventObject} e
32835 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
32838 * @cfg {Boolean} isLayoutInstant = no animation?
32840 isLayoutInstant : false, // needed?
32843 * @cfg {Number} boxWidth width of the columns
32848 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
32853 * @cfg {Number} padWidth padding below box..
32858 * @cfg {Number} gutter gutter width..
32863 * @cfg {Number} maxCols maximum number of columns
32869 * @cfg {Boolean} isAutoInitial defalut true
32871 isAutoInitial : true,
32876 * @cfg {Boolean} isHorizontal defalut false
32878 isHorizontal : false,
32880 currentSize : null,
32886 bricks: null, //CompositeElement
32890 _isLayoutInited : false,
32892 // isAlternative : false, // only use for vertical layout...
32895 * @cfg {Number} alternativePadWidth padding below box..
32897 alternativePadWidth : 50,
32899 selectedBrick : [],
32901 getAutoCreate : function(){
32903 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32907 cls: 'blog-masonary-wrapper ' + this.cls,
32909 cls : 'mas-boxes masonary'
32916 getChildContainer: function( )
32918 if (this.boxesEl) {
32919 return this.boxesEl;
32922 this.boxesEl = this.el.select('.mas-boxes').first();
32924 return this.boxesEl;
32928 initEvents : function()
32932 if(this.isAutoInitial){
32933 Roo.log('hook children rendered');
32934 this.on('childrenrendered', function() {
32935 Roo.log('children rendered');
32941 initial : function()
32943 this.selectedBrick = [];
32945 this.currentSize = this.el.getBox(true);
32947 Roo.EventManager.onWindowResize(this.resize, this);
32949 if(!this.isAutoInitial){
32957 //this.layout.defer(500,this);
32961 resize : function()
32963 var cs = this.el.getBox(true);
32966 this.currentSize.width == cs.width &&
32967 this.currentSize.x == cs.x &&
32968 this.currentSize.height == cs.height &&
32969 this.currentSize.y == cs.y
32971 Roo.log("no change in with or X or Y");
32975 this.currentSize = cs;
32981 layout : function()
32983 this._resetLayout();
32985 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32987 this.layoutItems( isInstant );
32989 this._isLayoutInited = true;
32991 this.fireEvent('layout', this);
32995 _resetLayout : function()
32997 if(this.isHorizontal){
32998 this.horizontalMeasureColumns();
33002 this.verticalMeasureColumns();
33006 verticalMeasureColumns : function()
33008 this.getContainerWidth();
33010 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33011 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33015 var boxWidth = this.boxWidth + this.padWidth;
33017 if(this.containerWidth < this.boxWidth){
33018 boxWidth = this.containerWidth
33021 var containerWidth = this.containerWidth;
33023 var cols = Math.floor(containerWidth / boxWidth);
33025 this.cols = Math.max( cols, 1 );
33027 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33029 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33031 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33033 this.colWidth = boxWidth + avail - this.padWidth;
33035 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33036 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33039 horizontalMeasureColumns : function()
33041 this.getContainerWidth();
33043 var boxWidth = this.boxWidth;
33045 if(this.containerWidth < boxWidth){
33046 boxWidth = this.containerWidth;
33049 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33051 this.el.setHeight(boxWidth);
33055 getContainerWidth : function()
33057 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33060 layoutItems : function( isInstant )
33062 Roo.log(this.bricks);
33064 var items = Roo.apply([], this.bricks);
33066 if(this.isHorizontal){
33067 this._horizontalLayoutItems( items , isInstant );
33071 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33072 // this._verticalAlternativeLayoutItems( items , isInstant );
33076 this._verticalLayoutItems( items , isInstant );
33080 _verticalLayoutItems : function ( items , isInstant)
33082 if ( !items || !items.length ) {
33087 ['xs', 'xs', 'xs', 'tall'],
33088 ['xs', 'xs', 'tall'],
33089 ['xs', 'xs', 'sm'],
33090 ['xs', 'xs', 'xs'],
33096 ['sm', 'xs', 'xs'],
33100 ['tall', 'xs', 'xs', 'xs'],
33101 ['tall', 'xs', 'xs'],
33113 Roo.each(items, function(item, k){
33115 switch (item.size) {
33116 // these layouts take up a full box,
33127 boxes.push([item]);
33150 var filterPattern = function(box, length)
33158 var pattern = box.slice(0, length);
33162 Roo.each(pattern, function(i){
33163 format.push(i.size);
33166 Roo.each(standard, function(s){
33168 if(String(s) != String(format)){
33177 if(!match && length == 1){
33182 filterPattern(box, length - 1);
33186 queue.push(pattern);
33188 box = box.slice(length, box.length);
33190 filterPattern(box, 4);
33196 Roo.each(boxes, function(box, k){
33202 if(box.length == 1){
33207 filterPattern(box, 4);
33211 this._processVerticalLayoutQueue( queue, isInstant );
33215 // _verticalAlternativeLayoutItems : function( items , isInstant )
33217 // if ( !items || !items.length ) {
33221 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33225 _horizontalLayoutItems : function ( items , isInstant)
33227 if ( !items || !items.length || items.length < 3) {
33233 var eItems = items.slice(0, 3);
33235 items = items.slice(3, items.length);
33238 ['xs', 'xs', 'xs', 'wide'],
33239 ['xs', 'xs', 'wide'],
33240 ['xs', 'xs', 'sm'],
33241 ['xs', 'xs', 'xs'],
33247 ['sm', 'xs', 'xs'],
33251 ['wide', 'xs', 'xs', 'xs'],
33252 ['wide', 'xs', 'xs'],
33265 Roo.each(items, function(item, k){
33267 switch (item.size) {
33278 boxes.push([item]);
33302 var filterPattern = function(box, length)
33310 var pattern = box.slice(0, length);
33314 Roo.each(pattern, function(i){
33315 format.push(i.size);
33318 Roo.each(standard, function(s){
33320 if(String(s) != String(format)){
33329 if(!match && length == 1){
33334 filterPattern(box, length - 1);
33338 queue.push(pattern);
33340 box = box.slice(length, box.length);
33342 filterPattern(box, 4);
33348 Roo.each(boxes, function(box, k){
33354 if(box.length == 1){
33359 filterPattern(box, 4);
33366 var pos = this.el.getBox(true);
33370 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33372 var hit_end = false;
33374 Roo.each(queue, function(box){
33378 Roo.each(box, function(b){
33380 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33390 Roo.each(box, function(b){
33392 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33395 mx = Math.max(mx, b.x);
33399 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33403 Roo.each(box, function(b){
33405 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33419 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33422 /** Sets position of item in DOM
33423 * @param {Element} item
33424 * @param {Number} x - horizontal position
33425 * @param {Number} y - vertical position
33426 * @param {Boolean} isInstant - disables transitions
33428 _processVerticalLayoutQueue : function( queue, isInstant )
33430 var pos = this.el.getBox(true);
33435 for (var i = 0; i < this.cols; i++){
33439 Roo.each(queue, function(box, k){
33441 var col = k % this.cols;
33443 Roo.each(box, function(b,kk){
33445 b.el.position('absolute');
33447 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33448 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33450 if(b.size == 'md-left' || b.size == 'md-right'){
33451 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33452 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33455 b.el.setWidth(width);
33456 b.el.setHeight(height);
33458 b.el.select('iframe',true).setSize(width,height);
33462 for (var i = 0; i < this.cols; i++){
33464 if(maxY[i] < maxY[col]){
33469 col = Math.min(col, i);
33473 x = pos.x + col * (this.colWidth + this.padWidth);
33477 var positions = [];
33479 switch (box.length){
33481 positions = this.getVerticalOneBoxColPositions(x, y, box);
33484 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33487 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33490 positions = this.getVerticalFourBoxColPositions(x, y, box);
33496 Roo.each(box, function(b,kk){
33498 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33500 var sz = b.el.getSize();
33502 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33510 for (var i = 0; i < this.cols; i++){
33511 mY = Math.max(mY, maxY[i]);
33514 this.el.setHeight(mY - pos.y);
33518 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33520 // var pos = this.el.getBox(true);
33523 // var maxX = pos.right;
33525 // var maxHeight = 0;
33527 // Roo.each(items, function(item, k){
33531 // item.el.position('absolute');
33533 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33535 // item.el.setWidth(width);
33537 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33539 // item.el.setHeight(height);
33542 // item.el.setXY([x, y], isInstant ? false : true);
33544 // item.el.setXY([maxX - width, y], isInstant ? false : true);
33547 // y = y + height + this.alternativePadWidth;
33549 // maxHeight = maxHeight + height + this.alternativePadWidth;
33553 // this.el.setHeight(maxHeight);
33557 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33559 var pos = this.el.getBox(true);
33564 var maxX = pos.right;
33566 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33568 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33570 Roo.each(queue, function(box, k){
33572 Roo.each(box, function(b, kk){
33574 b.el.position('absolute');
33576 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33577 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33579 if(b.size == 'md-left' || b.size == 'md-right'){
33580 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33581 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33584 b.el.setWidth(width);
33585 b.el.setHeight(height);
33593 var positions = [];
33595 switch (box.length){
33597 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33600 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33603 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33606 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33612 Roo.each(box, function(b,kk){
33614 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33616 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33624 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33626 Roo.each(eItems, function(b,k){
33628 b.size = (k == 0) ? 'sm' : 'xs';
33629 b.x = (k == 0) ? 2 : 1;
33630 b.y = (k == 0) ? 2 : 1;
33632 b.el.position('absolute');
33634 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33636 b.el.setWidth(width);
33638 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33640 b.el.setHeight(height);
33644 var positions = [];
33647 x : maxX - this.unitWidth * 2 - this.gutter,
33652 x : maxX - this.unitWidth,
33653 y : minY + (this.unitWidth + this.gutter) * 2
33657 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33661 Roo.each(eItems, function(b,k){
33663 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33669 getVerticalOneBoxColPositions : function(x, y, box)
33673 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33675 if(box[0].size == 'md-left'){
33679 if(box[0].size == 'md-right'){
33684 x : x + (this.unitWidth + this.gutter) * rand,
33691 getVerticalTwoBoxColPositions : function(x, y, box)
33695 if(box[0].size == 'xs'){
33699 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33703 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33717 x : x + (this.unitWidth + this.gutter) * 2,
33718 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33725 getVerticalThreeBoxColPositions : function(x, y, box)
33729 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33737 x : x + (this.unitWidth + this.gutter) * 1,
33742 x : x + (this.unitWidth + this.gutter) * 2,
33750 if(box[0].size == 'xs' && box[1].size == 'xs'){
33759 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33763 x : x + (this.unitWidth + this.gutter) * 1,
33777 x : x + (this.unitWidth + this.gutter) * 2,
33782 x : x + (this.unitWidth + this.gutter) * 2,
33783 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33790 getVerticalFourBoxColPositions : function(x, y, box)
33794 if(box[0].size == 'xs'){
33803 y : y + (this.unitHeight + this.gutter) * 1
33808 y : y + (this.unitHeight + this.gutter) * 2
33812 x : x + (this.unitWidth + this.gutter) * 1,
33826 x : x + (this.unitWidth + this.gutter) * 2,
33831 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33832 y : y + (this.unitHeight + this.gutter) * 1
33836 x : x + (this.unitWidth + this.gutter) * 2,
33837 y : y + (this.unitWidth + this.gutter) * 2
33844 getHorizontalOneBoxColPositions : function(maxX, minY, box)
33848 if(box[0].size == 'md-left'){
33850 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33857 if(box[0].size == 'md-right'){
33859 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33860 y : minY + (this.unitWidth + this.gutter) * 1
33866 var rand = Math.floor(Math.random() * (4 - box[0].y));
33869 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33870 y : minY + (this.unitWidth + this.gutter) * rand
33877 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33881 if(box[0].size == 'xs'){
33884 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33889 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33890 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33898 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33903 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33904 y : minY + (this.unitWidth + this.gutter) * 2
33911 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33915 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33918 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33923 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33924 y : minY + (this.unitWidth + this.gutter) * 1
33928 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33929 y : minY + (this.unitWidth + this.gutter) * 2
33936 if(box[0].size == 'xs' && box[1].size == 'xs'){
33939 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33944 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33949 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33950 y : minY + (this.unitWidth + this.gutter) * 1
33958 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33963 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33964 y : minY + (this.unitWidth + this.gutter) * 2
33968 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33969 y : minY + (this.unitWidth + this.gutter) * 2
33976 getHorizontalFourBoxColPositions : function(maxX, minY, box)
33980 if(box[0].size == 'xs'){
33983 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33988 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33993 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),
33998 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33999 y : minY + (this.unitWidth + this.gutter) * 1
34007 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34012 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34013 y : minY + (this.unitWidth + this.gutter) * 2
34017 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34018 y : minY + (this.unitWidth + this.gutter) * 2
34022 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),
34023 y : minY + (this.unitWidth + this.gutter) * 2
34031 * remove a Masonry Brick
34032 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34034 removeBrick : function(brick_id)
34040 for (var i = 0; i<this.bricks.length; i++) {
34041 if (this.bricks[i].id == brick_id) {
34042 this.bricks.splice(i,1);
34043 this.el.dom.removeChild(Roo.get(brick_id).dom);
34050 * adds a Masonry Brick
34051 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34053 addBrick : function(cfg)
34055 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34056 //this.register(cn);
34057 cn.parentId = this.id;
34058 cn.render(this.el);
34063 * register a Masonry Brick
34064 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34067 register : function(brick)
34069 this.bricks.push(brick);
34070 brick.masonryId = this.id;
34074 * clear all the Masonry Brick
34076 clearAll : function()
34079 //this.getChildContainer().dom.innerHTML = "";
34080 this.el.dom.innerHTML = '';
34083 getSelected : function()
34085 if (!this.selectedBrick) {
34089 return this.selectedBrick;
34093 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34097 * register a Masonry Layout
34098 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34101 register : function(layout)
34103 this.groups[layout.id] = layout;
34106 * fetch a Masonry Layout based on the masonry layout ID
34107 * @param {string} the masonry layout to add
34108 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34111 get: function(layout_id) {
34112 if (typeof(this.groups[layout_id]) == 'undefined') {
34115 return this.groups[layout_id] ;
34127 * http://masonry.desandro.com
34129 * The idea is to render all the bricks based on vertical width...
34131 * The original code extends 'outlayer' - we might need to use that....
34137 * @class Roo.bootstrap.LayoutMasonryAuto
34138 * @extends Roo.bootstrap.Component
34139 * Bootstrap Layout Masonry class
34142 * Create a new Element
34143 * @param {Object} config The config object
34146 Roo.bootstrap.LayoutMasonryAuto = function(config){
34147 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34150 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34153 * @cfg {Boolean} isFitWidth - resize the width..
34155 isFitWidth : false, // options..
34157 * @cfg {Boolean} isOriginLeft = left align?
34159 isOriginLeft : true,
34161 * @cfg {Boolean} isOriginTop = top align?
34163 isOriginTop : false,
34165 * @cfg {Boolean} isLayoutInstant = no animation?
34167 isLayoutInstant : false, // needed?
34169 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34171 isResizingContainer : true,
34173 * @cfg {Number} columnWidth width of the columns
34179 * @cfg {Number} maxCols maximum number of columns
34184 * @cfg {Number} padHeight padding below box..
34190 * @cfg {Boolean} isAutoInitial defalut true
34193 isAutoInitial : true,
34199 initialColumnWidth : 0,
34200 currentSize : null,
34202 colYs : null, // array.
34209 bricks: null, //CompositeElement
34210 cols : 0, // array?
34211 // element : null, // wrapped now this.el
34212 _isLayoutInited : null,
34215 getAutoCreate : function(){
34219 cls: 'blog-masonary-wrapper ' + this.cls,
34221 cls : 'mas-boxes masonary'
34228 getChildContainer: function( )
34230 if (this.boxesEl) {
34231 return this.boxesEl;
34234 this.boxesEl = this.el.select('.mas-boxes').first();
34236 return this.boxesEl;
34240 initEvents : function()
34244 if(this.isAutoInitial){
34245 Roo.log('hook children rendered');
34246 this.on('childrenrendered', function() {
34247 Roo.log('children rendered');
34254 initial : function()
34256 this.reloadItems();
34258 this.currentSize = this.el.getBox(true);
34260 /// was window resize... - let's see if this works..
34261 Roo.EventManager.onWindowResize(this.resize, this);
34263 if(!this.isAutoInitial){
34268 this.layout.defer(500,this);
34271 reloadItems: function()
34273 this.bricks = this.el.select('.masonry-brick', true);
34275 this.bricks.each(function(b) {
34276 //Roo.log(b.getSize());
34277 if (!b.attr('originalwidth')) {
34278 b.attr('originalwidth', b.getSize().width);
34283 Roo.log(this.bricks.elements.length);
34286 resize : function()
34289 var cs = this.el.getBox(true);
34291 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34292 Roo.log("no change in with or X");
34295 this.currentSize = cs;
34299 layout : function()
34302 this._resetLayout();
34303 //this._manageStamps();
34305 // don't animate first layout
34306 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34307 this.layoutItems( isInstant );
34309 // flag for initalized
34310 this._isLayoutInited = true;
34313 layoutItems : function( isInstant )
34315 //var items = this._getItemsForLayout( this.items );
34316 // original code supports filtering layout items.. we just ignore it..
34318 this._layoutItems( this.bricks , isInstant );
34320 this._postLayout();
34322 _layoutItems : function ( items , isInstant)
34324 //this.fireEvent( 'layout', this, items );
34327 if ( !items || !items.elements.length ) {
34328 // no items, emit event with empty array
34333 items.each(function(item) {
34334 Roo.log("layout item");
34336 // get x/y object from method
34337 var position = this._getItemLayoutPosition( item );
34339 position.item = item;
34340 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34341 queue.push( position );
34344 this._processLayoutQueue( queue );
34346 /** Sets position of item in DOM
34347 * @param {Element} item
34348 * @param {Number} x - horizontal position
34349 * @param {Number} y - vertical position
34350 * @param {Boolean} isInstant - disables transitions
34352 _processLayoutQueue : function( queue )
34354 for ( var i=0, len = queue.length; i < len; i++ ) {
34355 var obj = queue[i];
34356 obj.item.position('absolute');
34357 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34363 * Any logic you want to do after each layout,
34364 * i.e. size the container
34366 _postLayout : function()
34368 this.resizeContainer();
34371 resizeContainer : function()
34373 if ( !this.isResizingContainer ) {
34376 var size = this._getContainerSize();
34378 this.el.setSize(size.width,size.height);
34379 this.boxesEl.setSize(size.width,size.height);
34385 _resetLayout : function()
34387 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34388 this.colWidth = this.el.getWidth();
34389 //this.gutter = this.el.getWidth();
34391 this.measureColumns();
34397 this.colYs.push( 0 );
34403 measureColumns : function()
34405 this.getContainerWidth();
34406 // if columnWidth is 0, default to outerWidth of first item
34407 if ( !this.columnWidth ) {
34408 var firstItem = this.bricks.first();
34409 Roo.log(firstItem);
34410 this.columnWidth = this.containerWidth;
34411 if (firstItem && firstItem.attr('originalwidth') ) {
34412 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34414 // columnWidth fall back to item of first element
34415 Roo.log("set column width?");
34416 this.initialColumnWidth = this.columnWidth ;
34418 // if first elem has no width, default to size of container
34423 if (this.initialColumnWidth) {
34424 this.columnWidth = this.initialColumnWidth;
34429 // column width is fixed at the top - however if container width get's smaller we should
34432 // this bit calcs how man columns..
34434 var columnWidth = this.columnWidth += this.gutter;
34436 // calculate columns
34437 var containerWidth = this.containerWidth + this.gutter;
34439 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34440 // fix rounding errors, typically with gutters
34441 var excess = columnWidth - containerWidth % columnWidth;
34444 // if overshoot is less than a pixel, round up, otherwise floor it
34445 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34446 cols = Math[ mathMethod ]( cols );
34447 this.cols = Math.max( cols, 1 );
34448 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34450 // padding positioning..
34451 var totalColWidth = this.cols * this.columnWidth;
34452 var padavail = this.containerWidth - totalColWidth;
34453 // so for 2 columns - we need 3 'pads'
34455 var padNeeded = (1+this.cols) * this.padWidth;
34457 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34459 this.columnWidth += padExtra
34460 //this.padWidth = Math.floor(padavail / ( this.cols));
34462 // adjust colum width so that padding is fixed??
34464 // we have 3 columns ... total = width * 3
34465 // we have X left over... that should be used by
34467 //if (this.expandC) {
34475 getContainerWidth : function()
34477 /* // container is parent if fit width
34478 var container = this.isFitWidth ? this.element.parentNode : this.element;
34479 // check that this.size and size are there
34480 // IE8 triggers resize on body size change, so they might not be
34482 var size = getSize( container ); //FIXME
34483 this.containerWidth = size && size.innerWidth; //FIXME
34486 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34490 _getItemLayoutPosition : function( item ) // what is item?
34492 // we resize the item to our columnWidth..
34494 item.setWidth(this.columnWidth);
34495 item.autoBoxAdjust = false;
34497 var sz = item.getSize();
34499 // how many columns does this brick span
34500 var remainder = this.containerWidth % this.columnWidth;
34502 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34503 // round if off by 1 pixel, otherwise use ceil
34504 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34505 colSpan = Math.min( colSpan, this.cols );
34507 // normally this should be '1' as we dont' currently allow multi width columns..
34509 var colGroup = this._getColGroup( colSpan );
34510 // get the minimum Y value from the columns
34511 var minimumY = Math.min.apply( Math, colGroup );
34512 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34514 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
34516 // position the brick
34518 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34519 y: this.currentSize.y + minimumY + this.padHeight
34523 // apply setHeight to necessary columns
34524 var setHeight = minimumY + sz.height + this.padHeight;
34525 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34527 var setSpan = this.cols + 1 - colGroup.length;
34528 for ( var i = 0; i < setSpan; i++ ) {
34529 this.colYs[ shortColIndex + i ] = setHeight ;
34536 * @param {Number} colSpan - number of columns the element spans
34537 * @returns {Array} colGroup
34539 _getColGroup : function( colSpan )
34541 if ( colSpan < 2 ) {
34542 // if brick spans only one column, use all the column Ys
34547 // how many different places could this brick fit horizontally
34548 var groupCount = this.cols + 1 - colSpan;
34549 // for each group potential horizontal position
34550 for ( var i = 0; i < groupCount; i++ ) {
34551 // make an array of colY values for that one group
34552 var groupColYs = this.colYs.slice( i, i + colSpan );
34553 // and get the max value of the array
34554 colGroup[i] = Math.max.apply( Math, groupColYs );
34559 _manageStamp : function( stamp )
34561 var stampSize = stamp.getSize();
34562 var offset = stamp.getBox();
34563 // get the columns that this stamp affects
34564 var firstX = this.isOriginLeft ? offset.x : offset.right;
34565 var lastX = firstX + stampSize.width;
34566 var firstCol = Math.floor( firstX / this.columnWidth );
34567 firstCol = Math.max( 0, firstCol );
34569 var lastCol = Math.floor( lastX / this.columnWidth );
34570 // lastCol should not go over if multiple of columnWidth #425
34571 lastCol -= lastX % this.columnWidth ? 0 : 1;
34572 lastCol = Math.min( this.cols - 1, lastCol );
34574 // set colYs to bottom of the stamp
34575 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34578 for ( var i = firstCol; i <= lastCol; i++ ) {
34579 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34584 _getContainerSize : function()
34586 this.maxY = Math.max.apply( Math, this.colYs );
34591 if ( this.isFitWidth ) {
34592 size.width = this._getContainerFitWidth();
34598 _getContainerFitWidth : function()
34600 var unusedCols = 0;
34601 // count unused columns
34604 if ( this.colYs[i] !== 0 ) {
34609 // fit container to columns that have been used
34610 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34613 needsResizeLayout : function()
34615 var previousWidth = this.containerWidth;
34616 this.getContainerWidth();
34617 return previousWidth !== this.containerWidth;
34632 * @class Roo.bootstrap.MasonryBrick
34633 * @extends Roo.bootstrap.Component
34634 * Bootstrap MasonryBrick class
34637 * Create a new MasonryBrick
34638 * @param {Object} config The config object
34641 Roo.bootstrap.MasonryBrick = function(config){
34643 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34645 Roo.bootstrap.MasonryBrick.register(this);
34651 * When a MasonryBrick is clcik
34652 * @param {Roo.bootstrap.MasonryBrick} this
34653 * @param {Roo.EventObject} e
34659 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34662 * @cfg {String} title
34666 * @cfg {String} html
34670 * @cfg {String} bgimage
34674 * @cfg {String} videourl
34678 * @cfg {String} cls
34682 * @cfg {String} href
34686 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34691 * @cfg {String} placetitle (center|bottom)
34696 * @cfg {Boolean} isFitContainer defalut true
34698 isFitContainer : true,
34701 * @cfg {Boolean} preventDefault defalut false
34703 preventDefault : false,
34706 * @cfg {Boolean} inverse defalut false
34708 maskInverse : false,
34710 getAutoCreate : function()
34712 if(!this.isFitContainer){
34713 return this.getSplitAutoCreate();
34716 var cls = 'masonry-brick masonry-brick-full';
34718 if(this.href.length){
34719 cls += ' masonry-brick-link';
34722 if(this.bgimage.length){
34723 cls += ' masonry-brick-image';
34726 if(this.maskInverse){
34727 cls += ' mask-inverse';
34730 if(!this.html.length && !this.maskInverse && !this.videourl.length){
34731 cls += ' enable-mask';
34735 cls += ' masonry-' + this.size + '-brick';
34738 if(this.placetitle.length){
34740 switch (this.placetitle) {
34742 cls += ' masonry-center-title';
34745 cls += ' masonry-bottom-title';
34752 if(!this.html.length && !this.bgimage.length){
34753 cls += ' masonry-center-title';
34756 if(!this.html.length && this.bgimage.length){
34757 cls += ' masonry-bottom-title';
34762 cls += ' ' + this.cls;
34766 tag: (this.href.length) ? 'a' : 'div',
34771 cls: 'masonry-brick-mask'
34775 cls: 'masonry-brick-paragraph',
34781 if(this.href.length){
34782 cfg.href = this.href;
34785 var cn = cfg.cn[1].cn;
34787 if(this.title.length){
34790 cls: 'masonry-brick-title',
34795 if(this.html.length){
34798 cls: 'masonry-brick-text',
34803 if (!this.title.length && !this.html.length) {
34804 cfg.cn[1].cls += ' hide';
34807 if(this.bgimage.length){
34810 cls: 'masonry-brick-image-view',
34815 if(this.videourl.length){
34816 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34817 // youtube support only?
34820 cls: 'masonry-brick-image-view',
34823 allowfullscreen : true
34831 getSplitAutoCreate : function()
34833 var cls = 'masonry-brick masonry-brick-split';
34835 if(this.href.length){
34836 cls += ' masonry-brick-link';
34839 if(this.bgimage.length){
34840 cls += ' masonry-brick-image';
34844 cls += ' masonry-' + this.size + '-brick';
34847 switch (this.placetitle) {
34849 cls += ' masonry-center-title';
34852 cls += ' masonry-bottom-title';
34855 if(!this.bgimage.length){
34856 cls += ' masonry-center-title';
34859 if(this.bgimage.length){
34860 cls += ' masonry-bottom-title';
34866 cls += ' ' + this.cls;
34870 tag: (this.href.length) ? 'a' : 'div',
34875 cls: 'masonry-brick-split-head',
34879 cls: 'masonry-brick-paragraph',
34886 cls: 'masonry-brick-split-body',
34892 if(this.href.length){
34893 cfg.href = this.href;
34896 if(this.title.length){
34897 cfg.cn[0].cn[0].cn.push({
34899 cls: 'masonry-brick-title',
34904 if(this.html.length){
34905 cfg.cn[1].cn.push({
34907 cls: 'masonry-brick-text',
34912 if(this.bgimage.length){
34913 cfg.cn[0].cn.push({
34915 cls: 'masonry-brick-image-view',
34920 if(this.videourl.length){
34921 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34922 // youtube support only?
34923 cfg.cn[0].cn.cn.push({
34925 cls: 'masonry-brick-image-view',
34928 allowfullscreen : true
34935 initEvents: function()
34937 switch (this.size) {
34970 this.el.on('touchstart', this.onTouchStart, this);
34971 this.el.on('touchmove', this.onTouchMove, this);
34972 this.el.on('touchend', this.onTouchEnd, this);
34973 this.el.on('contextmenu', this.onContextMenu, this);
34975 this.el.on('mouseenter' ,this.enter, this);
34976 this.el.on('mouseleave', this.leave, this);
34977 this.el.on('click', this.onClick, this);
34980 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34981 this.parent().bricks.push(this);
34986 onClick: function(e, el)
34988 var time = this.endTimer - this.startTimer;
34989 // Roo.log(e.preventDefault());
34992 e.preventDefault();
34997 if(!this.preventDefault){
35001 e.preventDefault();
35003 if (this.activeClass != '') {
35004 this.selectBrick();
35007 this.fireEvent('click', this, e);
35010 enter: function(e, el)
35012 e.preventDefault();
35014 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35018 if(this.bgimage.length && this.html.length){
35019 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35023 leave: function(e, el)
35025 e.preventDefault();
35027 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35031 if(this.bgimage.length && this.html.length){
35032 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35036 onTouchStart: function(e, el)
35038 // e.preventDefault();
35040 this.touchmoved = false;
35042 if(!this.isFitContainer){
35046 if(!this.bgimage.length || !this.html.length){
35050 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35052 this.timer = new Date().getTime();
35056 onTouchMove: function(e, el)
35058 this.touchmoved = true;
35061 onContextMenu : function(e,el)
35063 e.preventDefault();
35064 e.stopPropagation();
35068 onTouchEnd: function(e, el)
35070 // e.preventDefault();
35072 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35079 if(!this.bgimage.length || !this.html.length){
35081 if(this.href.length){
35082 window.location.href = this.href;
35088 if(!this.isFitContainer){
35092 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35094 window.location.href = this.href;
35097 //selection on single brick only
35098 selectBrick : function() {
35100 if (!this.parentId) {
35104 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35105 var index = m.selectedBrick.indexOf(this.id);
35108 m.selectedBrick.splice(index,1);
35109 this.el.removeClass(this.activeClass);
35113 for(var i = 0; i < m.selectedBrick.length; i++) {
35114 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35115 b.el.removeClass(b.activeClass);
35118 m.selectedBrick = [];
35120 m.selectedBrick.push(this.id);
35121 this.el.addClass(this.activeClass);
35125 isSelected : function(){
35126 return this.el.hasClass(this.activeClass);
35131 Roo.apply(Roo.bootstrap.MasonryBrick, {
35134 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35136 * register a Masonry Brick
35137 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35140 register : function(brick)
35142 //this.groups[brick.id] = brick;
35143 this.groups.add(brick.id, brick);
35146 * fetch a masonry brick based on the masonry brick ID
35147 * @param {string} the masonry brick to add
35148 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35151 get: function(brick_id)
35153 // if (typeof(this.groups[brick_id]) == 'undefined') {
35156 // return this.groups[brick_id] ;
35158 if(this.groups.key(brick_id)) {
35159 return this.groups.key(brick_id);
35177 * @class Roo.bootstrap.Brick
35178 * @extends Roo.bootstrap.Component
35179 * Bootstrap Brick class
35182 * Create a new Brick
35183 * @param {Object} config The config object
35186 Roo.bootstrap.Brick = function(config){
35187 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35193 * When a Brick is click
35194 * @param {Roo.bootstrap.Brick} this
35195 * @param {Roo.EventObject} e
35201 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35204 * @cfg {String} title
35208 * @cfg {String} html
35212 * @cfg {String} bgimage
35216 * @cfg {String} cls
35220 * @cfg {String} href
35224 * @cfg {String} video
35228 * @cfg {Boolean} square
35232 getAutoCreate : function()
35234 var cls = 'roo-brick';
35236 if(this.href.length){
35237 cls += ' roo-brick-link';
35240 if(this.bgimage.length){
35241 cls += ' roo-brick-image';
35244 if(!this.html.length && !this.bgimage.length){
35245 cls += ' roo-brick-center-title';
35248 if(!this.html.length && this.bgimage.length){
35249 cls += ' roo-brick-bottom-title';
35253 cls += ' ' + this.cls;
35257 tag: (this.href.length) ? 'a' : 'div',
35262 cls: 'roo-brick-paragraph',
35268 if(this.href.length){
35269 cfg.href = this.href;
35272 var cn = cfg.cn[0].cn;
35274 if(this.title.length){
35277 cls: 'roo-brick-title',
35282 if(this.html.length){
35285 cls: 'roo-brick-text',
35292 if(this.bgimage.length){
35295 cls: 'roo-brick-image-view',
35303 initEvents: function()
35305 if(this.title.length || this.html.length){
35306 this.el.on('mouseenter' ,this.enter, this);
35307 this.el.on('mouseleave', this.leave, this);
35310 Roo.EventManager.onWindowResize(this.resize, this);
35312 if(this.bgimage.length){
35313 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35314 this.imageEl.on('load', this.onImageLoad, this);
35321 onImageLoad : function()
35326 resize : function()
35328 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35330 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35332 if(this.bgimage.length){
35333 var image = this.el.select('.roo-brick-image-view', true).first();
35335 image.setWidth(paragraph.getWidth());
35338 image.setHeight(paragraph.getWidth());
35341 this.el.setHeight(image.getHeight());
35342 paragraph.setHeight(image.getHeight());
35348 enter: function(e, el)
35350 e.preventDefault();
35352 if(this.bgimage.length){
35353 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35354 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35358 leave: function(e, el)
35360 e.preventDefault();
35362 if(this.bgimage.length){
35363 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35364 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35379 * @class Roo.bootstrap.NumberField
35380 * @extends Roo.bootstrap.Input
35381 * Bootstrap NumberField class
35387 * Create a new NumberField
35388 * @param {Object} config The config object
35391 Roo.bootstrap.NumberField = function(config){
35392 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35395 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35398 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35400 allowDecimals : true,
35402 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35404 decimalSeparator : ".",
35406 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35408 decimalPrecision : 2,
35410 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35412 allowNegative : true,
35415 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35419 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35421 minValue : Number.NEGATIVE_INFINITY,
35423 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35425 maxValue : Number.MAX_VALUE,
35427 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35429 minText : "The minimum value for this field is {0}",
35431 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35433 maxText : "The maximum value for this field is {0}",
35435 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35436 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35438 nanText : "{0} is not a valid number",
35440 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35442 thousandsDelimiter : false,
35444 * @cfg {String} valueAlign alignment of value
35446 valueAlign : "left",
35448 getAutoCreate : function()
35450 var hiddenInput = {
35454 cls: 'hidden-number-input'
35458 hiddenInput.name = this.name;
35463 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35465 this.name = hiddenInput.name;
35467 if(cfg.cn.length > 0) {
35468 cfg.cn.push(hiddenInput);
35475 initEvents : function()
35477 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35479 var allowed = "0123456789";
35481 if(this.allowDecimals){
35482 allowed += this.decimalSeparator;
35485 if(this.allowNegative){
35489 if(this.thousandsDelimiter) {
35493 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35495 var keyPress = function(e){
35497 var k = e.getKey();
35499 var c = e.getCharCode();
35502 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35503 allowed.indexOf(String.fromCharCode(c)) === -1
35509 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35513 if(allowed.indexOf(String.fromCharCode(c)) === -1){
35518 this.el.on("keypress", keyPress, this);
35521 validateValue : function(value)
35524 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35528 var num = this.parseValue(value);
35531 this.markInvalid(String.format(this.nanText, value));
35535 if(num < this.minValue){
35536 this.markInvalid(String.format(this.minText, this.minValue));
35540 if(num > this.maxValue){
35541 this.markInvalid(String.format(this.maxText, this.maxValue));
35548 getValue : function()
35550 var v = this.hiddenEl().getValue();
35552 return this.fixPrecision(this.parseValue(v));
35555 parseValue : function(value)
35557 if(this.thousandsDelimiter) {
35559 r = new RegExp(",", "g");
35560 value = value.replace(r, "");
35563 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35564 return isNaN(value) ? '' : value;
35567 fixPrecision : function(value)
35569 if(this.thousandsDelimiter) {
35571 r = new RegExp(",", "g");
35572 value = value.replace(r, "");
35575 var nan = isNaN(value);
35577 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35578 return nan ? '' : value;
35580 return parseFloat(value).toFixed(this.decimalPrecision);
35583 setValue : function(v)
35585 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35591 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35593 this.inputEl().dom.value = (v == '') ? '' :
35594 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35596 if(!this.allowZero && v === '0') {
35597 this.hiddenEl().dom.value = '';
35598 this.inputEl().dom.value = '';
35605 decimalPrecisionFcn : function(v)
35607 return Math.floor(v);
35610 beforeBlur : function()
35612 var v = this.parseValue(this.getRawValue());
35614 if(v || v === 0 || v === ''){
35619 hiddenEl : function()
35621 return this.el.select('input.hidden-number-input',true).first();
35633 * @class Roo.bootstrap.DocumentSlider
35634 * @extends Roo.bootstrap.Component
35635 * Bootstrap DocumentSlider class
35638 * Create a new DocumentViewer
35639 * @param {Object} config The config object
35642 Roo.bootstrap.DocumentSlider = function(config){
35643 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35650 * Fire after initEvent
35651 * @param {Roo.bootstrap.DocumentSlider} this
35656 * Fire after update
35657 * @param {Roo.bootstrap.DocumentSlider} this
35663 * @param {Roo.bootstrap.DocumentSlider} this
35669 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35675 getAutoCreate : function()
35679 cls : 'roo-document-slider',
35683 cls : 'roo-document-slider-header',
35687 cls : 'roo-document-slider-header-title'
35693 cls : 'roo-document-slider-body',
35697 cls : 'roo-document-slider-prev',
35701 cls : 'fa fa-chevron-left'
35707 cls : 'roo-document-slider-thumb',
35711 cls : 'roo-document-slider-image'
35717 cls : 'roo-document-slider-next',
35721 cls : 'fa fa-chevron-right'
35733 initEvents : function()
35735 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35736 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35738 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35739 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35741 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35742 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35744 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35745 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35747 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35748 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35750 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35751 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35753 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35754 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35756 this.thumbEl.on('click', this.onClick, this);
35758 this.prevIndicator.on('click', this.prev, this);
35760 this.nextIndicator.on('click', this.next, this);
35764 initial : function()
35766 if(this.files.length){
35767 this.indicator = 1;
35771 this.fireEvent('initial', this);
35774 update : function()
35776 this.imageEl.attr('src', this.files[this.indicator - 1]);
35778 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35780 this.prevIndicator.show();
35782 if(this.indicator == 1){
35783 this.prevIndicator.hide();
35786 this.nextIndicator.show();
35788 if(this.indicator == this.files.length){
35789 this.nextIndicator.hide();
35792 this.thumbEl.scrollTo('top');
35794 this.fireEvent('update', this);
35797 onClick : function(e)
35799 e.preventDefault();
35801 this.fireEvent('click', this);
35806 e.preventDefault();
35808 this.indicator = Math.max(1, this.indicator - 1);
35815 e.preventDefault();
35817 this.indicator = Math.min(this.files.length, this.indicator + 1);
35831 * @class Roo.bootstrap.RadioSet
35832 * @extends Roo.bootstrap.Input
35833 * Bootstrap RadioSet class
35834 * @cfg {String} indicatorpos (left|right) default left
35835 * @cfg {Boolean} inline (true|false) inline the element (default true)
35836 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35838 * Create a new RadioSet
35839 * @param {Object} config The config object
35842 Roo.bootstrap.RadioSet = function(config){
35844 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35848 Roo.bootstrap.RadioSet.register(this);
35853 * Fires when the element is checked or unchecked.
35854 * @param {Roo.bootstrap.RadioSet} this This radio
35855 * @param {Roo.bootstrap.Radio} item The checked item
35860 * Fires when the element is click.
35861 * @param {Roo.bootstrap.RadioSet} this This radio set
35862 * @param {Roo.bootstrap.Radio} item The checked item
35863 * @param {Roo.EventObject} e The event object
35870 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
35878 indicatorpos : 'left',
35880 getAutoCreate : function()
35884 cls : 'roo-radio-set-label',
35888 html : this.fieldLabel
35892 if (Roo.bootstrap.version == 3) {
35895 if(this.indicatorpos == 'left'){
35898 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35899 tooltip : 'This field is required'
35904 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35905 tooltip : 'This field is required'
35911 cls : 'roo-radio-set-items'
35914 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35916 if (align === 'left' && this.fieldLabel.length) {
35919 cls : "roo-radio-set-right",
35925 if(this.labelWidth > 12){
35926 label.style = "width: " + this.labelWidth + 'px';
35929 if(this.labelWidth < 13 && this.labelmd == 0){
35930 this.labelmd = this.labelWidth;
35933 if(this.labellg > 0){
35934 label.cls += ' col-lg-' + this.labellg;
35935 items.cls += ' col-lg-' + (12 - this.labellg);
35938 if(this.labelmd > 0){
35939 label.cls += ' col-md-' + this.labelmd;
35940 items.cls += ' col-md-' + (12 - this.labelmd);
35943 if(this.labelsm > 0){
35944 label.cls += ' col-sm-' + this.labelsm;
35945 items.cls += ' col-sm-' + (12 - this.labelsm);
35948 if(this.labelxs > 0){
35949 label.cls += ' col-xs-' + this.labelxs;
35950 items.cls += ' col-xs-' + (12 - this.labelxs);
35956 cls : 'roo-radio-set',
35960 cls : 'roo-radio-set-input',
35963 value : this.value ? this.value : ''
35970 if(this.weight.length){
35971 cfg.cls += ' roo-radio-' + this.weight;
35975 cfg.cls += ' roo-radio-set-inline';
35979 ['xs','sm','md','lg'].map(function(size){
35980 if (settings[size]) {
35981 cfg.cls += ' col-' + size + '-' + settings[size];
35989 initEvents : function()
35991 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35992 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35994 if(!this.fieldLabel.length){
35995 this.labelEl.hide();
35998 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35999 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36001 this.indicator = this.indicatorEl();
36003 if(this.indicator){
36004 this.indicator.addClass('invisible');
36007 this.originalValue = this.getValue();
36011 inputEl: function ()
36013 return this.el.select('.roo-radio-set-input', true).first();
36016 getChildContainer : function()
36018 return this.itemsEl;
36021 register : function(item)
36023 this.radioes.push(item);
36027 validate : function()
36029 if(this.getVisibilityEl().hasClass('hidden')){
36035 Roo.each(this.radioes, function(i){
36044 if(this.allowBlank) {
36048 if(this.disabled || valid){
36053 this.markInvalid();
36058 markValid : function()
36060 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36061 this.indicatorEl().removeClass('visible');
36062 this.indicatorEl().addClass('invisible');
36066 if (Roo.bootstrap.version == 3) {
36067 this.el.removeClass([this.invalidClass, this.validClass]);
36068 this.el.addClass(this.validClass);
36070 this.el.removeClass(['is-invalid','is-valid']);
36071 this.el.addClass(['is-valid']);
36073 this.fireEvent('valid', this);
36076 markInvalid : function(msg)
36078 if(this.allowBlank || this.disabled){
36082 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36083 this.indicatorEl().removeClass('invisible');
36084 this.indicatorEl().addClass('visible');
36086 if (Roo.bootstrap.version == 3) {
36087 this.el.removeClass([this.invalidClass, this.validClass]);
36088 this.el.addClass(this.invalidClass);
36090 this.el.removeClass(['is-invalid','is-valid']);
36091 this.el.addClass(['is-invalid']);
36094 this.fireEvent('invalid', this, msg);
36098 setValue : function(v, suppressEvent)
36100 if(this.value === v){
36107 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36110 Roo.each(this.radioes, function(i){
36112 i.el.removeClass('checked');
36115 Roo.each(this.radioes, function(i){
36117 if(i.value === v || i.value.toString() === v.toString()){
36119 i.el.addClass('checked');
36121 if(suppressEvent !== true){
36122 this.fireEvent('check', this, i);
36133 clearInvalid : function(){
36135 if(!this.el || this.preventMark){
36139 this.el.removeClass([this.invalidClass]);
36141 this.fireEvent('valid', this);
36146 Roo.apply(Roo.bootstrap.RadioSet, {
36150 register : function(set)
36152 this.groups[set.name] = set;
36155 get: function(name)
36157 if (typeof(this.groups[name]) == 'undefined') {
36161 return this.groups[name] ;
36167 * Ext JS Library 1.1.1
36168 * Copyright(c) 2006-2007, Ext JS, LLC.
36170 * Originally Released Under LGPL - original licence link has changed is not relivant.
36173 * <script type="text/javascript">
36178 * @class Roo.bootstrap.SplitBar
36179 * @extends Roo.util.Observable
36180 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36184 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36185 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36186 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36187 split.minSize = 100;
36188 split.maxSize = 600;
36189 split.animate = true;
36190 split.on('moved', splitterMoved);
36193 * Create a new SplitBar
36194 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36195 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36196 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36197 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36198 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36199 position of the SplitBar).
36201 Roo.bootstrap.SplitBar = function(cfg){
36206 // dragElement : elm
36207 // resizingElement: el,
36209 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36210 // placement : Roo.bootstrap.SplitBar.LEFT ,
36211 // existingProxy ???
36214 this.el = Roo.get(cfg.dragElement, true);
36215 this.el.dom.unselectable = "on";
36217 this.resizingEl = Roo.get(cfg.resizingElement, true);
36221 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36222 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36225 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36228 * The minimum size of the resizing element. (Defaults to 0)
36234 * The maximum size of the resizing element. (Defaults to 2000)
36237 this.maxSize = 2000;
36240 * Whether to animate the transition to the new size
36243 this.animate = false;
36246 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36249 this.useShim = false;
36254 if(!cfg.existingProxy){
36256 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36258 this.proxy = Roo.get(cfg.existingProxy).dom;
36261 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36264 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36267 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36270 this.dragSpecs = {};
36273 * @private The adapter to use to positon and resize elements
36275 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36276 this.adapter.init(this);
36278 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36280 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36281 this.el.addClass("roo-splitbar-h");
36284 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36285 this.el.addClass("roo-splitbar-v");
36291 * Fires when the splitter is moved (alias for {@link #event-moved})
36292 * @param {Roo.bootstrap.SplitBar} this
36293 * @param {Number} newSize the new width or height
36298 * Fires when the splitter is moved
36299 * @param {Roo.bootstrap.SplitBar} this
36300 * @param {Number} newSize the new width or height
36304 * @event beforeresize
36305 * Fires before the splitter is dragged
36306 * @param {Roo.bootstrap.SplitBar} this
36308 "beforeresize" : true,
36310 "beforeapply" : true
36313 Roo.util.Observable.call(this);
36316 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36317 onStartProxyDrag : function(x, y){
36318 this.fireEvent("beforeresize", this);
36320 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36322 o.enableDisplayMode("block");
36323 // all splitbars share the same overlay
36324 Roo.bootstrap.SplitBar.prototype.overlay = o;
36326 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36327 this.overlay.show();
36328 Roo.get(this.proxy).setDisplayed("block");
36329 var size = this.adapter.getElementSize(this);
36330 this.activeMinSize = this.getMinimumSize();;
36331 this.activeMaxSize = this.getMaximumSize();;
36332 var c1 = size - this.activeMinSize;
36333 var c2 = Math.max(this.activeMaxSize - size, 0);
36334 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36335 this.dd.resetConstraints();
36336 this.dd.setXConstraint(
36337 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36338 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36340 this.dd.setYConstraint(0, 0);
36342 this.dd.resetConstraints();
36343 this.dd.setXConstraint(0, 0);
36344 this.dd.setYConstraint(
36345 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36346 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36349 this.dragSpecs.startSize = size;
36350 this.dragSpecs.startPoint = [x, y];
36351 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36355 * @private Called after the drag operation by the DDProxy
36357 onEndProxyDrag : function(e){
36358 Roo.get(this.proxy).setDisplayed(false);
36359 var endPoint = Roo.lib.Event.getXY(e);
36361 this.overlay.hide();
36364 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36365 newSize = this.dragSpecs.startSize +
36366 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36367 endPoint[0] - this.dragSpecs.startPoint[0] :
36368 this.dragSpecs.startPoint[0] - endPoint[0]
36371 newSize = this.dragSpecs.startSize +
36372 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36373 endPoint[1] - this.dragSpecs.startPoint[1] :
36374 this.dragSpecs.startPoint[1] - endPoint[1]
36377 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36378 if(newSize != this.dragSpecs.startSize){
36379 if(this.fireEvent('beforeapply', this, newSize) !== false){
36380 this.adapter.setElementSize(this, newSize);
36381 this.fireEvent("moved", this, newSize);
36382 this.fireEvent("resize", this, newSize);
36388 * Get the adapter this SplitBar uses
36389 * @return The adapter object
36391 getAdapter : function(){
36392 return this.adapter;
36396 * Set the adapter this SplitBar uses
36397 * @param {Object} adapter A SplitBar adapter object
36399 setAdapter : function(adapter){
36400 this.adapter = adapter;
36401 this.adapter.init(this);
36405 * Gets the minimum size for the resizing element
36406 * @return {Number} The minimum size
36408 getMinimumSize : function(){
36409 return this.minSize;
36413 * Sets the minimum size for the resizing element
36414 * @param {Number} minSize The minimum size
36416 setMinimumSize : function(minSize){
36417 this.minSize = minSize;
36421 * Gets the maximum size for the resizing element
36422 * @return {Number} The maximum size
36424 getMaximumSize : function(){
36425 return this.maxSize;
36429 * Sets the maximum size for the resizing element
36430 * @param {Number} maxSize The maximum size
36432 setMaximumSize : function(maxSize){
36433 this.maxSize = maxSize;
36437 * Sets the initialize size for the resizing element
36438 * @param {Number} size The initial size
36440 setCurrentSize : function(size){
36441 var oldAnimate = this.animate;
36442 this.animate = false;
36443 this.adapter.setElementSize(this, size);
36444 this.animate = oldAnimate;
36448 * Destroy this splitbar.
36449 * @param {Boolean} removeEl True to remove the element
36451 destroy : function(removeEl){
36453 this.shim.remove();
36456 this.proxy.parentNode.removeChild(this.proxy);
36464 * @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.
36466 Roo.bootstrap.SplitBar.createProxy = function(dir){
36467 var proxy = new Roo.Element(document.createElement("div"));
36468 proxy.unselectable();
36469 var cls = 'roo-splitbar-proxy';
36470 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36471 document.body.appendChild(proxy.dom);
36476 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36477 * Default Adapter. It assumes the splitter and resizing element are not positioned
36478 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36480 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36483 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36484 // do nothing for now
36485 init : function(s){
36489 * Called before drag operations to get the current size of the resizing element.
36490 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36492 getElementSize : function(s){
36493 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36494 return s.resizingEl.getWidth();
36496 return s.resizingEl.getHeight();
36501 * Called after drag operations to set the size of the resizing element.
36502 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36503 * @param {Number} newSize The new size to set
36504 * @param {Function} onComplete A function to be invoked when resizing is complete
36506 setElementSize : function(s, newSize, onComplete){
36507 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36509 s.resizingEl.setWidth(newSize);
36511 onComplete(s, newSize);
36514 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36519 s.resizingEl.setHeight(newSize);
36521 onComplete(s, newSize);
36524 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36531 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36532 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36533 * Adapter that moves the splitter element to align with the resized sizing element.
36534 * Used with an absolute positioned SplitBar.
36535 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36536 * document.body, make sure you assign an id to the body element.
36538 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36539 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36540 this.container = Roo.get(container);
36543 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36544 init : function(s){
36545 this.basic.init(s);
36548 getElementSize : function(s){
36549 return this.basic.getElementSize(s);
36552 setElementSize : function(s, newSize, onComplete){
36553 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36556 moveSplitter : function(s){
36557 var yes = Roo.bootstrap.SplitBar;
36558 switch(s.placement){
36560 s.el.setX(s.resizingEl.getRight());
36563 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36566 s.el.setY(s.resizingEl.getBottom());
36569 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36576 * Orientation constant - Create a vertical SplitBar
36580 Roo.bootstrap.SplitBar.VERTICAL = 1;
36583 * Orientation constant - Create a horizontal SplitBar
36587 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36590 * Placement constant - The resizing element is to the left of the splitter element
36594 Roo.bootstrap.SplitBar.LEFT = 1;
36597 * Placement constant - The resizing element is to the right of the splitter element
36601 Roo.bootstrap.SplitBar.RIGHT = 2;
36604 * Placement constant - The resizing element is positioned above the splitter element
36608 Roo.bootstrap.SplitBar.TOP = 3;
36611 * Placement constant - The resizing element is positioned under splitter element
36615 Roo.bootstrap.SplitBar.BOTTOM = 4;
36616 Roo.namespace("Roo.bootstrap.layout");/*
36618 * Ext JS Library 1.1.1
36619 * Copyright(c) 2006-2007, Ext JS, LLC.
36621 * Originally Released Under LGPL - original licence link has changed is not relivant.
36624 * <script type="text/javascript">
36628 * @class Roo.bootstrap.layout.Manager
36629 * @extends Roo.bootstrap.Component
36630 * Base class for layout managers.
36632 Roo.bootstrap.layout.Manager = function(config)
36634 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36640 /** false to disable window resize monitoring @type Boolean */
36641 this.monitorWindowResize = true;
36646 * Fires when a layout is performed.
36647 * @param {Roo.LayoutManager} this
36651 * @event regionresized
36652 * Fires when the user resizes a region.
36653 * @param {Roo.LayoutRegion} region The resized region
36654 * @param {Number} newSize The new size (width for east/west, height for north/south)
36656 "regionresized" : true,
36658 * @event regioncollapsed
36659 * Fires when a region is collapsed.
36660 * @param {Roo.LayoutRegion} region The collapsed region
36662 "regioncollapsed" : true,
36664 * @event regionexpanded
36665 * Fires when a region is expanded.
36666 * @param {Roo.LayoutRegion} region The expanded region
36668 "regionexpanded" : true
36670 this.updating = false;
36673 this.el = Roo.get(config.el);
36679 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36684 monitorWindowResize : true,
36690 onRender : function(ct, position)
36693 this.el = Roo.get(ct);
36696 //this.fireEvent('render',this);
36700 initEvents: function()
36704 // ie scrollbar fix
36705 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36706 document.body.scroll = "no";
36707 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36708 this.el.position('relative');
36710 this.id = this.el.id;
36711 this.el.addClass("roo-layout-container");
36712 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36713 if(this.el.dom != document.body ) {
36714 this.el.on('resize', this.layout,this);
36715 this.el.on('show', this.layout,this);
36721 * Returns true if this layout is currently being updated
36722 * @return {Boolean}
36724 isUpdating : function(){
36725 return this.updating;
36729 * Suspend the LayoutManager from doing auto-layouts while
36730 * making multiple add or remove calls
36732 beginUpdate : function(){
36733 this.updating = true;
36737 * Restore auto-layouts and optionally disable the manager from performing a layout
36738 * @param {Boolean} noLayout true to disable a layout update
36740 endUpdate : function(noLayout){
36741 this.updating = false;
36747 layout: function(){
36751 onRegionResized : function(region, newSize){
36752 this.fireEvent("regionresized", region, newSize);
36756 onRegionCollapsed : function(region){
36757 this.fireEvent("regioncollapsed", region);
36760 onRegionExpanded : function(region){
36761 this.fireEvent("regionexpanded", region);
36765 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36766 * performs box-model adjustments.
36767 * @return {Object} The size as an object {width: (the width), height: (the height)}
36769 getViewSize : function()
36772 if(this.el.dom != document.body){
36773 size = this.el.getSize();
36775 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36777 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36778 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36783 * Returns the Element this layout is bound to.
36784 * @return {Roo.Element}
36786 getEl : function(){
36791 * Returns the specified region.
36792 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36793 * @return {Roo.LayoutRegion}
36795 getRegion : function(target){
36796 return this.regions[target.toLowerCase()];
36799 onWindowResize : function(){
36800 if(this.monitorWindowResize){
36807 * Ext JS Library 1.1.1
36808 * Copyright(c) 2006-2007, Ext JS, LLC.
36810 * Originally Released Under LGPL - original licence link has changed is not relivant.
36813 * <script type="text/javascript">
36816 * @class Roo.bootstrap.layout.Border
36817 * @extends Roo.bootstrap.layout.Manager
36818 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36819 * please see: examples/bootstrap/nested.html<br><br>
36821 <b>The container the layout is rendered into can be either the body element or any other element.
36822 If it is not the body element, the container needs to either be an absolute positioned element,
36823 or you will need to add "position:relative" to the css of the container. You will also need to specify
36824 the container size if it is not the body element.</b>
36827 * Create a new Border
36828 * @param {Object} config Configuration options
36830 Roo.bootstrap.layout.Border = function(config){
36831 config = config || {};
36832 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36836 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36837 if(config[region]){
36838 config[region].region = region;
36839 this.addRegion(config[region]);
36845 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
36847 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36849 parent : false, // this might point to a 'nest' or a ???
36852 * Creates and adds a new region if it doesn't already exist.
36853 * @param {String} target The target region key (north, south, east, west or center).
36854 * @param {Object} config The regions config object
36855 * @return {BorderLayoutRegion} The new region
36857 addRegion : function(config)
36859 if(!this.regions[config.region]){
36860 var r = this.factory(config);
36861 this.bindRegion(r);
36863 return this.regions[config.region];
36867 bindRegion : function(r){
36868 this.regions[r.config.region] = r;
36870 r.on("visibilitychange", this.layout, this);
36871 r.on("paneladded", this.layout, this);
36872 r.on("panelremoved", this.layout, this);
36873 r.on("invalidated", this.layout, this);
36874 r.on("resized", this.onRegionResized, this);
36875 r.on("collapsed", this.onRegionCollapsed, this);
36876 r.on("expanded", this.onRegionExpanded, this);
36880 * Performs a layout update.
36882 layout : function()
36884 if(this.updating) {
36888 // render all the rebions if they have not been done alreayd?
36889 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36890 if(this.regions[region] && !this.regions[region].bodyEl){
36891 this.regions[region].onRender(this.el)
36895 var size = this.getViewSize();
36896 var w = size.width;
36897 var h = size.height;
36902 //var x = 0, y = 0;
36904 var rs = this.regions;
36905 var north = rs["north"];
36906 var south = rs["south"];
36907 var west = rs["west"];
36908 var east = rs["east"];
36909 var center = rs["center"];
36910 //if(this.hideOnLayout){ // not supported anymore
36911 //c.el.setStyle("display", "none");
36913 if(north && north.isVisible()){
36914 var b = north.getBox();
36915 var m = north.getMargins();
36916 b.width = w - (m.left+m.right);
36919 centerY = b.height + b.y + m.bottom;
36920 centerH -= centerY;
36921 north.updateBox(this.safeBox(b));
36923 if(south && south.isVisible()){
36924 var b = south.getBox();
36925 var m = south.getMargins();
36926 b.width = w - (m.left+m.right);
36928 var totalHeight = (b.height + m.top + m.bottom);
36929 b.y = h - totalHeight + m.top;
36930 centerH -= totalHeight;
36931 south.updateBox(this.safeBox(b));
36933 if(west && west.isVisible()){
36934 var b = west.getBox();
36935 var m = west.getMargins();
36936 b.height = centerH - (m.top+m.bottom);
36938 b.y = centerY + m.top;
36939 var totalWidth = (b.width + m.left + m.right);
36940 centerX += totalWidth;
36941 centerW -= totalWidth;
36942 west.updateBox(this.safeBox(b));
36944 if(east && east.isVisible()){
36945 var b = east.getBox();
36946 var m = east.getMargins();
36947 b.height = centerH - (m.top+m.bottom);
36948 var totalWidth = (b.width + m.left + m.right);
36949 b.x = w - totalWidth + m.left;
36950 b.y = centerY + m.top;
36951 centerW -= totalWidth;
36952 east.updateBox(this.safeBox(b));
36955 var m = center.getMargins();
36957 x: centerX + m.left,
36958 y: centerY + m.top,
36959 width: centerW - (m.left+m.right),
36960 height: centerH - (m.top+m.bottom)
36962 //if(this.hideOnLayout){
36963 //center.el.setStyle("display", "block");
36965 center.updateBox(this.safeBox(centerBox));
36968 this.fireEvent("layout", this);
36972 safeBox : function(box){
36973 box.width = Math.max(0, box.width);
36974 box.height = Math.max(0, box.height);
36979 * Adds a ContentPanel (or subclass) to this layout.
36980 * @param {String} target The target region key (north, south, east, west or center).
36981 * @param {Roo.ContentPanel} panel The panel to add
36982 * @return {Roo.ContentPanel} The added panel
36984 add : function(target, panel){
36986 target = target.toLowerCase();
36987 return this.regions[target].add(panel);
36991 * Remove a ContentPanel (or subclass) to this layout.
36992 * @param {String} target The target region key (north, south, east, west or center).
36993 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36994 * @return {Roo.ContentPanel} The removed panel
36996 remove : function(target, panel){
36997 target = target.toLowerCase();
36998 return this.regions[target].remove(panel);
37002 * Searches all regions for a panel with the specified id
37003 * @param {String} panelId
37004 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37006 findPanel : function(panelId){
37007 var rs = this.regions;
37008 for(var target in rs){
37009 if(typeof rs[target] != "function"){
37010 var p = rs[target].getPanel(panelId);
37020 * Searches all regions for a panel with the specified id and activates (shows) it.
37021 * @param {String/ContentPanel} panelId The panels id or the panel itself
37022 * @return {Roo.ContentPanel} The shown panel or null
37024 showPanel : function(panelId) {
37025 var rs = this.regions;
37026 for(var target in rs){
37027 var r = rs[target];
37028 if(typeof r != "function"){
37029 if(r.hasPanel(panelId)){
37030 return r.showPanel(panelId);
37038 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37039 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37042 restoreState : function(provider){
37044 provider = Roo.state.Manager;
37046 var sm = new Roo.LayoutStateManager();
37047 sm.init(this, provider);
37053 * Adds a xtype elements to the layout.
37057 xtype : 'ContentPanel',
37064 xtype : 'NestedLayoutPanel',
37070 items : [ ... list of content panels or nested layout panels.. ]
37074 * @param {Object} cfg Xtype definition of item to add.
37076 addxtype : function(cfg)
37078 // basically accepts a pannel...
37079 // can accept a layout region..!?!?
37080 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37083 // theory? children can only be panels??
37085 //if (!cfg.xtype.match(/Panel$/)) {
37090 if (typeof(cfg.region) == 'undefined') {
37091 Roo.log("Failed to add Panel, region was not set");
37095 var region = cfg.region;
37101 xitems = cfg.items;
37106 if ( region == 'center') {
37107 Roo.log("Center: " + cfg.title);
37113 case 'Content': // ContentPanel (el, cfg)
37114 case 'Scroll': // ContentPanel (el, cfg)
37116 cfg.autoCreate = cfg.autoCreate || true;
37117 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37119 // var el = this.el.createChild();
37120 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37123 this.add(region, ret);
37127 case 'TreePanel': // our new panel!
37128 cfg.el = this.el.createChild();
37129 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37130 this.add(region, ret);
37135 // create a new Layout (which is a Border Layout...
37137 var clayout = cfg.layout;
37138 clayout.el = this.el.createChild();
37139 clayout.items = clayout.items || [];
37143 // replace this exitems with the clayout ones..
37144 xitems = clayout.items;
37146 // force background off if it's in center...
37147 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37148 cfg.background = false;
37150 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37153 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37154 //console.log('adding nested layout panel ' + cfg.toSource());
37155 this.add(region, ret);
37156 nb = {}; /// find first...
37161 // needs grid and region
37163 //var el = this.getRegion(region).el.createChild();
37165 *var el = this.el.createChild();
37166 // create the grid first...
37167 cfg.grid.container = el;
37168 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37171 if (region == 'center' && this.active ) {
37172 cfg.background = false;
37175 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37177 this.add(region, ret);
37179 if (cfg.background) {
37180 // render grid on panel activation (if panel background)
37181 ret.on('activate', function(gp) {
37182 if (!gp.grid.rendered) {
37183 // gp.grid.render(el);
37187 // cfg.grid.render(el);
37193 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37194 // it was the old xcomponent building that caused this before.
37195 // espeically if border is the top element in the tree.
37205 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37207 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37208 this.add(region, ret);
37212 throw "Can not add '" + cfg.xtype + "' to Border";
37218 this.beginUpdate();
37222 Roo.each(xitems, function(i) {
37223 region = nb && i.region ? i.region : false;
37225 var add = ret.addxtype(i);
37228 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37229 if (!i.background) {
37230 abn[region] = nb[region] ;
37237 // make the last non-background panel active..
37238 //if (nb) { Roo.log(abn); }
37241 for(var r in abn) {
37242 region = this.getRegion(r);
37244 // tried using nb[r], but it does not work..
37246 region.showPanel(abn[r]);
37257 factory : function(cfg)
37260 var validRegions = Roo.bootstrap.layout.Border.regions;
37262 var target = cfg.region;
37265 var r = Roo.bootstrap.layout;
37269 return new r.North(cfg);
37271 return new r.South(cfg);
37273 return new r.East(cfg);
37275 return new r.West(cfg);
37277 return new r.Center(cfg);
37279 throw 'Layout region "'+target+'" not supported.';
37286 * Ext JS Library 1.1.1
37287 * Copyright(c) 2006-2007, Ext JS, LLC.
37289 * Originally Released Under LGPL - original licence link has changed is not relivant.
37292 * <script type="text/javascript">
37296 * @class Roo.bootstrap.layout.Basic
37297 * @extends Roo.util.Observable
37298 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37299 * and does not have a titlebar, tabs or any other features. All it does is size and position
37300 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37301 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37302 * @cfg {string} region the region that it inhabits..
37303 * @cfg {bool} skipConfig skip config?
37307 Roo.bootstrap.layout.Basic = function(config){
37309 this.mgr = config.mgr;
37311 this.position = config.region;
37313 var skipConfig = config.skipConfig;
37317 * @scope Roo.BasicLayoutRegion
37321 * @event beforeremove
37322 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37323 * @param {Roo.LayoutRegion} this
37324 * @param {Roo.ContentPanel} panel The panel
37325 * @param {Object} e The cancel event object
37327 "beforeremove" : true,
37329 * @event invalidated
37330 * Fires when the layout for this region is changed.
37331 * @param {Roo.LayoutRegion} this
37333 "invalidated" : true,
37335 * @event visibilitychange
37336 * Fires when this region is shown or hidden
37337 * @param {Roo.LayoutRegion} this
37338 * @param {Boolean} visibility true or false
37340 "visibilitychange" : true,
37342 * @event paneladded
37343 * Fires when a panel is added.
37344 * @param {Roo.LayoutRegion} this
37345 * @param {Roo.ContentPanel} panel The panel
37347 "paneladded" : true,
37349 * @event panelremoved
37350 * Fires when a panel is removed.
37351 * @param {Roo.LayoutRegion} this
37352 * @param {Roo.ContentPanel} panel The panel
37354 "panelremoved" : true,
37356 * @event beforecollapse
37357 * Fires when this region before collapse.
37358 * @param {Roo.LayoutRegion} this
37360 "beforecollapse" : true,
37363 * Fires when this region is collapsed.
37364 * @param {Roo.LayoutRegion} this
37366 "collapsed" : true,
37369 * Fires when this region is expanded.
37370 * @param {Roo.LayoutRegion} this
37375 * Fires when this region is slid into view.
37376 * @param {Roo.LayoutRegion} this
37378 "slideshow" : true,
37381 * Fires when this region slides out of view.
37382 * @param {Roo.LayoutRegion} this
37384 "slidehide" : true,
37386 * @event panelactivated
37387 * Fires when a panel is activated.
37388 * @param {Roo.LayoutRegion} this
37389 * @param {Roo.ContentPanel} panel The activated panel
37391 "panelactivated" : true,
37394 * Fires when the user resizes this region.
37395 * @param {Roo.LayoutRegion} this
37396 * @param {Number} newSize The new size (width for east/west, height for north/south)
37400 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37401 this.panels = new Roo.util.MixedCollection();
37402 this.panels.getKey = this.getPanelId.createDelegate(this);
37404 this.activePanel = null;
37405 // ensure listeners are added...
37407 if (config.listeners || config.events) {
37408 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37409 listeners : config.listeners || {},
37410 events : config.events || {}
37414 if(skipConfig !== true){
37415 this.applyConfig(config);
37419 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37421 getPanelId : function(p){
37425 applyConfig : function(config){
37426 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37427 this.config = config;
37432 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37433 * the width, for horizontal (north, south) the height.
37434 * @param {Number} newSize The new width or height
37436 resizeTo : function(newSize){
37437 var el = this.el ? this.el :
37438 (this.activePanel ? this.activePanel.getEl() : null);
37440 switch(this.position){
37443 el.setWidth(newSize);
37444 this.fireEvent("resized", this, newSize);
37448 el.setHeight(newSize);
37449 this.fireEvent("resized", this, newSize);
37455 getBox : function(){
37456 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37459 getMargins : function(){
37460 return this.margins;
37463 updateBox : function(box){
37465 var el = this.activePanel.getEl();
37466 el.dom.style.left = box.x + "px";
37467 el.dom.style.top = box.y + "px";
37468 this.activePanel.setSize(box.width, box.height);
37472 * Returns the container element for this region.
37473 * @return {Roo.Element}
37475 getEl : function(){
37476 return this.activePanel;
37480 * Returns true if this region is currently visible.
37481 * @return {Boolean}
37483 isVisible : function(){
37484 return this.activePanel ? true : false;
37487 setActivePanel : function(panel){
37488 panel = this.getPanel(panel);
37489 if(this.activePanel && this.activePanel != panel){
37490 this.activePanel.setActiveState(false);
37491 this.activePanel.getEl().setLeftTop(-10000,-10000);
37493 this.activePanel = panel;
37494 panel.setActiveState(true);
37496 panel.setSize(this.box.width, this.box.height);
37498 this.fireEvent("panelactivated", this, panel);
37499 this.fireEvent("invalidated");
37503 * Show the specified panel.
37504 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37505 * @return {Roo.ContentPanel} The shown panel or null
37507 showPanel : function(panel){
37508 panel = this.getPanel(panel);
37510 this.setActivePanel(panel);
37516 * Get the active panel for this region.
37517 * @return {Roo.ContentPanel} The active panel or null
37519 getActivePanel : function(){
37520 return this.activePanel;
37524 * Add the passed ContentPanel(s)
37525 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37526 * @return {Roo.ContentPanel} The panel added (if only one was added)
37528 add : function(panel){
37529 if(arguments.length > 1){
37530 for(var i = 0, len = arguments.length; i < len; i++) {
37531 this.add(arguments[i]);
37535 if(this.hasPanel(panel)){
37536 this.showPanel(panel);
37539 var el = panel.getEl();
37540 if(el.dom.parentNode != this.mgr.el.dom){
37541 this.mgr.el.dom.appendChild(el.dom);
37543 if(panel.setRegion){
37544 panel.setRegion(this);
37546 this.panels.add(panel);
37547 el.setStyle("position", "absolute");
37548 if(!panel.background){
37549 this.setActivePanel(panel);
37550 if(this.config.initialSize && this.panels.getCount()==1){
37551 this.resizeTo(this.config.initialSize);
37554 this.fireEvent("paneladded", this, panel);
37559 * Returns true if the panel is in this region.
37560 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37561 * @return {Boolean}
37563 hasPanel : function(panel){
37564 if(typeof panel == "object"){ // must be panel obj
37565 panel = panel.getId();
37567 return this.getPanel(panel) ? true : false;
37571 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37572 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37573 * @param {Boolean} preservePanel Overrides the config preservePanel option
37574 * @return {Roo.ContentPanel} The panel that was removed
37576 remove : function(panel, preservePanel){
37577 panel = this.getPanel(panel);
37582 this.fireEvent("beforeremove", this, panel, e);
37583 if(e.cancel === true){
37586 var panelId = panel.getId();
37587 this.panels.removeKey(panelId);
37592 * Returns the panel specified or null if it's not in this region.
37593 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37594 * @return {Roo.ContentPanel}
37596 getPanel : function(id){
37597 if(typeof id == "object"){ // must be panel obj
37600 return this.panels.get(id);
37604 * Returns this regions position (north/south/east/west/center).
37607 getPosition: function(){
37608 return this.position;
37612 * Ext JS Library 1.1.1
37613 * Copyright(c) 2006-2007, Ext JS, LLC.
37615 * Originally Released Under LGPL - original licence link has changed is not relivant.
37618 * <script type="text/javascript">
37622 * @class Roo.bootstrap.layout.Region
37623 * @extends Roo.bootstrap.layout.Basic
37624 * This class represents a region in a layout manager.
37626 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37627 * @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})
37628 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
37629 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
37630 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
37631 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
37632 * @cfg {String} title The title for the region (overrides panel titles)
37633 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
37634 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37635 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
37636 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37637 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37638 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37639 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37640 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37641 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37642 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37644 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37645 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37646 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37647 * @cfg {Number} width For East/West panels
37648 * @cfg {Number} height For North/South panels
37649 * @cfg {Boolean} split To show the splitter
37650 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37652 * @cfg {string} cls Extra CSS classes to add to region
37654 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37655 * @cfg {string} region the region that it inhabits..
37658 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37659 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37661 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37662 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37663 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37665 Roo.bootstrap.layout.Region = function(config)
37667 this.applyConfig(config);
37669 var mgr = config.mgr;
37670 var pos = config.region;
37671 config.skipConfig = true;
37672 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37675 this.onRender(mgr.el);
37678 this.visible = true;
37679 this.collapsed = false;
37680 this.unrendered_panels = [];
37683 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37685 position: '', // set by wrapper (eg. north/south etc..)
37686 unrendered_panels : null, // unrendered panels.
37688 tabPosition : false,
37690 mgr: false, // points to 'Border'
37693 createBody : function(){
37694 /** This region's body element
37695 * @type Roo.Element */
37696 this.bodyEl = this.el.createChild({
37698 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37702 onRender: function(ctr, pos)
37704 var dh = Roo.DomHelper;
37705 /** This region's container element
37706 * @type Roo.Element */
37707 this.el = dh.append(ctr.dom, {
37709 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37711 /** This region's title element
37712 * @type Roo.Element */
37714 this.titleEl = dh.append(this.el.dom, {
37716 unselectable: "on",
37717 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37719 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
37720 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37724 this.titleEl.enableDisplayMode();
37725 /** This region's title text element
37726 * @type HTMLElement */
37727 this.titleTextEl = this.titleEl.dom.firstChild;
37728 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37730 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37731 this.closeBtn.enableDisplayMode();
37732 this.closeBtn.on("click", this.closeClicked, this);
37733 this.closeBtn.hide();
37735 this.createBody(this.config);
37736 if(this.config.hideWhenEmpty){
37738 this.on("paneladded", this.validateVisibility, this);
37739 this.on("panelremoved", this.validateVisibility, this);
37741 if(this.autoScroll){
37742 this.bodyEl.setStyle("overflow", "auto");
37744 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37746 //if(c.titlebar !== false){
37747 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37748 this.titleEl.hide();
37750 this.titleEl.show();
37751 if(this.config.title){
37752 this.titleTextEl.innerHTML = this.config.title;
37756 if(this.config.collapsed){
37757 this.collapse(true);
37759 if(this.config.hidden){
37763 if (this.unrendered_panels && this.unrendered_panels.length) {
37764 for (var i =0;i< this.unrendered_panels.length; i++) {
37765 this.add(this.unrendered_panels[i]);
37767 this.unrendered_panels = null;
37773 applyConfig : function(c)
37776 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37777 var dh = Roo.DomHelper;
37778 if(c.titlebar !== false){
37779 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37780 this.collapseBtn.on("click", this.collapse, this);
37781 this.collapseBtn.enableDisplayMode();
37783 if(c.showPin === true || this.showPin){
37784 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37785 this.stickBtn.enableDisplayMode();
37786 this.stickBtn.on("click", this.expand, this);
37787 this.stickBtn.hide();
37792 /** This region's collapsed element
37793 * @type Roo.Element */
37796 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37797 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37800 if(c.floatable !== false){
37801 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37802 this.collapsedEl.on("click", this.collapseClick, this);
37805 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37806 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37807 id: "message", unselectable: "on", style:{"float":"left"}});
37808 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37810 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37811 this.expandBtn.on("click", this.expand, this);
37815 if(this.collapseBtn){
37816 this.collapseBtn.setVisible(c.collapsible == true);
37819 this.cmargins = c.cmargins || this.cmargins ||
37820 (this.position == "west" || this.position == "east" ?
37821 {top: 0, left: 2, right:2, bottom: 0} :
37822 {top: 2, left: 0, right:0, bottom: 2});
37824 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37827 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37829 this.autoScroll = c.autoScroll || false;
37834 this.duration = c.duration || .30;
37835 this.slideDuration = c.slideDuration || .45;
37840 * Returns true if this region is currently visible.
37841 * @return {Boolean}
37843 isVisible : function(){
37844 return this.visible;
37848 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37849 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
37851 //setCollapsedTitle : function(title){
37852 // title = title || " ";
37853 // if(this.collapsedTitleTextEl){
37854 // this.collapsedTitleTextEl.innerHTML = title;
37858 getBox : function(){
37860 // if(!this.collapsed){
37861 b = this.el.getBox(false, true);
37863 // b = this.collapsedEl.getBox(false, true);
37868 getMargins : function(){
37869 return this.margins;
37870 //return this.collapsed ? this.cmargins : this.margins;
37873 highlight : function(){
37874 this.el.addClass("x-layout-panel-dragover");
37877 unhighlight : function(){
37878 this.el.removeClass("x-layout-panel-dragover");
37881 updateBox : function(box)
37883 if (!this.bodyEl) {
37884 return; // not rendered yet..
37888 if(!this.collapsed){
37889 this.el.dom.style.left = box.x + "px";
37890 this.el.dom.style.top = box.y + "px";
37891 this.updateBody(box.width, box.height);
37893 this.collapsedEl.dom.style.left = box.x + "px";
37894 this.collapsedEl.dom.style.top = box.y + "px";
37895 this.collapsedEl.setSize(box.width, box.height);
37898 this.tabs.autoSizeTabs();
37902 updateBody : function(w, h)
37905 this.el.setWidth(w);
37906 w -= this.el.getBorderWidth("rl");
37907 if(this.config.adjustments){
37908 w += this.config.adjustments[0];
37911 if(h !== null && h > 0){
37912 this.el.setHeight(h);
37913 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37914 h -= this.el.getBorderWidth("tb");
37915 if(this.config.adjustments){
37916 h += this.config.adjustments[1];
37918 this.bodyEl.setHeight(h);
37920 h = this.tabs.syncHeight(h);
37923 if(this.panelSize){
37924 w = w !== null ? w : this.panelSize.width;
37925 h = h !== null ? h : this.panelSize.height;
37927 if(this.activePanel){
37928 var el = this.activePanel.getEl();
37929 w = w !== null ? w : el.getWidth();
37930 h = h !== null ? h : el.getHeight();
37931 this.panelSize = {width: w, height: h};
37932 this.activePanel.setSize(w, h);
37934 if(Roo.isIE && this.tabs){
37935 this.tabs.el.repaint();
37940 * Returns the container element for this region.
37941 * @return {Roo.Element}
37943 getEl : function(){
37948 * Hides this region.
37951 //if(!this.collapsed){
37952 this.el.dom.style.left = "-2000px";
37955 // this.collapsedEl.dom.style.left = "-2000px";
37956 // this.collapsedEl.hide();
37958 this.visible = false;
37959 this.fireEvent("visibilitychange", this, false);
37963 * Shows this region if it was previously hidden.
37966 //if(!this.collapsed){
37969 // this.collapsedEl.show();
37971 this.visible = true;
37972 this.fireEvent("visibilitychange", this, true);
37975 closeClicked : function(){
37976 if(this.activePanel){
37977 this.remove(this.activePanel);
37981 collapseClick : function(e){
37983 e.stopPropagation();
37986 e.stopPropagation();
37992 * Collapses this region.
37993 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37996 collapse : function(skipAnim, skipCheck = false){
37997 if(this.collapsed) {
38001 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38003 this.collapsed = true;
38005 this.split.el.hide();
38007 if(this.config.animate && skipAnim !== true){
38008 this.fireEvent("invalidated", this);
38009 this.animateCollapse();
38011 this.el.setLocation(-20000,-20000);
38013 this.collapsedEl.show();
38014 this.fireEvent("collapsed", this);
38015 this.fireEvent("invalidated", this);
38021 animateCollapse : function(){
38026 * Expands this region if it was previously collapsed.
38027 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38028 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38031 expand : function(e, skipAnim){
38033 e.stopPropagation();
38035 if(!this.collapsed || this.el.hasActiveFx()) {
38039 this.afterSlideIn();
38042 this.collapsed = false;
38043 if(this.config.animate && skipAnim !== true){
38044 this.animateExpand();
38048 this.split.el.show();
38050 this.collapsedEl.setLocation(-2000,-2000);
38051 this.collapsedEl.hide();
38052 this.fireEvent("invalidated", this);
38053 this.fireEvent("expanded", this);
38057 animateExpand : function(){
38061 initTabs : function()
38063 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38065 var ts = new Roo.bootstrap.panel.Tabs({
38066 el: this.bodyEl.dom,
38068 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38069 disableTooltips: this.config.disableTabTips,
38070 toolbar : this.config.toolbar
38073 if(this.config.hideTabs){
38074 ts.stripWrap.setDisplayed(false);
38077 ts.resizeTabs = this.config.resizeTabs === true;
38078 ts.minTabWidth = this.config.minTabWidth || 40;
38079 ts.maxTabWidth = this.config.maxTabWidth || 250;
38080 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38081 ts.monitorResize = false;
38082 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38083 ts.bodyEl.addClass('roo-layout-tabs-body');
38084 this.panels.each(this.initPanelAsTab, this);
38087 initPanelAsTab : function(panel){
38088 var ti = this.tabs.addTab(
38092 this.config.closeOnTab && panel.isClosable(),
38095 if(panel.tabTip !== undefined){
38096 ti.setTooltip(panel.tabTip);
38098 ti.on("activate", function(){
38099 this.setActivePanel(panel);
38102 if(this.config.closeOnTab){
38103 ti.on("beforeclose", function(t, e){
38105 this.remove(panel);
38109 panel.tabItem = ti;
38114 updatePanelTitle : function(panel, title)
38116 if(this.activePanel == panel){
38117 this.updateTitle(title);
38120 var ti = this.tabs.getTab(panel.getEl().id);
38122 if(panel.tabTip !== undefined){
38123 ti.setTooltip(panel.tabTip);
38128 updateTitle : function(title){
38129 if(this.titleTextEl && !this.config.title){
38130 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38134 setActivePanel : function(panel)
38136 panel = this.getPanel(panel);
38137 if(this.activePanel && this.activePanel != panel){
38138 if(this.activePanel.setActiveState(false) === false){
38142 this.activePanel = panel;
38143 panel.setActiveState(true);
38144 if(this.panelSize){
38145 panel.setSize(this.panelSize.width, this.panelSize.height);
38148 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38150 this.updateTitle(panel.getTitle());
38152 this.fireEvent("invalidated", this);
38154 this.fireEvent("panelactivated", this, panel);
38158 * Shows the specified panel.
38159 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38160 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38162 showPanel : function(panel)
38164 panel = this.getPanel(panel);
38167 var tab = this.tabs.getTab(panel.getEl().id);
38168 if(tab.isHidden()){
38169 this.tabs.unhideTab(tab.id);
38173 this.setActivePanel(panel);
38180 * Get the active panel for this region.
38181 * @return {Roo.ContentPanel} The active panel or null
38183 getActivePanel : function(){
38184 return this.activePanel;
38187 validateVisibility : function(){
38188 if(this.panels.getCount() < 1){
38189 this.updateTitle(" ");
38190 this.closeBtn.hide();
38193 if(!this.isVisible()){
38200 * Adds the passed ContentPanel(s) to this region.
38201 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38202 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38204 add : function(panel)
38206 if(arguments.length > 1){
38207 for(var i = 0, len = arguments.length; i < len; i++) {
38208 this.add(arguments[i]);
38213 // if we have not been rendered yet, then we can not really do much of this..
38214 if (!this.bodyEl) {
38215 this.unrendered_panels.push(panel);
38222 if(this.hasPanel(panel)){
38223 this.showPanel(panel);
38226 panel.setRegion(this);
38227 this.panels.add(panel);
38228 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38229 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38230 // and hide them... ???
38231 this.bodyEl.dom.appendChild(panel.getEl().dom);
38232 if(panel.background !== true){
38233 this.setActivePanel(panel);
38235 this.fireEvent("paneladded", this, panel);
38242 this.initPanelAsTab(panel);
38246 if(panel.background !== true){
38247 this.tabs.activate(panel.getEl().id);
38249 this.fireEvent("paneladded", this, panel);
38254 * Hides the tab for the specified panel.
38255 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38257 hidePanel : function(panel){
38258 if(this.tabs && (panel = this.getPanel(panel))){
38259 this.tabs.hideTab(panel.getEl().id);
38264 * Unhides the tab for a previously hidden panel.
38265 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38267 unhidePanel : function(panel){
38268 if(this.tabs && (panel = this.getPanel(panel))){
38269 this.tabs.unhideTab(panel.getEl().id);
38273 clearPanels : function(){
38274 while(this.panels.getCount() > 0){
38275 this.remove(this.panels.first());
38280 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38281 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38282 * @param {Boolean} preservePanel Overrides the config preservePanel option
38283 * @return {Roo.ContentPanel} The panel that was removed
38285 remove : function(panel, preservePanel)
38287 panel = this.getPanel(panel);
38292 this.fireEvent("beforeremove", this, panel, e);
38293 if(e.cancel === true){
38296 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38297 var panelId = panel.getId();
38298 this.panels.removeKey(panelId);
38300 document.body.appendChild(panel.getEl().dom);
38303 this.tabs.removeTab(panel.getEl().id);
38304 }else if (!preservePanel){
38305 this.bodyEl.dom.removeChild(panel.getEl().dom);
38307 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38308 var p = this.panels.first();
38309 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38310 tempEl.appendChild(p.getEl().dom);
38311 this.bodyEl.update("");
38312 this.bodyEl.dom.appendChild(p.getEl().dom);
38314 this.updateTitle(p.getTitle());
38316 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38317 this.setActivePanel(p);
38319 panel.setRegion(null);
38320 if(this.activePanel == panel){
38321 this.activePanel = null;
38323 if(this.config.autoDestroy !== false && preservePanel !== true){
38324 try{panel.destroy();}catch(e){}
38326 this.fireEvent("panelremoved", this, panel);
38331 * Returns the TabPanel component used by this region
38332 * @return {Roo.TabPanel}
38334 getTabs : function(){
38338 createTool : function(parentEl, className){
38339 var btn = Roo.DomHelper.append(parentEl, {
38341 cls: "x-layout-tools-button",
38344 cls: "roo-layout-tools-button-inner " + className,
38348 btn.addClassOnOver("roo-layout-tools-button-over");
38353 * Ext JS Library 1.1.1
38354 * Copyright(c) 2006-2007, Ext JS, LLC.
38356 * Originally Released Under LGPL - original licence link has changed is not relivant.
38359 * <script type="text/javascript">
38365 * @class Roo.SplitLayoutRegion
38366 * @extends Roo.LayoutRegion
38367 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38369 Roo.bootstrap.layout.Split = function(config){
38370 this.cursor = config.cursor;
38371 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38374 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38376 splitTip : "Drag to resize.",
38377 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38378 useSplitTips : false,
38380 applyConfig : function(config){
38381 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38384 onRender : function(ctr,pos) {
38386 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38387 if(!this.config.split){
38392 var splitEl = Roo.DomHelper.append(ctr.dom, {
38394 id: this.el.id + "-split",
38395 cls: "roo-layout-split roo-layout-split-"+this.position,
38398 /** The SplitBar for this region
38399 * @type Roo.SplitBar */
38400 // does not exist yet...
38401 Roo.log([this.position, this.orientation]);
38403 this.split = new Roo.bootstrap.SplitBar({
38404 dragElement : splitEl,
38405 resizingElement: this.el,
38406 orientation : this.orientation
38409 this.split.on("moved", this.onSplitMove, this);
38410 this.split.useShim = this.config.useShim === true;
38411 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38412 if(this.useSplitTips){
38413 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38415 //if(config.collapsible){
38416 // this.split.el.on("dblclick", this.collapse, this);
38419 if(typeof this.config.minSize != "undefined"){
38420 this.split.minSize = this.config.minSize;
38422 if(typeof this.config.maxSize != "undefined"){
38423 this.split.maxSize = this.config.maxSize;
38425 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38426 this.hideSplitter();
38431 getHMaxSize : function(){
38432 var cmax = this.config.maxSize || 10000;
38433 var center = this.mgr.getRegion("center");
38434 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38437 getVMaxSize : function(){
38438 var cmax = this.config.maxSize || 10000;
38439 var center = this.mgr.getRegion("center");
38440 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38443 onSplitMove : function(split, newSize){
38444 this.fireEvent("resized", this, newSize);
38448 * Returns the {@link Roo.SplitBar} for this region.
38449 * @return {Roo.SplitBar}
38451 getSplitBar : function(){
38456 this.hideSplitter();
38457 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38460 hideSplitter : function(){
38462 this.split.el.setLocation(-2000,-2000);
38463 this.split.el.hide();
38469 this.split.el.show();
38471 Roo.bootstrap.layout.Split.superclass.show.call(this);
38474 beforeSlide: function(){
38475 if(Roo.isGecko){// firefox overflow auto bug workaround
38476 this.bodyEl.clip();
38478 this.tabs.bodyEl.clip();
38480 if(this.activePanel){
38481 this.activePanel.getEl().clip();
38483 if(this.activePanel.beforeSlide){
38484 this.activePanel.beforeSlide();
38490 afterSlide : function(){
38491 if(Roo.isGecko){// firefox overflow auto bug workaround
38492 this.bodyEl.unclip();
38494 this.tabs.bodyEl.unclip();
38496 if(this.activePanel){
38497 this.activePanel.getEl().unclip();
38498 if(this.activePanel.afterSlide){
38499 this.activePanel.afterSlide();
38505 initAutoHide : function(){
38506 if(this.autoHide !== false){
38507 if(!this.autoHideHd){
38508 var st = new Roo.util.DelayedTask(this.slideIn, this);
38509 this.autoHideHd = {
38510 "mouseout": function(e){
38511 if(!e.within(this.el, true)){
38515 "mouseover" : function(e){
38521 this.el.on(this.autoHideHd);
38525 clearAutoHide : function(){
38526 if(this.autoHide !== false){
38527 this.el.un("mouseout", this.autoHideHd.mouseout);
38528 this.el.un("mouseover", this.autoHideHd.mouseover);
38532 clearMonitor : function(){
38533 Roo.get(document).un("click", this.slideInIf, this);
38536 // these names are backwards but not changed for compat
38537 slideOut : function(){
38538 if(this.isSlid || this.el.hasActiveFx()){
38541 this.isSlid = true;
38542 if(this.collapseBtn){
38543 this.collapseBtn.hide();
38545 this.closeBtnState = this.closeBtn.getStyle('display');
38546 this.closeBtn.hide();
38548 this.stickBtn.show();
38551 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38552 this.beforeSlide();
38553 this.el.setStyle("z-index", 10001);
38554 this.el.slideIn(this.getSlideAnchor(), {
38555 callback: function(){
38557 this.initAutoHide();
38558 Roo.get(document).on("click", this.slideInIf, this);
38559 this.fireEvent("slideshow", this);
38566 afterSlideIn : function(){
38567 this.clearAutoHide();
38568 this.isSlid = false;
38569 this.clearMonitor();
38570 this.el.setStyle("z-index", "");
38571 if(this.collapseBtn){
38572 this.collapseBtn.show();
38574 this.closeBtn.setStyle('display', this.closeBtnState);
38576 this.stickBtn.hide();
38578 this.fireEvent("slidehide", this);
38581 slideIn : function(cb){
38582 if(!this.isSlid || this.el.hasActiveFx()){
38586 this.isSlid = false;
38587 this.beforeSlide();
38588 this.el.slideOut(this.getSlideAnchor(), {
38589 callback: function(){
38590 this.el.setLeftTop(-10000, -10000);
38592 this.afterSlideIn();
38600 slideInIf : function(e){
38601 if(!e.within(this.el)){
38606 animateCollapse : function(){
38607 this.beforeSlide();
38608 this.el.setStyle("z-index", 20000);
38609 var anchor = this.getSlideAnchor();
38610 this.el.slideOut(anchor, {
38611 callback : function(){
38612 this.el.setStyle("z-index", "");
38613 this.collapsedEl.slideIn(anchor, {duration:.3});
38615 this.el.setLocation(-10000,-10000);
38617 this.fireEvent("collapsed", this);
38624 animateExpand : function(){
38625 this.beforeSlide();
38626 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38627 this.el.setStyle("z-index", 20000);
38628 this.collapsedEl.hide({
38631 this.el.slideIn(this.getSlideAnchor(), {
38632 callback : function(){
38633 this.el.setStyle("z-index", "");
38636 this.split.el.show();
38638 this.fireEvent("invalidated", this);
38639 this.fireEvent("expanded", this);
38667 getAnchor : function(){
38668 return this.anchors[this.position];
38671 getCollapseAnchor : function(){
38672 return this.canchors[this.position];
38675 getSlideAnchor : function(){
38676 return this.sanchors[this.position];
38679 getAlignAdj : function(){
38680 var cm = this.cmargins;
38681 switch(this.position){
38697 getExpandAdj : function(){
38698 var c = this.collapsedEl, cm = this.cmargins;
38699 switch(this.position){
38701 return [-(cm.right+c.getWidth()+cm.left), 0];
38704 return [cm.right+c.getWidth()+cm.left, 0];
38707 return [0, -(cm.top+cm.bottom+c.getHeight())];
38710 return [0, cm.top+cm.bottom+c.getHeight()];
38716 * Ext JS Library 1.1.1
38717 * Copyright(c) 2006-2007, Ext JS, LLC.
38719 * Originally Released Under LGPL - original licence link has changed is not relivant.
38722 * <script type="text/javascript">
38725 * These classes are private internal classes
38727 Roo.bootstrap.layout.Center = function(config){
38728 config.region = "center";
38729 Roo.bootstrap.layout.Region.call(this, config);
38730 this.visible = true;
38731 this.minWidth = config.minWidth || 20;
38732 this.minHeight = config.minHeight || 20;
38735 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38737 // center panel can't be hidden
38741 // center panel can't be hidden
38744 getMinWidth: function(){
38745 return this.minWidth;
38748 getMinHeight: function(){
38749 return this.minHeight;
38763 Roo.bootstrap.layout.North = function(config)
38765 config.region = 'north';
38766 config.cursor = 'n-resize';
38768 Roo.bootstrap.layout.Split.call(this, config);
38772 this.split.placement = Roo.bootstrap.SplitBar.TOP;
38773 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38774 this.split.el.addClass("roo-layout-split-v");
38776 var size = config.initialSize || config.height;
38777 if(typeof size != "undefined"){
38778 this.el.setHeight(size);
38781 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38783 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38787 getBox : function(){
38788 if(this.collapsed){
38789 return this.collapsedEl.getBox();
38791 var box = this.el.getBox();
38793 box.height += this.split.el.getHeight();
38798 updateBox : function(box){
38799 if(this.split && !this.collapsed){
38800 box.height -= this.split.el.getHeight();
38801 this.split.el.setLeft(box.x);
38802 this.split.el.setTop(box.y+box.height);
38803 this.split.el.setWidth(box.width);
38805 if(this.collapsed){
38806 this.updateBody(box.width, null);
38808 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38816 Roo.bootstrap.layout.South = function(config){
38817 config.region = 'south';
38818 config.cursor = 's-resize';
38819 Roo.bootstrap.layout.Split.call(this, config);
38821 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38822 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38823 this.split.el.addClass("roo-layout-split-v");
38825 var size = config.initialSize || config.height;
38826 if(typeof size != "undefined"){
38827 this.el.setHeight(size);
38831 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38832 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38833 getBox : function(){
38834 if(this.collapsed){
38835 return this.collapsedEl.getBox();
38837 var box = this.el.getBox();
38839 var sh = this.split.el.getHeight();
38846 updateBox : function(box){
38847 if(this.split && !this.collapsed){
38848 var sh = this.split.el.getHeight();
38851 this.split.el.setLeft(box.x);
38852 this.split.el.setTop(box.y-sh);
38853 this.split.el.setWidth(box.width);
38855 if(this.collapsed){
38856 this.updateBody(box.width, null);
38858 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38862 Roo.bootstrap.layout.East = function(config){
38863 config.region = "east";
38864 config.cursor = "e-resize";
38865 Roo.bootstrap.layout.Split.call(this, config);
38867 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38868 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38869 this.split.el.addClass("roo-layout-split-h");
38871 var size = config.initialSize || config.width;
38872 if(typeof size != "undefined"){
38873 this.el.setWidth(size);
38876 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38877 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38878 getBox : function(){
38879 if(this.collapsed){
38880 return this.collapsedEl.getBox();
38882 var box = this.el.getBox();
38884 var sw = this.split.el.getWidth();
38891 updateBox : function(box){
38892 if(this.split && !this.collapsed){
38893 var sw = this.split.el.getWidth();
38895 this.split.el.setLeft(box.x);
38896 this.split.el.setTop(box.y);
38897 this.split.el.setHeight(box.height);
38900 if(this.collapsed){
38901 this.updateBody(null, box.height);
38903 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38907 Roo.bootstrap.layout.West = function(config){
38908 config.region = "west";
38909 config.cursor = "w-resize";
38911 Roo.bootstrap.layout.Split.call(this, config);
38913 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38914 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38915 this.split.el.addClass("roo-layout-split-h");
38919 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38920 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38922 onRender: function(ctr, pos)
38924 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38925 var size = this.config.initialSize || this.config.width;
38926 if(typeof size != "undefined"){
38927 this.el.setWidth(size);
38931 getBox : function(){
38932 if(this.collapsed){
38933 return this.collapsedEl.getBox();
38935 var box = this.el.getBox();
38937 box.width += this.split.el.getWidth();
38942 updateBox : function(box){
38943 if(this.split && !this.collapsed){
38944 var sw = this.split.el.getWidth();
38946 this.split.el.setLeft(box.x+box.width);
38947 this.split.el.setTop(box.y);
38948 this.split.el.setHeight(box.height);
38950 if(this.collapsed){
38951 this.updateBody(null, box.height);
38953 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38955 });Roo.namespace("Roo.bootstrap.panel");/*
38957 * Ext JS Library 1.1.1
38958 * Copyright(c) 2006-2007, Ext JS, LLC.
38960 * Originally Released Under LGPL - original licence link has changed is not relivant.
38963 * <script type="text/javascript">
38966 * @class Roo.ContentPanel
38967 * @extends Roo.util.Observable
38968 * A basic ContentPanel element.
38969 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
38970 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
38971 * @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
38972 * @cfg {Boolean} closable True if the panel can be closed/removed
38973 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
38974 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38975 * @cfg {Toolbar} toolbar A toolbar for this panel
38976 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
38977 * @cfg {String} title The title for this panel
38978 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38979 * @cfg {String} url Calls {@link #setUrl} with this value
38980 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38981 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
38982 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
38983 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
38984 * @cfg {Boolean} badges render the badges
38987 * Create a new ContentPanel.
38988 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38989 * @param {String/Object} config A string to set only the title or a config object
38990 * @param {String} content (optional) Set the HTML content for this panel
38991 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38993 Roo.bootstrap.panel.Content = function( config){
38995 this.tpl = config.tpl || false;
38997 var el = config.el;
38998 var content = config.content;
39000 if(config.autoCreate){ // xtype is available if this is called from factory
39003 this.el = Roo.get(el);
39004 if(!this.el && config && config.autoCreate){
39005 if(typeof config.autoCreate == "object"){
39006 if(!config.autoCreate.id){
39007 config.autoCreate.id = config.id||el;
39009 this.el = Roo.DomHelper.append(document.body,
39010 config.autoCreate, true);
39012 var elcfg = { tag: "div",
39013 cls: "roo-layout-inactive-content",
39017 elcfg.html = config.html;
39021 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39024 this.closable = false;
39025 this.loaded = false;
39026 this.active = false;
39029 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39031 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39033 this.wrapEl = this.el; //this.el.wrap();
39035 if (config.toolbar.items) {
39036 ti = config.toolbar.items ;
39037 delete config.toolbar.items ;
39041 this.toolbar.render(this.wrapEl, 'before');
39042 for(var i =0;i < ti.length;i++) {
39043 // Roo.log(['add child', items[i]]);
39044 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39046 this.toolbar.items = nitems;
39047 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39048 delete config.toolbar;
39052 // xtype created footer. - not sure if will work as we normally have to render first..
39053 if (this.footer && !this.footer.el && this.footer.xtype) {
39054 if (!this.wrapEl) {
39055 this.wrapEl = this.el.wrap();
39058 this.footer.container = this.wrapEl.createChild();
39060 this.footer = Roo.factory(this.footer, Roo);
39065 if(typeof config == "string"){
39066 this.title = config;
39068 Roo.apply(this, config);
39072 this.resizeEl = Roo.get(this.resizeEl, true);
39074 this.resizeEl = this.el;
39076 // handle view.xtype
39084 * Fires when this panel is activated.
39085 * @param {Roo.ContentPanel} this
39089 * @event deactivate
39090 * Fires when this panel is activated.
39091 * @param {Roo.ContentPanel} this
39093 "deactivate" : true,
39097 * Fires when this panel is resized if fitToFrame is true.
39098 * @param {Roo.ContentPanel} this
39099 * @param {Number} width The width after any component adjustments
39100 * @param {Number} height The height after any component adjustments
39106 * Fires when this tab is created
39107 * @param {Roo.ContentPanel} this
39118 if(this.autoScroll){
39119 this.resizeEl.setStyle("overflow", "auto");
39121 // fix randome scrolling
39122 //this.el.on('scroll', function() {
39123 // Roo.log('fix random scolling');
39124 // this.scrollTo('top',0);
39127 content = content || this.content;
39129 this.setContent(content);
39131 if(config && config.url){
39132 this.setUrl(this.url, this.params, this.loadOnce);
39137 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39139 if (this.view && typeof(this.view.xtype) != 'undefined') {
39140 this.view.el = this.el.appendChild(document.createElement("div"));
39141 this.view = Roo.factory(this.view);
39142 this.view.render && this.view.render(false, '');
39146 this.fireEvent('render', this);
39149 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39153 setRegion : function(region){
39154 this.region = region;
39155 this.setActiveClass(region && !this.background);
39159 setActiveClass: function(state)
39162 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39163 this.el.setStyle('position','relative');
39165 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39166 this.el.setStyle('position', 'absolute');
39171 * Returns the toolbar for this Panel if one was configured.
39172 * @return {Roo.Toolbar}
39174 getToolbar : function(){
39175 return this.toolbar;
39178 setActiveState : function(active)
39180 this.active = active;
39181 this.setActiveClass(active);
39183 if(this.fireEvent("deactivate", this) === false){
39188 this.fireEvent("activate", this);
39192 * Updates this panel's element
39193 * @param {String} content The new content
39194 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39196 setContent : function(content, loadScripts){
39197 this.el.update(content, loadScripts);
39200 ignoreResize : function(w, h){
39201 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39204 this.lastSize = {width: w, height: h};
39209 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39210 * @return {Roo.UpdateManager} The UpdateManager
39212 getUpdateManager : function(){
39213 return this.el.getUpdateManager();
39216 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39217 * @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:
39220 url: "your-url.php",
39221 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39222 callback: yourFunction,
39223 scope: yourObject, //(optional scope)
39226 text: "Loading...",
39231 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39232 * 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.
39233 * @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}
39234 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39235 * @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.
39236 * @return {Roo.ContentPanel} this
39239 var um = this.el.getUpdateManager();
39240 um.update.apply(um, arguments);
39246 * 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.
39247 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39248 * @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)
39249 * @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)
39250 * @return {Roo.UpdateManager} The UpdateManager
39252 setUrl : function(url, params, loadOnce){
39253 if(this.refreshDelegate){
39254 this.removeListener("activate", this.refreshDelegate);
39256 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39257 this.on("activate", this.refreshDelegate);
39258 return this.el.getUpdateManager();
39261 _handleRefresh : function(url, params, loadOnce){
39262 if(!loadOnce || !this.loaded){
39263 var updater = this.el.getUpdateManager();
39264 updater.update(url, params, this._setLoaded.createDelegate(this));
39268 _setLoaded : function(){
39269 this.loaded = true;
39273 * Returns this panel's id
39276 getId : function(){
39281 * Returns this panel's element - used by regiosn to add.
39282 * @return {Roo.Element}
39284 getEl : function(){
39285 return this.wrapEl || this.el;
39290 adjustForComponents : function(width, height)
39292 //Roo.log('adjustForComponents ');
39293 if(this.resizeEl != this.el){
39294 width -= this.el.getFrameWidth('lr');
39295 height -= this.el.getFrameWidth('tb');
39298 var te = this.toolbar.getEl();
39299 te.setWidth(width);
39300 height -= te.getHeight();
39303 var te = this.footer.getEl();
39304 te.setWidth(width);
39305 height -= te.getHeight();
39309 if(this.adjustments){
39310 width += this.adjustments[0];
39311 height += this.adjustments[1];
39313 return {"width": width, "height": height};
39316 setSize : function(width, height){
39317 if(this.fitToFrame && !this.ignoreResize(width, height)){
39318 if(this.fitContainer && this.resizeEl != this.el){
39319 this.el.setSize(width, height);
39321 var size = this.adjustForComponents(width, height);
39322 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39323 this.fireEvent('resize', this, size.width, size.height);
39328 * Returns this panel's title
39331 getTitle : function(){
39333 if (typeof(this.title) != 'object') {
39338 for (var k in this.title) {
39339 if (!this.title.hasOwnProperty(k)) {
39343 if (k.indexOf('-') >= 0) {
39344 var s = k.split('-');
39345 for (var i = 0; i<s.length; i++) {
39346 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39349 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39356 * Set this panel's title
39357 * @param {String} title
39359 setTitle : function(title){
39360 this.title = title;
39362 this.region.updatePanelTitle(this, title);
39367 * Returns true is this panel was configured to be closable
39368 * @return {Boolean}
39370 isClosable : function(){
39371 return this.closable;
39374 beforeSlide : function(){
39376 this.resizeEl.clip();
39379 afterSlide : function(){
39381 this.resizeEl.unclip();
39385 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39386 * Will fail silently if the {@link #setUrl} method has not been called.
39387 * This does not activate the panel, just updates its content.
39389 refresh : function(){
39390 if(this.refreshDelegate){
39391 this.loaded = false;
39392 this.refreshDelegate();
39397 * Destroys this panel
39399 destroy : function(){
39400 this.el.removeAllListeners();
39401 var tempEl = document.createElement("span");
39402 tempEl.appendChild(this.el.dom);
39403 tempEl.innerHTML = "";
39409 * form - if the content panel contains a form - this is a reference to it.
39410 * @type {Roo.form.Form}
39414 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39415 * This contains a reference to it.
39421 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39431 * @param {Object} cfg Xtype definition of item to add.
39435 getChildContainer: function () {
39436 return this.getEl();
39441 var ret = new Roo.factory(cfg);
39446 if (cfg.xtype.match(/^Form$/)) {
39449 //if (this.footer) {
39450 // el = this.footer.container.insertSibling(false, 'before');
39452 el = this.el.createChild();
39455 this.form = new Roo.form.Form(cfg);
39458 if ( this.form.allItems.length) {
39459 this.form.render(el.dom);
39463 // should only have one of theses..
39464 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39465 // views.. should not be just added - used named prop 'view''
39467 cfg.el = this.el.appendChild(document.createElement("div"));
39470 var ret = new Roo.factory(cfg);
39472 ret.render && ret.render(false, ''); // render blank..
39482 * @class Roo.bootstrap.panel.Grid
39483 * @extends Roo.bootstrap.panel.Content
39485 * Create a new GridPanel.
39486 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39487 * @param {Object} config A the config object
39493 Roo.bootstrap.panel.Grid = function(config)
39497 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39498 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39500 config.el = this.wrapper;
39501 //this.el = this.wrapper;
39503 if (config.container) {
39504 // ctor'ed from a Border/panel.grid
39507 this.wrapper.setStyle("overflow", "hidden");
39508 this.wrapper.addClass('roo-grid-container');
39513 if(config.toolbar){
39514 var tool_el = this.wrapper.createChild();
39515 this.toolbar = Roo.factory(config.toolbar);
39517 if (config.toolbar.items) {
39518 ti = config.toolbar.items ;
39519 delete config.toolbar.items ;
39523 this.toolbar.render(tool_el);
39524 for(var i =0;i < ti.length;i++) {
39525 // Roo.log(['add child', items[i]]);
39526 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39528 this.toolbar.items = nitems;
39530 delete config.toolbar;
39533 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39534 config.grid.scrollBody = true;;
39535 config.grid.monitorWindowResize = false; // turn off autosizing
39536 config.grid.autoHeight = false;
39537 config.grid.autoWidth = false;
39539 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39541 if (config.background) {
39542 // render grid on panel activation (if panel background)
39543 this.on('activate', function(gp) {
39544 if (!gp.grid.rendered) {
39545 gp.grid.render(this.wrapper);
39546 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39551 this.grid.render(this.wrapper);
39552 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39555 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39556 // ??? needed ??? config.el = this.wrapper;
39561 // xtype created footer. - not sure if will work as we normally have to render first..
39562 if (this.footer && !this.footer.el && this.footer.xtype) {
39564 var ctr = this.grid.getView().getFooterPanel(true);
39565 this.footer.dataSource = this.grid.dataSource;
39566 this.footer = Roo.factory(this.footer, Roo);
39567 this.footer.render(ctr);
39577 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39578 getId : function(){
39579 return this.grid.id;
39583 * Returns the grid for this panel
39584 * @return {Roo.bootstrap.Table}
39586 getGrid : function(){
39590 setSize : function(width, height){
39591 if(!this.ignoreResize(width, height)){
39592 var grid = this.grid;
39593 var size = this.adjustForComponents(width, height);
39594 // tfoot is not a footer?
39597 var gridel = grid.getGridEl();
39598 gridel.setSize(size.width, size.height);
39600 var tbd = grid.getGridEl().select('tbody', true).first();
39601 var thd = grid.getGridEl().select('thead',true).first();
39602 var tbf= grid.getGridEl().select('tfoot', true).first();
39605 size.height -= thd.getHeight();
39608 size.height -= thd.getHeight();
39611 tbd.setSize(size.width, size.height );
39612 // this is for the account management tab -seems to work there.
39613 var thd = grid.getGridEl().select('thead',true).first();
39615 // tbd.setSize(size.width, size.height - thd.getHeight());
39624 beforeSlide : function(){
39625 this.grid.getView().scroller.clip();
39628 afterSlide : function(){
39629 this.grid.getView().scroller.unclip();
39632 destroy : function(){
39633 this.grid.destroy();
39635 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
39640 * @class Roo.bootstrap.panel.Nest
39641 * @extends Roo.bootstrap.panel.Content
39643 * Create a new Panel, that can contain a layout.Border.
39646 * @param {Roo.BorderLayout} layout The layout for this panel
39647 * @param {String/Object} config A string to set only the title or a config object
39649 Roo.bootstrap.panel.Nest = function(config)
39651 // construct with only one argument..
39652 /* FIXME - implement nicer consturctors
39653 if (layout.layout) {
39655 layout = config.layout;
39656 delete config.layout;
39658 if (layout.xtype && !layout.getEl) {
39659 // then layout needs constructing..
39660 layout = Roo.factory(layout, Roo);
39664 config.el = config.layout.getEl();
39666 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39668 config.layout.monitorWindowResize = false; // turn off autosizing
39669 this.layout = config.layout;
39670 this.layout.getEl().addClass("roo-layout-nested-layout");
39671 this.layout.parent = this;
39678 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39680 setSize : function(width, height){
39681 if(!this.ignoreResize(width, height)){
39682 var size = this.adjustForComponents(width, height);
39683 var el = this.layout.getEl();
39684 if (size.height < 1) {
39685 el.setWidth(size.width);
39687 el.setSize(size.width, size.height);
39689 var touch = el.dom.offsetWidth;
39690 this.layout.layout();
39691 // ie requires a double layout on the first pass
39692 if(Roo.isIE && !this.initialized){
39693 this.initialized = true;
39694 this.layout.layout();
39699 // activate all subpanels if not currently active..
39701 setActiveState : function(active){
39702 this.active = active;
39703 this.setActiveClass(active);
39706 this.fireEvent("deactivate", this);
39710 this.fireEvent("activate", this);
39711 // not sure if this should happen before or after..
39712 if (!this.layout) {
39713 return; // should not happen..
39716 for (var r in this.layout.regions) {
39717 reg = this.layout.getRegion(r);
39718 if (reg.getActivePanel()) {
39719 //reg.showPanel(reg.getActivePanel()); // force it to activate..
39720 reg.setActivePanel(reg.getActivePanel());
39723 if (!reg.panels.length) {
39726 reg.showPanel(reg.getPanel(0));
39735 * Returns the nested BorderLayout for this panel
39736 * @return {Roo.BorderLayout}
39738 getLayout : function(){
39739 return this.layout;
39743 * Adds a xtype elements to the layout of the nested panel
39747 xtype : 'ContentPanel',
39754 xtype : 'NestedLayoutPanel',
39760 items : [ ... list of content panels or nested layout panels.. ]
39764 * @param {Object} cfg Xtype definition of item to add.
39766 addxtype : function(cfg) {
39767 return this.layout.addxtype(cfg);
39772 * Ext JS Library 1.1.1
39773 * Copyright(c) 2006-2007, Ext JS, LLC.
39775 * Originally Released Under LGPL - original licence link has changed is not relivant.
39778 * <script type="text/javascript">
39781 * @class Roo.TabPanel
39782 * @extends Roo.util.Observable
39783 * A lightweight tab container.
39787 // basic tabs 1, built from existing content
39788 var tabs = new Roo.TabPanel("tabs1");
39789 tabs.addTab("script", "View Script");
39790 tabs.addTab("markup", "View Markup");
39791 tabs.activate("script");
39793 // more advanced tabs, built from javascript
39794 var jtabs = new Roo.TabPanel("jtabs");
39795 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39797 // set up the UpdateManager
39798 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39799 var updater = tab2.getUpdateManager();
39800 updater.setDefaultUrl("ajax1.htm");
39801 tab2.on('activate', updater.refresh, updater, true);
39803 // Use setUrl for Ajax loading
39804 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39805 tab3.setUrl("ajax2.htm", null, true);
39808 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39811 jtabs.activate("jtabs-1");
39814 * Create a new TabPanel.
39815 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39816 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39818 Roo.bootstrap.panel.Tabs = function(config){
39820 * The container element for this TabPanel.
39821 * @type Roo.Element
39823 this.el = Roo.get(config.el);
39826 if(typeof config == "boolean"){
39827 this.tabPosition = config ? "bottom" : "top";
39829 Roo.apply(this, config);
39833 if(this.tabPosition == "bottom"){
39834 // if tabs are at the bottom = create the body first.
39835 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39836 this.el.addClass("roo-tabs-bottom");
39838 // next create the tabs holders
39840 if (this.tabPosition == "west"){
39842 var reg = this.region; // fake it..
39844 if (!reg.mgr.parent) {
39847 reg = reg.mgr.parent.region;
39849 Roo.log("got nest?");
39851 if (reg.mgr.getRegion('west')) {
39852 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39853 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39854 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39855 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39856 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39864 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39865 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39866 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39867 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39872 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39875 // finally - if tabs are at the top, then create the body last..
39876 if(this.tabPosition != "bottom"){
39877 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39878 * @type Roo.Element
39880 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39881 this.el.addClass("roo-tabs-top");
39885 this.bodyEl.setStyle("position", "relative");
39887 this.active = null;
39888 this.activateDelegate = this.activate.createDelegate(this);
39893 * Fires when the active tab changes
39894 * @param {Roo.TabPanel} this
39895 * @param {Roo.TabPanelItem} activePanel The new active tab
39899 * @event beforetabchange
39900 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39901 * @param {Roo.TabPanel} this
39902 * @param {Object} e Set cancel to true on this object to cancel the tab change
39903 * @param {Roo.TabPanelItem} tab The tab being changed to
39905 "beforetabchange" : true
39908 Roo.EventManager.onWindowResize(this.onResize, this);
39909 this.cpad = this.el.getPadding("lr");
39910 this.hiddenCount = 0;
39913 // toolbar on the tabbar support...
39914 if (this.toolbar) {
39915 alert("no toolbar support yet");
39916 this.toolbar = false;
39918 var tcfg = this.toolbar;
39919 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
39920 this.toolbar = new Roo.Toolbar(tcfg);
39921 if (Roo.isSafari) {
39922 var tbl = tcfg.container.child('table', true);
39923 tbl.setAttribute('width', '100%');
39931 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39934 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39936 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39938 tabPosition : "top",
39940 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39942 currentTabWidth : 0,
39944 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39948 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39952 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39954 preferredTabWidth : 175,
39956 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39958 resizeTabs : false,
39960 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39962 monitorResize : true,
39964 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
39966 toolbar : false, // set by caller..
39968 region : false, /// set by caller
39970 disableTooltips : true, // not used yet...
39973 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39974 * @param {String} id The id of the div to use <b>or create</b>
39975 * @param {String} text The text for the tab
39976 * @param {String} content (optional) Content to put in the TabPanelItem body
39977 * @param {Boolean} closable (optional) True to create a close icon on the tab
39978 * @return {Roo.TabPanelItem} The created TabPanelItem
39980 addTab : function(id, text, content, closable, tpl)
39982 var item = new Roo.bootstrap.panel.TabItem({
39986 closable : closable,
39989 this.addTabItem(item);
39991 item.setContent(content);
39997 * Returns the {@link Roo.TabPanelItem} with the specified id/index
39998 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39999 * @return {Roo.TabPanelItem}
40001 getTab : function(id){
40002 return this.items[id];
40006 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40007 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40009 hideTab : function(id){
40010 var t = this.items[id];
40013 this.hiddenCount++;
40014 this.autoSizeTabs();
40019 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40020 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40022 unhideTab : function(id){
40023 var t = this.items[id];
40025 t.setHidden(false);
40026 this.hiddenCount--;
40027 this.autoSizeTabs();
40032 * Adds an existing {@link Roo.TabPanelItem}.
40033 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40035 addTabItem : function(item)
40037 this.items[item.id] = item;
40038 this.items.push(item);
40039 this.autoSizeTabs();
40040 // if(this.resizeTabs){
40041 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40042 // this.autoSizeTabs();
40044 // item.autoSize();
40049 * Removes a {@link Roo.TabPanelItem}.
40050 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40052 removeTab : function(id){
40053 var items = this.items;
40054 var tab = items[id];
40055 if(!tab) { return; }
40056 var index = items.indexOf(tab);
40057 if(this.active == tab && items.length > 1){
40058 var newTab = this.getNextAvailable(index);
40063 this.stripEl.dom.removeChild(tab.pnode.dom);
40064 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40065 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40067 items.splice(index, 1);
40068 delete this.items[tab.id];
40069 tab.fireEvent("close", tab);
40070 tab.purgeListeners();
40071 this.autoSizeTabs();
40074 getNextAvailable : function(start){
40075 var items = this.items;
40077 // look for a next tab that will slide over to
40078 // replace the one being removed
40079 while(index < items.length){
40080 var item = items[++index];
40081 if(item && !item.isHidden()){
40085 // if one isn't found select the previous tab (on the left)
40088 var item = items[--index];
40089 if(item && !item.isHidden()){
40097 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40098 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40100 disableTab : function(id){
40101 var tab = this.items[id];
40102 if(tab && this.active != tab){
40108 * Enables a {@link Roo.TabPanelItem} that is disabled.
40109 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40111 enableTab : function(id){
40112 var tab = this.items[id];
40117 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40118 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40119 * @return {Roo.TabPanelItem} The TabPanelItem.
40121 activate : function(id)
40123 //Roo.log('activite:' + id);
40125 var tab = this.items[id];
40129 if(tab == this.active || tab.disabled){
40133 this.fireEvent("beforetabchange", this, e, tab);
40134 if(e.cancel !== true && !tab.disabled){
40136 this.active.hide();
40138 this.active = this.items[id];
40139 this.active.show();
40140 this.fireEvent("tabchange", this, this.active);
40146 * Gets the active {@link Roo.TabPanelItem}.
40147 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40149 getActiveTab : function(){
40150 return this.active;
40154 * Updates the tab body element to fit the height of the container element
40155 * for overflow scrolling
40156 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40158 syncHeight : function(targetHeight){
40159 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40160 var bm = this.bodyEl.getMargins();
40161 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40162 this.bodyEl.setHeight(newHeight);
40166 onResize : function(){
40167 if(this.monitorResize){
40168 this.autoSizeTabs();
40173 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40175 beginUpdate : function(){
40176 this.updating = true;
40180 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40182 endUpdate : function(){
40183 this.updating = false;
40184 this.autoSizeTabs();
40188 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40190 autoSizeTabs : function()
40192 var count = this.items.length;
40193 var vcount = count - this.hiddenCount;
40196 this.stripEl.hide();
40198 this.stripEl.show();
40201 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40206 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40207 var availWidth = Math.floor(w / vcount);
40208 var b = this.stripBody;
40209 if(b.getWidth() > w){
40210 var tabs = this.items;
40211 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40212 if(availWidth < this.minTabWidth){
40213 /*if(!this.sleft){ // incomplete scrolling code
40214 this.createScrollButtons();
40217 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40220 if(this.currentTabWidth < this.preferredTabWidth){
40221 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40227 * Returns the number of tabs in this TabPanel.
40230 getCount : function(){
40231 return this.items.length;
40235 * Resizes all the tabs to the passed width
40236 * @param {Number} The new width
40238 setTabWidth : function(width){
40239 this.currentTabWidth = width;
40240 for(var i = 0, len = this.items.length; i < len; i++) {
40241 if(!this.items[i].isHidden()) {
40242 this.items[i].setWidth(width);
40248 * Destroys this TabPanel
40249 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40251 destroy : function(removeEl){
40252 Roo.EventManager.removeResizeListener(this.onResize, this);
40253 for(var i = 0, len = this.items.length; i < len; i++){
40254 this.items[i].purgeListeners();
40256 if(removeEl === true){
40257 this.el.update("");
40262 createStrip : function(container)
40264 var strip = document.createElement("nav");
40265 strip.className = Roo.bootstrap.version == 4 ?
40266 "navbar-light bg-light" :
40267 "navbar navbar-default"; //"x-tabs-wrap";
40268 container.appendChild(strip);
40272 createStripList : function(strip)
40274 // div wrapper for retard IE
40275 // returns the "tr" element.
40276 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40277 //'<div class="x-tabs-strip-wrap">'+
40278 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40279 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40280 return strip.firstChild; //.firstChild.firstChild.firstChild;
40282 createBody : function(container)
40284 var body = document.createElement("div");
40285 Roo.id(body, "tab-body");
40286 //Roo.fly(body).addClass("x-tabs-body");
40287 Roo.fly(body).addClass("tab-content");
40288 container.appendChild(body);
40291 createItemBody :function(bodyEl, id){
40292 var body = Roo.getDom(id);
40294 body = document.createElement("div");
40297 //Roo.fly(body).addClass("x-tabs-item-body");
40298 Roo.fly(body).addClass("tab-pane");
40299 bodyEl.insertBefore(body, bodyEl.firstChild);
40303 createStripElements : function(stripEl, text, closable, tpl)
40305 var td = document.createElement("li"); // was td..
40306 td.className = 'nav-item';
40308 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40311 stripEl.appendChild(td);
40313 td.className = "x-tabs-closable";
40314 if(!this.closeTpl){
40315 this.closeTpl = new Roo.Template(
40316 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40317 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40318 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40321 var el = this.closeTpl.overwrite(td, {"text": text});
40322 var close = el.getElementsByTagName("div")[0];
40323 var inner = el.getElementsByTagName("em")[0];
40324 return {"el": el, "close": close, "inner": inner};
40327 // not sure what this is..
40328 // if(!this.tabTpl){
40329 //this.tabTpl = new Roo.Template(
40330 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40331 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40333 // this.tabTpl = new Roo.Template(
40334 // '<a href="#">' +
40335 // '<span unselectable="on"' +
40336 // (this.disableTooltips ? '' : ' title="{text}"') +
40337 // ' >{text}</span></a>'
40343 var template = tpl || this.tabTpl || false;
40346 template = new Roo.Template(
40347 Roo.bootstrap.version == 4 ?
40349 '<a class="nav-link" href="#" unselectable="on"' +
40350 (this.disableTooltips ? '' : ' title="{text}"') +
40353 '<a class="nav-link" href="#">' +
40354 '<span unselectable="on"' +
40355 (this.disableTooltips ? '' : ' title="{text}"') +
40356 ' >{text}</span></a>'
40361 switch (typeof(template)) {
40365 template = new Roo.Template(template);
40371 var el = template.overwrite(td, {"text": text});
40373 var inner = el.getElementsByTagName("span")[0];
40375 return {"el": el, "inner": inner};
40383 * @class Roo.TabPanelItem
40384 * @extends Roo.util.Observable
40385 * Represents an individual item (tab plus body) in a TabPanel.
40386 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40387 * @param {String} id The id of this TabPanelItem
40388 * @param {String} text The text for the tab of this TabPanelItem
40389 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40391 Roo.bootstrap.panel.TabItem = function(config){
40393 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40394 * @type Roo.TabPanel
40396 this.tabPanel = config.panel;
40398 * The id for this TabPanelItem
40401 this.id = config.id;
40403 this.disabled = false;
40405 this.text = config.text;
40407 this.loaded = false;
40408 this.closable = config.closable;
40411 * The body element for this TabPanelItem.
40412 * @type Roo.Element
40414 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40415 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40416 this.bodyEl.setStyle("display", "block");
40417 this.bodyEl.setStyle("zoom", "1");
40418 //this.hideAction();
40420 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40422 this.el = Roo.get(els.el);
40423 this.inner = Roo.get(els.inner, true);
40424 this.textEl = Roo.bootstrap.version == 4 ?
40425 this.el : Roo.get(this.el.dom.firstChild, true);
40427 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40428 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40431 // this.el.on("mousedown", this.onTabMouseDown, this);
40432 this.el.on("click", this.onTabClick, this);
40434 if(config.closable){
40435 var c = Roo.get(els.close, true);
40436 c.dom.title = this.closeText;
40437 c.addClassOnOver("close-over");
40438 c.on("click", this.closeClick, this);
40444 * Fires when this tab becomes the active tab.
40445 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40446 * @param {Roo.TabPanelItem} this
40450 * @event beforeclose
40451 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40452 * @param {Roo.TabPanelItem} this
40453 * @param {Object} e Set cancel to true on this object to cancel the close.
40455 "beforeclose": true,
40458 * Fires when this tab is closed.
40459 * @param {Roo.TabPanelItem} this
40463 * @event deactivate
40464 * Fires when this tab is no longer the active tab.
40465 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40466 * @param {Roo.TabPanelItem} this
40468 "deactivate" : true
40470 this.hidden = false;
40472 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40475 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40477 purgeListeners : function(){
40478 Roo.util.Observable.prototype.purgeListeners.call(this);
40479 this.el.removeAllListeners();
40482 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40485 this.status_node.addClass("active");
40488 this.tabPanel.stripWrap.repaint();
40490 this.fireEvent("activate", this.tabPanel, this);
40494 * Returns true if this tab is the active tab.
40495 * @return {Boolean}
40497 isActive : function(){
40498 return this.tabPanel.getActiveTab() == this;
40502 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40505 this.status_node.removeClass("active");
40507 this.fireEvent("deactivate", this.tabPanel, this);
40510 hideAction : function(){
40511 this.bodyEl.hide();
40512 this.bodyEl.setStyle("position", "absolute");
40513 this.bodyEl.setLeft("-20000px");
40514 this.bodyEl.setTop("-20000px");
40517 showAction : function(){
40518 this.bodyEl.setStyle("position", "relative");
40519 this.bodyEl.setTop("");
40520 this.bodyEl.setLeft("");
40521 this.bodyEl.show();
40525 * Set the tooltip for the tab.
40526 * @param {String} tooltip The tab's tooltip
40528 setTooltip : function(text){
40529 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40530 this.textEl.dom.qtip = text;
40531 this.textEl.dom.removeAttribute('title');
40533 this.textEl.dom.title = text;
40537 onTabClick : function(e){
40538 e.preventDefault();
40539 this.tabPanel.activate(this.id);
40542 onTabMouseDown : function(e){
40543 e.preventDefault();
40544 this.tabPanel.activate(this.id);
40547 getWidth : function(){
40548 return this.inner.getWidth();
40551 setWidth : function(width){
40552 var iwidth = width - this.linode.getPadding("lr");
40553 this.inner.setWidth(iwidth);
40554 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40555 this.linode.setWidth(width);
40559 * Show or hide the tab
40560 * @param {Boolean} hidden True to hide or false to show.
40562 setHidden : function(hidden){
40563 this.hidden = hidden;
40564 this.linode.setStyle("display", hidden ? "none" : "");
40568 * Returns true if this tab is "hidden"
40569 * @return {Boolean}
40571 isHidden : function(){
40572 return this.hidden;
40576 * Returns the text for this tab
40579 getText : function(){
40583 autoSize : function(){
40584 //this.el.beginMeasure();
40585 this.textEl.setWidth(1);
40587 * #2804 [new] Tabs in Roojs
40588 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40590 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40591 //this.el.endMeasure();
40595 * Sets the text for the tab (Note: this also sets the tooltip text)
40596 * @param {String} text The tab's text and tooltip
40598 setText : function(text){
40600 this.textEl.update(text);
40601 this.setTooltip(text);
40602 //if(!this.tabPanel.resizeTabs){
40603 // this.autoSize();
40607 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40609 activate : function(){
40610 this.tabPanel.activate(this.id);
40614 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40616 disable : function(){
40617 if(this.tabPanel.active != this){
40618 this.disabled = true;
40619 this.status_node.addClass("disabled");
40624 * Enables this TabPanelItem if it was previously disabled.
40626 enable : function(){
40627 this.disabled = false;
40628 this.status_node.removeClass("disabled");
40632 * Sets the content for this TabPanelItem.
40633 * @param {String} content The content
40634 * @param {Boolean} loadScripts true to look for and load scripts
40636 setContent : function(content, loadScripts){
40637 this.bodyEl.update(content, loadScripts);
40641 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40642 * @return {Roo.UpdateManager} The UpdateManager
40644 getUpdateManager : function(){
40645 return this.bodyEl.getUpdateManager();
40649 * Set a URL to be used to load the content for this TabPanelItem.
40650 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40651 * @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)
40652 * @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)
40653 * @return {Roo.UpdateManager} The UpdateManager
40655 setUrl : function(url, params, loadOnce){
40656 if(this.refreshDelegate){
40657 this.un('activate', this.refreshDelegate);
40659 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40660 this.on("activate", this.refreshDelegate);
40661 return this.bodyEl.getUpdateManager();
40665 _handleRefresh : function(url, params, loadOnce){
40666 if(!loadOnce || !this.loaded){
40667 var updater = this.bodyEl.getUpdateManager();
40668 updater.update(url, params, this._setLoaded.createDelegate(this));
40673 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40674 * Will fail silently if the setUrl method has not been called.
40675 * This does not activate the panel, just updates its content.
40677 refresh : function(){
40678 if(this.refreshDelegate){
40679 this.loaded = false;
40680 this.refreshDelegate();
40685 _setLoaded : function(){
40686 this.loaded = true;
40690 closeClick : function(e){
40693 this.fireEvent("beforeclose", this, o);
40694 if(o.cancel !== true){
40695 this.tabPanel.removeTab(this.id);
40699 * The text displayed in the tooltip for the close icon.
40702 closeText : "Close this tab"
40705 * This script refer to:
40706 * Title: International Telephone Input
40707 * Author: Jack O'Connor
40708 * Code version: v12.1.12
40709 * Availability: https://github.com/jackocnr/intl-tel-input.git
40712 Roo.bootstrap.PhoneInputData = function() {
40715 "Afghanistan (افغانستان)",
40720 "Albania (Shqipëri)",
40725 "Algeria (الجزائر)",
40750 "Antigua and Barbuda",
40760 "Armenia (Հայաստան)",
40776 "Austria (Österreich)",
40781 "Azerbaijan (Azərbaycan)",
40791 "Bahrain (البحرين)",
40796 "Bangladesh (বাংলাদেশ)",
40806 "Belarus (Беларусь)",
40811 "Belgium (België)",
40841 "Bosnia and Herzegovina (Босна и Херцеговина)",
40856 "British Indian Ocean Territory",
40861 "British Virgin Islands",
40871 "Bulgaria (България)",
40881 "Burundi (Uburundi)",
40886 "Cambodia (កម្ពុជា)",
40891 "Cameroon (Cameroun)",
40900 ["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"]
40903 "Cape Verde (Kabu Verdi)",
40908 "Caribbean Netherlands",
40919 "Central African Republic (République centrafricaine)",
40939 "Christmas Island",
40945 "Cocos (Keeling) Islands",
40956 "Comoros (جزر القمر)",
40961 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40966 "Congo (Republic) (Congo-Brazzaville)",
40986 "Croatia (Hrvatska)",
41007 "Czech Republic (Česká republika)",
41012 "Denmark (Danmark)",
41027 "Dominican Republic (República Dominicana)",
41031 ["809", "829", "849"]
41049 "Equatorial Guinea (Guinea Ecuatorial)",
41069 "Falkland Islands (Islas Malvinas)",
41074 "Faroe Islands (Føroyar)",
41095 "French Guiana (Guyane française)",
41100 "French Polynesia (Polynésie française)",
41115 "Georgia (საქართველო)",
41120 "Germany (Deutschland)",
41140 "Greenland (Kalaallit Nunaat)",
41177 "Guinea-Bissau (Guiné Bissau)",
41202 "Hungary (Magyarország)",
41207 "Iceland (Ísland)",
41227 "Iraq (العراق)",
41243 "Israel (ישראל)",
41270 "Jordan (الأردن)",
41275 "Kazakhstan (Казахстан)",
41296 "Kuwait (الكويت)",
41301 "Kyrgyzstan (Кыргызстан)",
41311 "Latvia (Latvija)",
41316 "Lebanon (لبنان)",
41331 "Libya (ليبيا)",
41341 "Lithuania (Lietuva)",
41356 "Macedonia (FYROM) (Македонија)",
41361 "Madagascar (Madagasikara)",
41391 "Marshall Islands",
41401 "Mauritania (موريتانيا)",
41406 "Mauritius (Moris)",
41427 "Moldova (Republica Moldova)",
41437 "Mongolia (Монгол)",
41442 "Montenegro (Crna Gora)",
41452 "Morocco (المغرب)",
41458 "Mozambique (Moçambique)",
41463 "Myanmar (Burma) (မြန်မာ)",
41468 "Namibia (Namibië)",
41483 "Netherlands (Nederland)",
41488 "New Caledonia (Nouvelle-Calédonie)",
41523 "North Korea (조선 민주주의 인민 공화국)",
41528 "Northern Mariana Islands",
41544 "Pakistan (پاکستان)",
41554 "Palestine (فلسطين)",
41564 "Papua New Guinea",
41606 "Réunion (La Réunion)",
41612 "Romania (România)",
41628 "Saint Barthélemy",
41639 "Saint Kitts and Nevis",
41649 "Saint Martin (Saint-Martin (partie française))",
41655 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41660 "Saint Vincent and the Grenadines",
41675 "São Tomé and Príncipe (São Tomé e Príncipe)",
41680 "Saudi Arabia (المملكة العربية السعودية)",
41685 "Senegal (Sénégal)",
41715 "Slovakia (Slovensko)",
41720 "Slovenia (Slovenija)",
41730 "Somalia (Soomaaliya)",
41740 "South Korea (대한민국)",
41745 "South Sudan (جنوب السودان)",
41755 "Sri Lanka (ශ්රී ලංකාව)",
41760 "Sudan (السودان)",
41770 "Svalbard and Jan Mayen",
41781 "Sweden (Sverige)",
41786 "Switzerland (Schweiz)",
41791 "Syria (سوريا)",
41836 "Trinidad and Tobago",
41841 "Tunisia (تونس)",
41846 "Turkey (Türkiye)",
41856 "Turks and Caicos Islands",
41866 "U.S. Virgin Islands",
41876 "Ukraine (Україна)",
41881 "United Arab Emirates (الإمارات العربية المتحدة)",
41903 "Uzbekistan (Oʻzbekiston)",
41913 "Vatican City (Città del Vaticano)",
41924 "Vietnam (Việt Nam)",
41929 "Wallis and Futuna (Wallis-et-Futuna)",
41934 "Western Sahara (الصحراء الغربية)",
41940 "Yemen (اليمن)",
41964 * This script refer to:
41965 * Title: International Telephone Input
41966 * Author: Jack O'Connor
41967 * Code version: v12.1.12
41968 * Availability: https://github.com/jackocnr/intl-tel-input.git
41972 * @class Roo.bootstrap.PhoneInput
41973 * @extends Roo.bootstrap.TriggerField
41974 * An input with International dial-code selection
41976 * @cfg {String} defaultDialCode default '+852'
41977 * @cfg {Array} preferedCountries default []
41980 * Create a new PhoneInput.
41981 * @param {Object} config Configuration options
41984 Roo.bootstrap.PhoneInput = function(config) {
41985 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41988 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41990 listWidth: undefined,
41992 selectedClass: 'active',
41994 invalidClass : "has-warning",
41996 validClass: 'has-success',
41998 allowed: '0123456789',
42003 * @cfg {String} defaultDialCode The default dial code when initializing the input
42005 defaultDialCode: '+852',
42008 * @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
42010 preferedCountries: false,
42012 getAutoCreate : function()
42014 var data = Roo.bootstrap.PhoneInputData();
42015 var align = this.labelAlign || this.parentLabelAlign();
42018 this.allCountries = [];
42019 this.dialCodeMapping = [];
42021 for (var i = 0; i < data.length; i++) {
42023 this.allCountries[i] = {
42027 priority: c[3] || 0,
42028 areaCodes: c[4] || null
42030 this.dialCodeMapping[c[2]] = {
42033 priority: c[3] || 0,
42034 areaCodes: c[4] || null
42046 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42047 maxlength: this.max_length,
42048 cls : 'form-control tel-input',
42049 autocomplete: 'new-password'
42052 var hiddenInput = {
42055 cls: 'hidden-tel-input'
42059 hiddenInput.name = this.name;
42062 if (this.disabled) {
42063 input.disabled = true;
42066 var flag_container = {
42083 cls: this.hasFeedback ? 'has-feedback' : '',
42089 cls: 'dial-code-holder',
42096 cls: 'roo-select2-container input-group',
42103 if (this.fieldLabel.length) {
42106 tooltip: 'This field is required'
42112 cls: 'control-label',
42118 html: this.fieldLabel
42121 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42127 if(this.indicatorpos == 'right') {
42128 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42135 if(align == 'left') {
42143 if(this.labelWidth > 12){
42144 label.style = "width: " + this.labelWidth + 'px';
42146 if(this.labelWidth < 13 && this.labelmd == 0){
42147 this.labelmd = this.labelWidth;
42149 if(this.labellg > 0){
42150 label.cls += ' col-lg-' + this.labellg;
42151 input.cls += ' col-lg-' + (12 - this.labellg);
42153 if(this.labelmd > 0){
42154 label.cls += ' col-md-' + this.labelmd;
42155 container.cls += ' col-md-' + (12 - this.labelmd);
42157 if(this.labelsm > 0){
42158 label.cls += ' col-sm-' + this.labelsm;
42159 container.cls += ' col-sm-' + (12 - this.labelsm);
42161 if(this.labelxs > 0){
42162 label.cls += ' col-xs-' + this.labelxs;
42163 container.cls += ' col-xs-' + (12 - this.labelxs);
42173 var settings = this;
42175 ['xs','sm','md','lg'].map(function(size){
42176 if (settings[size]) {
42177 cfg.cls += ' col-' + size + '-' + settings[size];
42181 this.store = new Roo.data.Store({
42182 proxy : new Roo.data.MemoryProxy({}),
42183 reader : new Roo.data.JsonReader({
42194 'name' : 'dialCode',
42198 'name' : 'priority',
42202 'name' : 'areaCodes',
42209 if(!this.preferedCountries) {
42210 this.preferedCountries = [
42217 var p = this.preferedCountries.reverse();
42220 for (var i = 0; i < p.length; i++) {
42221 for (var j = 0; j < this.allCountries.length; j++) {
42222 if(this.allCountries[j].iso2 == p[i]) {
42223 var t = this.allCountries[j];
42224 this.allCountries.splice(j,1);
42225 this.allCountries.unshift(t);
42231 this.store.proxy.data = {
42233 data: this.allCountries
42239 initEvents : function()
42242 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42244 this.indicator = this.indicatorEl();
42245 this.flag = this.flagEl();
42246 this.dialCodeHolder = this.dialCodeHolderEl();
42248 this.trigger = this.el.select('div.flag-box',true).first();
42249 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42254 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42255 _this.list.setWidth(lw);
42258 this.list.on('mouseover', this.onViewOver, this);
42259 this.list.on('mousemove', this.onViewMove, this);
42260 this.inputEl().on("keyup", this.onKeyUp, this);
42261 this.inputEl().on("keypress", this.onKeyPress, this);
42263 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42265 this.view = new Roo.View(this.list, this.tpl, {
42266 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42269 this.view.on('click', this.onViewClick, this);
42270 this.setValue(this.defaultDialCode);
42273 onTriggerClick : function(e)
42275 Roo.log('trigger click');
42280 if(this.isExpanded()){
42282 this.hasFocus = false;
42284 this.store.load({});
42285 this.hasFocus = true;
42290 isExpanded : function()
42292 return this.list.isVisible();
42295 collapse : function()
42297 if(!this.isExpanded()){
42301 Roo.get(document).un('mousedown', this.collapseIf, this);
42302 Roo.get(document).un('mousewheel', this.collapseIf, this);
42303 this.fireEvent('collapse', this);
42307 expand : function()
42311 if(this.isExpanded() || !this.hasFocus){
42315 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42316 this.list.setWidth(lw);
42319 this.restrictHeight();
42321 Roo.get(document).on('mousedown', this.collapseIf, this);
42322 Roo.get(document).on('mousewheel', this.collapseIf, this);
42324 this.fireEvent('expand', this);
42327 restrictHeight : function()
42329 this.list.alignTo(this.inputEl(), this.listAlign);
42330 this.list.alignTo(this.inputEl(), this.listAlign);
42333 onViewOver : function(e, t)
42335 if(this.inKeyMode){
42338 var item = this.view.findItemFromChild(t);
42341 var index = this.view.indexOf(item);
42342 this.select(index, false);
42347 onViewClick : function(view, doFocus, el, e)
42349 var index = this.view.getSelectedIndexes()[0];
42351 var r = this.store.getAt(index);
42354 this.onSelect(r, index);
42356 if(doFocus !== false && !this.blockFocus){
42357 this.inputEl().focus();
42361 onViewMove : function(e, t)
42363 this.inKeyMode = false;
42366 select : function(index, scrollIntoView)
42368 this.selectedIndex = index;
42369 this.view.select(index);
42370 if(scrollIntoView !== false){
42371 var el = this.view.getNode(index);
42373 this.list.scrollChildIntoView(el, false);
42378 createList : function()
42380 this.list = Roo.get(document.body).createChild({
42382 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42383 style: 'display:none'
42386 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42389 collapseIf : function(e)
42391 var in_combo = e.within(this.el);
42392 var in_list = e.within(this.list);
42393 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42395 if (in_combo || in_list || is_list) {
42401 onSelect : function(record, index)
42403 if(this.fireEvent('beforeselect', this, record, index) !== false){
42405 this.setFlagClass(record.data.iso2);
42406 this.setDialCode(record.data.dialCode);
42407 this.hasFocus = false;
42409 this.fireEvent('select', this, record, index);
42413 flagEl : function()
42415 var flag = this.el.select('div.flag',true).first();
42422 dialCodeHolderEl : function()
42424 var d = this.el.select('input.dial-code-holder',true).first();
42431 setDialCode : function(v)
42433 this.dialCodeHolder.dom.value = '+'+v;
42436 setFlagClass : function(n)
42438 this.flag.dom.className = 'flag '+n;
42441 getValue : function()
42443 var v = this.inputEl().getValue();
42444 if(this.dialCodeHolder) {
42445 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42450 setValue : function(v)
42452 var d = this.getDialCode(v);
42454 //invalid dial code
42455 if(v.length == 0 || !d || d.length == 0) {
42457 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42458 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42464 this.setFlagClass(this.dialCodeMapping[d].iso2);
42465 this.setDialCode(d);
42466 this.inputEl().dom.value = v.replace('+'+d,'');
42467 this.hiddenEl().dom.value = this.getValue();
42472 getDialCode : function(v)
42476 if (v.length == 0) {
42477 return this.dialCodeHolder.dom.value;
42481 if (v.charAt(0) != "+") {
42484 var numericChars = "";
42485 for (var i = 1; i < v.length; i++) {
42486 var c = v.charAt(i);
42489 if (this.dialCodeMapping[numericChars]) {
42490 dialCode = v.substr(1, i);
42492 if (numericChars.length == 4) {
42502 this.setValue(this.defaultDialCode);
42506 hiddenEl : function()
42508 return this.el.select('input.hidden-tel-input',true).first();
42511 // after setting val
42512 onKeyUp : function(e){
42513 this.setValue(this.getValue());
42516 onKeyPress : function(e){
42517 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42524 * @class Roo.bootstrap.MoneyField
42525 * @extends Roo.bootstrap.ComboBox
42526 * Bootstrap MoneyField class
42529 * Create a new MoneyField.
42530 * @param {Object} config Configuration options
42533 Roo.bootstrap.MoneyField = function(config) {
42535 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42539 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42542 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42544 allowDecimals : true,
42546 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42548 decimalSeparator : ".",
42550 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42552 decimalPrecision : 0,
42554 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42556 allowNegative : true,
42558 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42562 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42564 minValue : Number.NEGATIVE_INFINITY,
42566 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42568 maxValue : Number.MAX_VALUE,
42570 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42572 minText : "The minimum value for this field is {0}",
42574 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42576 maxText : "The maximum value for this field is {0}",
42578 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
42579 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42581 nanText : "{0} is not a valid number",
42583 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42587 * @cfg {String} defaults currency of the MoneyField
42588 * value should be in lkey
42590 defaultCurrency : false,
42592 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42594 thousandsDelimiter : false,
42596 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42607 getAutoCreate : function()
42609 var align = this.labelAlign || this.parentLabelAlign();
42621 cls : 'form-control roo-money-amount-input',
42622 autocomplete: 'new-password'
42625 var hiddenInput = {
42629 cls: 'hidden-number-input'
42632 if(this.max_length) {
42633 input.maxlength = this.max_length;
42637 hiddenInput.name = this.name;
42640 if (this.disabled) {
42641 input.disabled = true;
42644 var clg = 12 - this.inputlg;
42645 var cmd = 12 - this.inputmd;
42646 var csm = 12 - this.inputsm;
42647 var cxs = 12 - this.inputxs;
42651 cls : 'row roo-money-field',
42655 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42659 cls: 'roo-select2-container input-group',
42663 cls : 'form-control roo-money-currency-input',
42664 autocomplete: 'new-password',
42666 name : this.currencyName
42670 cls : 'input-group-addon',
42684 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42688 cls: this.hasFeedback ? 'has-feedback' : '',
42699 if (this.fieldLabel.length) {
42702 tooltip: 'This field is required'
42708 cls: 'control-label',
42714 html: this.fieldLabel
42717 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42723 if(this.indicatorpos == 'right') {
42724 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42731 if(align == 'left') {
42739 if(this.labelWidth > 12){
42740 label.style = "width: " + this.labelWidth + 'px';
42742 if(this.labelWidth < 13 && this.labelmd == 0){
42743 this.labelmd = this.labelWidth;
42745 if(this.labellg > 0){
42746 label.cls += ' col-lg-' + this.labellg;
42747 input.cls += ' col-lg-' + (12 - this.labellg);
42749 if(this.labelmd > 0){
42750 label.cls += ' col-md-' + this.labelmd;
42751 container.cls += ' col-md-' + (12 - this.labelmd);
42753 if(this.labelsm > 0){
42754 label.cls += ' col-sm-' + this.labelsm;
42755 container.cls += ' col-sm-' + (12 - this.labelsm);
42757 if(this.labelxs > 0){
42758 label.cls += ' col-xs-' + this.labelxs;
42759 container.cls += ' col-xs-' + (12 - this.labelxs);
42770 var settings = this;
42772 ['xs','sm','md','lg'].map(function(size){
42773 if (settings[size]) {
42774 cfg.cls += ' col-' + size + '-' + settings[size];
42781 initEvents : function()
42783 this.indicator = this.indicatorEl();
42785 this.initCurrencyEvent();
42787 this.initNumberEvent();
42790 initCurrencyEvent : function()
42793 throw "can not find store for combo";
42796 this.store = Roo.factory(this.store, Roo.data);
42797 this.store.parent = this;
42801 this.triggerEl = this.el.select('.input-group-addon', true).first();
42803 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42808 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42809 _this.list.setWidth(lw);
42812 this.list.on('mouseover', this.onViewOver, this);
42813 this.list.on('mousemove', this.onViewMove, this);
42814 this.list.on('scroll', this.onViewScroll, this);
42817 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42820 this.view = new Roo.View(this.list, this.tpl, {
42821 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42824 this.view.on('click', this.onViewClick, this);
42826 this.store.on('beforeload', this.onBeforeLoad, this);
42827 this.store.on('load', this.onLoad, this);
42828 this.store.on('loadexception', this.onLoadException, this);
42830 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42831 "up" : function(e){
42832 this.inKeyMode = true;
42836 "down" : function(e){
42837 if(!this.isExpanded()){
42838 this.onTriggerClick();
42840 this.inKeyMode = true;
42845 "enter" : function(e){
42848 if(this.fireEvent("specialkey", this, e)){
42849 this.onViewClick(false);
42855 "esc" : function(e){
42859 "tab" : function(e){
42862 if(this.fireEvent("specialkey", this, e)){
42863 this.onViewClick(false);
42871 doRelay : function(foo, bar, hname){
42872 if(hname == 'down' || this.scope.isExpanded()){
42873 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42881 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42885 initNumberEvent : function(e)
42887 this.inputEl().on("keydown" , this.fireKey, this);
42888 this.inputEl().on("focus", this.onFocus, this);
42889 this.inputEl().on("blur", this.onBlur, this);
42891 this.inputEl().relayEvent('keyup', this);
42893 if(this.indicator){
42894 this.indicator.addClass('invisible');
42897 this.originalValue = this.getValue();
42899 if(this.validationEvent == 'keyup'){
42900 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42901 this.inputEl().on('keyup', this.filterValidation, this);
42903 else if(this.validationEvent !== false){
42904 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42907 if(this.selectOnFocus){
42908 this.on("focus", this.preFocus, this);
42911 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42912 this.inputEl().on("keypress", this.filterKeys, this);
42914 this.inputEl().relayEvent('keypress', this);
42917 var allowed = "0123456789";
42919 if(this.allowDecimals){
42920 allowed += this.decimalSeparator;
42923 if(this.allowNegative){
42927 if(this.thousandsDelimiter) {
42931 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42933 var keyPress = function(e){
42935 var k = e.getKey();
42937 var c = e.getCharCode();
42940 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42941 allowed.indexOf(String.fromCharCode(c)) === -1
42947 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42951 if(allowed.indexOf(String.fromCharCode(c)) === -1){
42956 this.inputEl().on("keypress", keyPress, this);
42960 onTriggerClick : function(e)
42967 this.loadNext = false;
42969 if(this.isExpanded()){
42974 this.hasFocus = true;
42976 if(this.triggerAction == 'all') {
42977 this.doQuery(this.allQuery, true);
42981 this.doQuery(this.getRawValue());
42984 getCurrency : function()
42986 var v = this.currencyEl().getValue();
42991 restrictHeight : function()
42993 this.list.alignTo(this.currencyEl(), this.listAlign);
42994 this.list.alignTo(this.currencyEl(), this.listAlign);
42997 onViewClick : function(view, doFocus, el, e)
42999 var index = this.view.getSelectedIndexes()[0];
43001 var r = this.store.getAt(index);
43004 this.onSelect(r, index);
43008 onSelect : function(record, index){
43010 if(this.fireEvent('beforeselect', this, record, index) !== false){
43012 this.setFromCurrencyData(index > -1 ? record.data : false);
43016 this.fireEvent('select', this, record, index);
43020 setFromCurrencyData : function(o)
43024 this.lastCurrency = o;
43026 if (this.currencyField) {
43027 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43029 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43032 this.lastSelectionText = currency;
43034 //setting default currency
43035 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43036 this.setCurrency(this.defaultCurrency);
43040 this.setCurrency(currency);
43043 setFromData : function(o)
43047 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43049 this.setFromCurrencyData(c);
43054 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43056 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43059 this.setValue(value);
43063 setCurrency : function(v)
43065 this.currencyValue = v;
43068 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43073 setValue : function(v)
43075 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43081 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43083 this.inputEl().dom.value = (v == '') ? '' :
43084 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43086 if(!this.allowZero && v === '0') {
43087 this.hiddenEl().dom.value = '';
43088 this.inputEl().dom.value = '';
43095 getRawValue : function()
43097 var v = this.inputEl().getValue();
43102 getValue : function()
43104 return this.fixPrecision(this.parseValue(this.getRawValue()));
43107 parseValue : function(value)
43109 if(this.thousandsDelimiter) {
43111 r = new RegExp(",", "g");
43112 value = value.replace(r, "");
43115 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43116 return isNaN(value) ? '' : value;
43120 fixPrecision : function(value)
43122 if(this.thousandsDelimiter) {
43124 r = new RegExp(",", "g");
43125 value = value.replace(r, "");
43128 var nan = isNaN(value);
43130 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43131 return nan ? '' : value;
43133 return parseFloat(value).toFixed(this.decimalPrecision);
43136 decimalPrecisionFcn : function(v)
43138 return Math.floor(v);
43141 validateValue : function(value)
43143 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43147 var num = this.parseValue(value);
43150 this.markInvalid(String.format(this.nanText, value));
43154 if(num < this.minValue){
43155 this.markInvalid(String.format(this.minText, this.minValue));
43159 if(num > this.maxValue){
43160 this.markInvalid(String.format(this.maxText, this.maxValue));
43167 validate : function()
43169 if(this.disabled || this.allowBlank){
43174 var currency = this.getCurrency();
43176 if(this.validateValue(this.getRawValue()) && currency.length){
43181 this.markInvalid();
43185 getName: function()
43190 beforeBlur : function()
43196 var v = this.parseValue(this.getRawValue());
43203 onBlur : function()
43207 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43208 //this.el.removeClass(this.focusClass);
43211 this.hasFocus = false;
43213 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43217 var v = this.getValue();
43219 if(String(v) !== String(this.startValue)){
43220 this.fireEvent('change', this, v, this.startValue);
43223 this.fireEvent("blur", this);
43226 inputEl : function()
43228 return this.el.select('.roo-money-amount-input', true).first();
43231 currencyEl : function()
43233 return this.el.select('.roo-money-currency-input', true).first();
43236 hiddenEl : function()
43238 return this.el.select('input.hidden-number-input',true).first();
43242 * @class Roo.bootstrap.BezierSignature
43243 * @extends Roo.bootstrap.Component
43244 * Bootstrap BezierSignature class
43245 * This script refer to:
43246 * Title: Signature Pad
43248 * Availability: https://github.com/szimek/signature_pad
43251 * Create a new BezierSignature
43252 * @param {Object} config The config object
43255 Roo.bootstrap.BezierSignature = function(config){
43256 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43262 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43269 mouse_btn_down: true,
43272 * @cfg {int} canvas height
43274 canvas_height: '200px',
43277 * @cfg {float|function} Radius of a single dot.
43282 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43287 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43292 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43297 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43302 * @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.
43304 bg_color: 'rgba(0, 0, 0, 0)',
43307 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43309 dot_color: 'black',
43312 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43314 velocity_filter_weight: 0.7,
43317 * @cfg {function} Callback when stroke begin.
43322 * @cfg {function} Callback when stroke end.
43326 getAutoCreate : function()
43328 var cls = 'roo-signature column';
43331 cls += ' ' + this.cls;
43341 for(var i = 0; i < col_sizes.length; i++) {
43342 if(this[col_sizes[i]]) {
43343 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43353 cls: 'roo-signature-body',
43357 cls: 'roo-signature-body-canvas',
43358 height: this.canvas_height,
43359 width: this.canvas_width
43366 style: 'display: none'
43374 initEvents: function()
43376 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43378 var canvas = this.canvasEl();
43380 // mouse && touch event swapping...
43381 canvas.dom.style.touchAction = 'none';
43382 canvas.dom.style.msTouchAction = 'none';
43384 this.mouse_btn_down = false;
43385 canvas.on('mousedown', this._handleMouseDown, this);
43386 canvas.on('mousemove', this._handleMouseMove, this);
43387 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43389 if (window.PointerEvent) {
43390 canvas.on('pointerdown', this._handleMouseDown, this);
43391 canvas.on('pointermove', this._handleMouseMove, this);
43392 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43395 if ('ontouchstart' in window) {
43396 canvas.on('touchstart', this._handleTouchStart, this);
43397 canvas.on('touchmove', this._handleTouchMove, this);
43398 canvas.on('touchend', this._handleTouchEnd, this);
43401 Roo.EventManager.onWindowResize(this.resize, this, true);
43403 // file input event
43404 this.fileEl().on('change', this.uploadImage, this);
43411 resize: function(){
43413 var canvas = this.canvasEl().dom;
43414 var ctx = this.canvasElCtx();
43415 var img_data = false;
43417 if(canvas.width > 0) {
43418 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43420 // setting canvas width will clean img data
43423 var style = window.getComputedStyle ?
43424 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43426 var padding_left = parseInt(style.paddingLeft) || 0;
43427 var padding_right = parseInt(style.paddingRight) || 0;
43429 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43432 ctx.putImageData(img_data, 0, 0);
43436 _handleMouseDown: function(e)
43438 if (e.browserEvent.which === 1) {
43439 this.mouse_btn_down = true;
43440 this.strokeBegin(e);
43444 _handleMouseMove: function (e)
43446 if (this.mouse_btn_down) {
43447 this.strokeMoveUpdate(e);
43451 _handleMouseUp: function (e)
43453 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43454 this.mouse_btn_down = false;
43459 _handleTouchStart: function (e) {
43461 e.preventDefault();
43462 if (e.browserEvent.targetTouches.length === 1) {
43463 // var touch = e.browserEvent.changedTouches[0];
43464 // this.strokeBegin(touch);
43466 this.strokeBegin(e); // assume e catching the correct xy...
43470 _handleTouchMove: function (e) {
43471 e.preventDefault();
43472 // var touch = event.targetTouches[0];
43473 // _this._strokeMoveUpdate(touch);
43474 this.strokeMoveUpdate(e);
43477 _handleTouchEnd: function (e) {
43478 var wasCanvasTouched = e.target === this.canvasEl().dom;
43479 if (wasCanvasTouched) {
43480 e.preventDefault();
43481 // var touch = event.changedTouches[0];
43482 // _this._strokeEnd(touch);
43487 reset: function () {
43488 this._lastPoints = [];
43489 this._lastVelocity = 0;
43490 this._lastWidth = (this.min_width + this.max_width) / 2;
43491 this.canvasElCtx().fillStyle = this.dot_color;
43494 strokeMoveUpdate: function(e)
43496 this.strokeUpdate(e);
43498 if (this.throttle) {
43499 this.throttleStroke(this.strokeUpdate, this.throttle);
43502 this.strokeUpdate(e);
43506 strokeBegin: function(e)
43508 var newPointGroup = {
43509 color: this.dot_color,
43513 if (typeof this.onBegin === 'function') {
43517 this.curve_data.push(newPointGroup);
43519 this.strokeUpdate(e);
43522 strokeUpdate: function(e)
43524 var rect = this.canvasEl().dom.getBoundingClientRect();
43525 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43526 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43527 var lastPoints = lastPointGroup.points;
43528 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43529 var isLastPointTooClose = lastPoint
43530 ? point.distanceTo(lastPoint) <= this.min_distance
43532 var color = lastPointGroup.color;
43533 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43534 var curve = this.addPoint(point);
43536 this.drawDot({color: color, point: point});
43539 this.drawCurve({color: color, curve: curve});
43549 strokeEnd: function(e)
43551 this.strokeUpdate(e);
43552 if (typeof this.onEnd === 'function') {
43557 addPoint: function (point) {
43558 var _lastPoints = this._lastPoints;
43559 _lastPoints.push(point);
43560 if (_lastPoints.length > 2) {
43561 if (_lastPoints.length === 3) {
43562 _lastPoints.unshift(_lastPoints[0]);
43564 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43565 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43566 _lastPoints.shift();
43572 calculateCurveWidths: function (startPoint, endPoint) {
43573 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43574 (1 - this.velocity_filter_weight) * this._lastVelocity;
43576 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43579 start: this._lastWidth
43582 this._lastVelocity = velocity;
43583 this._lastWidth = newWidth;
43587 drawDot: function (_a) {
43588 var color = _a.color, point = _a.point;
43589 var ctx = this.canvasElCtx();
43590 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43592 this.drawCurveSegment(point.x, point.y, width);
43594 ctx.fillStyle = color;
43598 drawCurve: function (_a) {
43599 var color = _a.color, curve = _a.curve;
43600 var ctx = this.canvasElCtx();
43601 var widthDelta = curve.endWidth - curve.startWidth;
43602 var drawSteps = Math.floor(curve.length()) * 2;
43604 ctx.fillStyle = color;
43605 for (var i = 0; i < drawSteps; i += 1) {
43606 var t = i / drawSteps;
43612 var x = uuu * curve.startPoint.x;
43613 x += 3 * uu * t * curve.control1.x;
43614 x += 3 * u * tt * curve.control2.x;
43615 x += ttt * curve.endPoint.x;
43616 var y = uuu * curve.startPoint.y;
43617 y += 3 * uu * t * curve.control1.y;
43618 y += 3 * u * tt * curve.control2.y;
43619 y += ttt * curve.endPoint.y;
43620 var width = curve.startWidth + ttt * widthDelta;
43621 this.drawCurveSegment(x, y, width);
43627 drawCurveSegment: function (x, y, width) {
43628 var ctx = this.canvasElCtx();
43630 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43631 this.is_empty = false;
43636 var ctx = this.canvasElCtx();
43637 var canvas = this.canvasEl().dom;
43638 ctx.fillStyle = this.bg_color;
43639 ctx.clearRect(0, 0, canvas.width, canvas.height);
43640 ctx.fillRect(0, 0, canvas.width, canvas.height);
43641 this.curve_data = [];
43643 this.is_empty = true;
43648 return this.el.select('input',true).first();
43651 canvasEl: function()
43653 return this.el.select('canvas',true).first();
43656 canvasElCtx: function()
43658 return this.el.select('canvas',true).first().dom.getContext('2d');
43661 getImage: function(type)
43663 if(this.is_empty) {
43668 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43671 drawFromImage: function(img_src)
43673 var img = new Image();
43675 img.onload = function(){
43676 this.canvasElCtx().drawImage(img, 0, 0);
43681 this.is_empty = false;
43684 selectImage: function()
43686 this.fileEl().dom.click();
43689 uploadImage: function(e)
43691 var reader = new FileReader();
43693 reader.onload = function(e){
43694 var img = new Image();
43695 img.onload = function(){
43697 this.canvasElCtx().drawImage(img, 0, 0);
43699 img.src = e.target.result;
43702 reader.readAsDataURL(e.target.files[0]);
43705 // Bezier Point Constructor
43706 Point: (function () {
43707 function Point(x, y, time) {
43710 this.time = time || Date.now();
43712 Point.prototype.distanceTo = function (start) {
43713 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43715 Point.prototype.equals = function (other) {
43716 return this.x === other.x && this.y === other.y && this.time === other.time;
43718 Point.prototype.velocityFrom = function (start) {
43719 return this.time !== start.time
43720 ? this.distanceTo(start) / (this.time - start.time)
43727 // Bezier Constructor
43728 Bezier: (function () {
43729 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43730 this.startPoint = startPoint;
43731 this.control2 = control2;
43732 this.control1 = control1;
43733 this.endPoint = endPoint;
43734 this.startWidth = startWidth;
43735 this.endWidth = endWidth;
43737 Bezier.fromPoints = function (points, widths, scope) {
43738 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43739 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43740 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43742 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43743 var dx1 = s1.x - s2.x;
43744 var dy1 = s1.y - s2.y;
43745 var dx2 = s2.x - s3.x;
43746 var dy2 = s2.y - s3.y;
43747 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43748 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43749 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43750 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43751 var dxm = m1.x - m2.x;
43752 var dym = m1.y - m2.y;
43753 var k = l2 / (l1 + l2);
43754 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43755 var tx = s2.x - cm.x;
43756 var ty = s2.y - cm.y;
43758 c1: new scope.Point(m1.x + tx, m1.y + ty),
43759 c2: new scope.Point(m2.x + tx, m2.y + ty)
43762 Bezier.prototype.length = function () {
43767 for (var i = 0; i <= steps; i += 1) {
43769 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43770 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43772 var xdiff = cx - px;
43773 var ydiff = cy - py;
43774 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43781 Bezier.prototype.point = function (t, start, c1, c2, end) {
43782 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43783 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43784 + (3.0 * c2 * (1.0 - t) * t * t)
43785 + (end * t * t * t);
43790 throttleStroke: function(fn, wait) {
43791 if (wait === void 0) { wait = 250; }
43793 var timeout = null;
43797 var later = function () {
43798 previous = Date.now();
43800 result = fn.apply(storedContext, storedArgs);
43802 storedContext = null;
43806 return function wrapper() {
43808 for (var _i = 0; _i < arguments.length; _i++) {
43809 args[_i] = arguments[_i];
43811 var now = Date.now();
43812 var remaining = wait - (now - previous);
43813 storedContext = this;
43815 if (remaining <= 0 || remaining > wait) {
43817 clearTimeout(timeout);
43821 result = fn.apply(storedContext, storedArgs);
43823 storedContext = null;
43827 else if (!timeout) {
43828 timeout = window.setTimeout(later, remaining);