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.
2023 containerEl: false, // body container
2024 bodyEl: false, // card-body
2025 headerContainerEl : false, //
2028 layoutCls : function()
2032 Roo.log(this.margin_bottom.length);
2033 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2034 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2036 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2037 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2039 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2040 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2044 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2045 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2046 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2050 // more generic support?
2058 // Roo.log("Call onRender: " + this.xtype);
2059 /* We are looking at something like this.
2061 <img src="..." class="card-img-top" alt="...">
2062 <div class="card-body">
2063 <h5 class="card-title">Card title</h5>
2064 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2066 >> this bit is really the body...
2067 <div> << we will ad dthis in hopefully it will not break shit.
2069 ** card text does not actually have any styling...
2071 <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>
2074 <a href="#" class="card-link">Card link</a>
2077 <div class="card-footer">
2078 <small class="text-muted">Last updated 3 mins ago</small>
2082 getAutoCreate : function(){
2090 if (this.weight.length && this.weight != 'light') {
2091 cfg.cls += ' text-white';
2093 cfg.cls += ' text-dark'; // need as it's nested..
2095 if (this.weight.length) {
2096 cfg.cls += ' bg-' + this.weight;
2099 cfg.cls += this.layoutCls();
2102 var hdr_ctr = false;
2103 if (this.header.length) {
2105 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2106 cls : 'card-header',
2114 cls : 'card-header d-none',
2120 if (this.collapsable) {
2123 cls : 'd-block user-select-none',
2127 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2132 hdr.cn.push(hdr_ctr);
2137 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2142 if (this.header_image.length) {
2145 cls : 'card-img-top',
2146 src: this.header_image // escape?
2151 cls : 'card-img-top d-none'
2157 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2161 if (this.collapsable || this.rotateable) {
2164 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2171 if (this.title.length) {
2175 src: this.title // escape?
2179 if (this.subtitle.length) {
2183 src: this.subtitle // escape?
2189 cls : 'roo-card-body-ctr'
2192 if (this.html.length) {
2198 // fixme ? handle objects?
2200 if (this.footer.length) {
2203 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2208 cfg.cn.push({cls : 'card-footer d-none'});
2217 getCardHeader : function()
2219 var ret = this.el.select('.card-header',true).first();
2220 if (ret.hasClass('d-none')) {
2221 ret.removeClass('d-none');
2226 getCardFooter : function()
2228 var ret = this.el.select('.card-footer',true).first();
2229 if (ret.hasClass('d-none')) {
2230 ret.removeClass('d-none');
2235 getCardImageTop : function()
2237 var ret = this.el.select('.card-img-top',true).first();
2238 if (ret.hasClass('d-none')) {
2239 ret.removeClass('d-none');
2245 getChildContainer : function()
2251 return this.el.select('.roo-card-body-ctr',true).first();
2254 initEvents: function()
2256 this.bodyEl = this.el.select('.card-body',true).first();
2257 this.containerEl = this.getChildContainer();
2259 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2260 containerScroll: true,
2261 ddGroup: this.drag_group || 'default_card_drag_group'
2263 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2265 if (this.dropable) {
2266 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2267 containerScroll: true,
2268 ddGroup: this.drop_group || 'default_card_drag_group'
2270 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2271 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2272 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2273 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2274 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2277 if (this.collapsable) {
2278 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2280 if (this.rotateable) {
2281 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2283 this.collapsableEl = this.el.select('.roo-collapsable').first();
2285 this.footerEl = this.el.select('.card-footer').first();
2286 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2287 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2288 this.headerEl = this.el.select('.card-header',true).first();
2291 this.el.addClass('roo-card-rotated');
2292 this.fireEvent('rotate', this, true);
2296 getDragData : function(e)
2298 var target = this.getEl();
2300 //this.handleSelection(e);
2305 nodes: this.getEl(),
2310 dragData.ddel = target.dom ; // the div element
2311 Roo.log(target.getWidth( ));
2312 dragData.ddel.style.width = target.getWidth() + 'px';
2319 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2320 * whole Element becomes the target, and this causes the drop gesture to append.
2322 getTargetFromEvent : function(e, dragged_card_el)
2324 var target = e.getTarget();
2325 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2326 target = target.parentNode;
2337 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2338 // see if target is one of the 'cards'...
2341 //Roo.log(this.items.length);
2344 var last_card_n = 0;
2346 for (var i = 0;i< this.items.length;i++) {
2348 if (!this.items[i].el.hasClass('card')) {
2351 pos = this.getDropPoint(e, this.items[i].el.dom);
2353 cards_len = ret.cards.length;
2354 //Roo.log(this.items[i].el.dom.id);
2355 ret.cards.push(this.items[i]);
2357 if (ret.card_n < 0 && pos == 'above') {
2358 ret.position = cards_len > 0 ? 'below' : pos;
2359 ret.items_n = i > 0 ? i - 1 : 0;
2360 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2361 ret.card = ret.cards[ret.card_n];
2364 if (!ret.cards.length) {
2366 ret.position = 'below';
2370 // could not find a card.. stick it at the end..
2371 if (ret.card_n < 0) {
2372 ret.card_n = last_card_n;
2373 ret.card = ret.cards[last_card_n];
2374 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2375 ret.position = 'below';
2378 if (this.items[ret.items_n].el == dragged_card_el) {
2382 if (ret.position == 'below') {
2383 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2385 if (card_after && card_after.el == dragged_card_el) {
2392 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2394 if (card_before && card_before.el == dragged_card_el) {
2401 onNodeEnter : function(n, dd, e, data){
2404 onNodeOver : function(n, dd, e, data)
2407 var target_info = this.getTargetFromEvent(e,data.source.el);
2408 if (target_info === false) {
2409 this.dropPlaceHolder('hide');
2412 Roo.log(['getTargetFromEvent', target_info ]);
2415 this.dropPlaceHolder('show', target_info,data);
2419 onNodeOut : function(n, dd, e, data){
2420 this.dropPlaceHolder('hide');
2423 onNodeDrop : function(n, dd, e, data)
2426 // call drop - return false if
2428 // this could actually fail - if the Network drops..
2429 // we will ignore this at present..- client should probably reload
2430 // the whole set of cards if stuff like that fails.
2433 var info = this.getTargetFromEvent(e,data.source.el);
2434 if (info === false) {
2437 this.dropPlaceHolder('hide');
2443 this.acceptCard(data.source, info.position, info.card, info.items_n);
2447 firstChildCard : function()
2449 for (var i = 0;i< this.items.length;i++) {
2451 if (!this.items[i].el.hasClass('card')) {
2454 return this.items[i];
2456 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2461 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2464 acceptCard : function(move_card, position, next_to_card )
2466 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2470 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2473 var dom = move_card.el.dom;
2474 dom.parentNode.removeChild(dom);
2477 if (next_to_card !== false) {
2478 var cardel = next_to_card.el.dom;
2480 if (position == 'above') {
2481 cardel.parentNode.insertBefore(dom, cardel);
2482 } else if (cardel.nextSibling) {
2483 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2485 cardel.parentNode.append(dom);
2488 // card container???
2489 this.containerEl.dom.append(dom);
2492 //FIXME HANDLE card = true
2494 // add this to the correct place in items.
2498 // remove Card from items.
2500 var old_parent = move_card.parent();
2502 old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2504 if (this.items.length) {
2506 //Roo.log([info.items_n, info.position, this.items.length]);
2507 for (var i =0; i < this.items.length; i++) {
2508 if (i == to_items_n && position == 'above') {
2509 nitems.push(move_card);
2511 nitems.push(this.items[i]);
2512 if (i == to_items_n && position == 'below') {
2513 nitems.push(move_card);
2516 this.items = nitems;
2517 Roo.log(this.items);
2519 this.items.push(move_card);
2522 move_card.parentId = this.id;
2530 /** Decide whether to drop above or below a View node. */
2531 getDropPoint : function(e, n, dd)
2536 if (n == this.containerEl.dom) {
2539 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2540 var c = t + (b - t) / 2;
2541 var y = Roo.lib.Event.getPageY(e);
2548 onToggleCollapse : function(e)
2550 if (this.collapsed) {
2551 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2552 this.collapsableEl.addClass('show');
2553 this.collapsed = false;
2556 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2557 this.collapsableEl.removeClass('show');
2558 this.collapsed = true;
2563 onToggleRotate : function(e)
2565 this.collapsableEl.removeClass('show');
2566 this.footerEl.removeClass('d-none');
2567 this.el.removeClass('roo-card-rotated');
2568 this.el.removeClass('d-none');
2571 this.collapsableEl.addClass('show');
2572 this.rotated = false;
2573 this.fireEvent('rotate', this, this.rotated);
2576 this.el.addClass('roo-card-rotated');
2577 this.footerEl.addClass('d-none');
2578 this.el.select('.roo-collapsable').removeClass('show');
2580 this.rotated = true;
2581 this.fireEvent('rotate', this, this.rotated);
2585 dropPlaceHolder: function (action, info, data)
2587 if (this.dropEl === false) {
2588 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2592 this.dropEl.removeClass(['d-none', 'd-block']);
2593 if (action == 'hide') {
2595 this.dropEl.addClass('d-none');
2598 // FIXME - info.card == true!!!
2599 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2601 if (info.card !== true) {
2602 var cardel = info.card.el.dom;
2604 if (info.position == 'above') {
2605 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2606 } else if (cardel.nextSibling) {
2607 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2609 cardel.parentNode.append(this.dropEl.dom);
2612 // card container???
2613 this.containerEl.dom.append(this.dropEl.dom);
2616 this.dropEl.addClass('d-block roo-card-dropzone');
2618 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2625 setHeaderText: function(html)
2627 this.headerContainerEl.dom.innerHTML = html;
2636 * Card header - holder for the card header elements.
2641 * @class Roo.bootstrap.CardHeader
2642 * @extends Roo.bootstrap.Element
2643 * Bootstrap CardHeader class
2645 * Create a new Card Header - that you can embed children into
2646 * @param {Object} config The config object
2649 Roo.bootstrap.CardHeader = function(config){
2650 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2653 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2656 container_method : 'getCardHeader'
2669 * Card footer - holder for the card footer elements.
2674 * @class Roo.bootstrap.CardFooter
2675 * @extends Roo.bootstrap.Element
2676 * Bootstrap CardFooter class
2678 * Create a new Card Footer - that you can embed children into
2679 * @param {Object} config The config object
2682 Roo.bootstrap.CardFooter = function(config){
2683 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2686 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2689 container_method : 'getCardFooter'
2702 * Card header - holder for the card header elements.
2707 * @class Roo.bootstrap.CardImageTop
2708 * @extends Roo.bootstrap.Element
2709 * Bootstrap CardImageTop class
2711 * Create a new Card Image Top container
2712 * @param {Object} config The config object
2715 Roo.bootstrap.CardImageTop = function(config){
2716 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2719 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2722 container_method : 'getCardImageTop'
2740 * @class Roo.bootstrap.Img
2741 * @extends Roo.bootstrap.Component
2742 * Bootstrap Img class
2743 * @cfg {Boolean} imgResponsive false | true
2744 * @cfg {String} border rounded | circle | thumbnail
2745 * @cfg {String} src image source
2746 * @cfg {String} alt image alternative text
2747 * @cfg {String} href a tag href
2748 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2749 * @cfg {String} xsUrl xs image source
2750 * @cfg {String} smUrl sm image source
2751 * @cfg {String} mdUrl md image source
2752 * @cfg {String} lgUrl lg image source
2755 * Create a new Input
2756 * @param {Object} config The config object
2759 Roo.bootstrap.Img = function(config){
2760 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2766 * The img click event for the img.
2767 * @param {Roo.EventObject} e
2773 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2775 imgResponsive: true,
2785 getAutoCreate : function()
2787 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2788 return this.createSingleImg();
2793 cls: 'roo-image-responsive-group',
2798 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2800 if(!_this[size + 'Url']){
2806 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2807 html: _this.html || cfg.html,
2808 src: _this[size + 'Url']
2811 img.cls += ' roo-image-responsive-' + size;
2813 var s = ['xs', 'sm', 'md', 'lg'];
2815 s.splice(s.indexOf(size), 1);
2817 Roo.each(s, function(ss){
2818 img.cls += ' hidden-' + ss;
2821 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2822 cfg.cls += ' img-' + _this.border;
2826 cfg.alt = _this.alt;
2839 a.target = _this.target;
2843 cfg.cn.push((_this.href) ? a : img);
2850 createSingleImg : function()
2854 cls: (this.imgResponsive) ? 'img-responsive' : '',
2856 src : 'about:blank' // just incase src get's set to undefined?!?
2859 cfg.html = this.html || cfg.html;
2861 cfg.src = this.src || cfg.src;
2863 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2864 cfg.cls += ' img-' + this.border;
2881 a.target = this.target;
2886 return (this.href) ? a : cfg;
2889 initEvents: function()
2892 this.el.on('click', this.onClick, this);
2897 onClick : function(e)
2899 Roo.log('img onclick');
2900 this.fireEvent('click', this, e);
2903 * Sets the url of the image - used to update it
2904 * @param {String} url the url of the image
2907 setSrc : function(url)
2911 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2912 this.el.dom.src = url;
2916 this.el.select('img', true).first().dom.src = url;
2932 * @class Roo.bootstrap.Link
2933 * @extends Roo.bootstrap.Component
2934 * Bootstrap Link Class
2935 * @cfg {String} alt image alternative text
2936 * @cfg {String} href a tag href
2937 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2938 * @cfg {String} html the content of the link.
2939 * @cfg {String} anchor name for the anchor link
2940 * @cfg {String} fa - favicon
2942 * @cfg {Boolean} preventDefault (true | false) default false
2946 * Create a new Input
2947 * @param {Object} config The config object
2950 Roo.bootstrap.Link = function(config){
2951 Roo.bootstrap.Link.superclass.constructor.call(this, config);
2957 * The img click event for the img.
2958 * @param {Roo.EventObject} e
2964 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
2968 preventDefault: false,
2974 getAutoCreate : function()
2976 var html = this.html || '';
2978 if (this.fa !== false) {
2979 html = '<i class="fa fa-' + this.fa + '"></i>';
2984 // anchor's do not require html/href...
2985 if (this.anchor === false) {
2987 cfg.href = this.href || '#';
2989 cfg.name = this.anchor;
2990 if (this.html !== false || this.fa !== false) {
2993 if (this.href !== false) {
2994 cfg.href = this.href;
2998 if(this.alt !== false){
3003 if(this.target !== false) {
3004 cfg.target = this.target;
3010 initEvents: function() {
3012 if(!this.href || this.preventDefault){
3013 this.el.on('click', this.onClick, this);
3017 onClick : function(e)
3019 if(this.preventDefault){
3022 //Roo.log('img onclick');
3023 this.fireEvent('click', this, e);
3036 * @class Roo.bootstrap.Header
3037 * @extends Roo.bootstrap.Component
3038 * Bootstrap Header class
3039 * @cfg {String} html content of header
3040 * @cfg {Number} level (1|2|3|4|5|6) default 1
3043 * Create a new Header
3044 * @param {Object} config The config object
3048 Roo.bootstrap.Header = function(config){
3049 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3052 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3060 getAutoCreate : function(){
3065 tag: 'h' + (1 *this.level),
3066 html: this.html || ''
3078 * Ext JS Library 1.1.1
3079 * Copyright(c) 2006-2007, Ext JS, LLC.
3081 * Originally Released Under LGPL - original licence link has changed is not relivant.
3084 * <script type="text/javascript">
3088 * @class Roo.bootstrap.MenuMgr
3089 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3092 Roo.bootstrap.MenuMgr = function(){
3093 var menus, active, groups = {}, attached = false, lastShow = new Date();
3095 // private - called when first menu is created
3098 active = new Roo.util.MixedCollection();
3099 Roo.get(document).addKeyListener(27, function(){
3100 if(active.length > 0){
3108 if(active && active.length > 0){
3109 var c = active.clone();
3119 if(active.length < 1){
3120 Roo.get(document).un("mouseup", onMouseDown);
3128 var last = active.last();
3129 lastShow = new Date();
3132 Roo.get(document).on("mouseup", onMouseDown);
3137 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3138 m.parentMenu.activeChild = m;
3139 }else if(last && last.isVisible()){
3140 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3145 function onBeforeHide(m){
3147 m.activeChild.hide();
3149 if(m.autoHideTimer){
3150 clearTimeout(m.autoHideTimer);
3151 delete m.autoHideTimer;
3156 function onBeforeShow(m){
3157 var pm = m.parentMenu;
3158 if(!pm && !m.allowOtherMenus){
3160 }else if(pm && pm.activeChild && active != m){
3161 pm.activeChild.hide();
3165 // private this should really trigger on mouseup..
3166 function onMouseDown(e){
3167 Roo.log("on Mouse Up");
3169 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3170 Roo.log("MenuManager hideAll");
3179 function onBeforeCheck(mi, state){
3181 var g = groups[mi.group];
3182 for(var i = 0, l = g.length; i < l; i++){
3184 g[i].setChecked(false);
3193 * Hides all menus that are currently visible
3195 hideAll : function(){
3200 register : function(menu){
3204 menus[menu.id] = menu;
3205 menu.on("beforehide", onBeforeHide);
3206 menu.on("hide", onHide);
3207 menu.on("beforeshow", onBeforeShow);
3208 menu.on("show", onShow);
3210 if(g && menu.events["checkchange"]){
3214 groups[g].push(menu);
3215 menu.on("checkchange", onCheck);
3220 * Returns a {@link Roo.menu.Menu} object
3221 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3222 * be used to generate and return a new Menu instance.
3224 get : function(menu){
3225 if(typeof menu == "string"){ // menu id
3227 }else if(menu.events){ // menu instance
3230 /*else if(typeof menu.length == 'number'){ // array of menu items?
3231 return new Roo.bootstrap.Menu({items:menu});
3232 }else{ // otherwise, must be a config
3233 return new Roo.bootstrap.Menu(menu);
3240 unregister : function(menu){
3241 delete menus[menu.id];
3242 menu.un("beforehide", onBeforeHide);
3243 menu.un("hide", onHide);
3244 menu.un("beforeshow", onBeforeShow);
3245 menu.un("show", onShow);
3247 if(g && menu.events["checkchange"]){
3248 groups[g].remove(menu);
3249 menu.un("checkchange", onCheck);
3254 registerCheckable : function(menuItem){
3255 var g = menuItem.group;
3260 groups[g].push(menuItem);
3261 menuItem.on("beforecheckchange", onBeforeCheck);
3266 unregisterCheckable : function(menuItem){
3267 var g = menuItem.group;
3269 groups[g].remove(menuItem);
3270 menuItem.un("beforecheckchange", onBeforeCheck);
3282 * @class Roo.bootstrap.Menu
3283 * @extends Roo.bootstrap.Component
3284 * Bootstrap Menu class - container for MenuItems
3285 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3286 * @cfg {bool} hidden if the menu should be hidden when rendered.
3287 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3288 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3292 * @param {Object} config The config object
3296 Roo.bootstrap.Menu = function(config){
3297 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3298 if (this.registerMenu && this.type != 'treeview') {
3299 Roo.bootstrap.MenuMgr.register(this);
3306 * Fires before this menu is displayed (return false to block)
3307 * @param {Roo.menu.Menu} this
3312 * Fires before this menu is hidden (return false to block)
3313 * @param {Roo.menu.Menu} this
3318 * Fires after this menu is displayed
3319 * @param {Roo.menu.Menu} this
3324 * Fires after this menu is hidden
3325 * @param {Roo.menu.Menu} this
3330 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3331 * @param {Roo.menu.Menu} this
3332 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3333 * @param {Roo.EventObject} e
3338 * Fires when the mouse is hovering over this menu
3339 * @param {Roo.menu.Menu} this
3340 * @param {Roo.EventObject} e
3341 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3346 * Fires when the mouse exits this menu
3347 * @param {Roo.menu.Menu} this
3348 * @param {Roo.EventObject} e
3349 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3354 * Fires when a menu item contained in this menu is clicked
3355 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3356 * @param {Roo.EventObject} e
3360 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3363 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3367 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3370 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3372 registerMenu : true,
3374 menuItems :false, // stores the menu items..
3384 getChildContainer : function() {
3388 getAutoCreate : function(){
3390 //if (['right'].indexOf(this.align)!==-1) {
3391 // cfg.cn[1].cls += ' pull-right'
3397 cls : 'dropdown-menu' ,
3398 style : 'z-index:1000'
3402 if (this.type === 'submenu') {
3403 cfg.cls = 'submenu active';
3405 if (this.type === 'treeview') {
3406 cfg.cls = 'treeview-menu';
3411 initEvents : function() {
3413 // Roo.log("ADD event");
3414 // Roo.log(this.triggerEl.dom);
3416 this.triggerEl.on('click', this.onTriggerClick, this);
3418 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3421 if (this.triggerEl.hasClass('nav-item')) {
3422 // dropdown toggle on the 'a' in BS4?
3423 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3425 this.triggerEl.addClass('dropdown-toggle');
3428 this.el.on('touchstart' , this.onTouch, this);
3430 this.el.on('click' , this.onClick, this);
3432 this.el.on("mouseover", this.onMouseOver, this);
3433 this.el.on("mouseout", this.onMouseOut, this);
3437 findTargetItem : function(e)
3439 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3443 //Roo.log(t); Roo.log(t.id);
3445 //Roo.log(this.menuitems);
3446 return this.menuitems.get(t.id);
3448 //return this.items.get(t.menuItemId);
3454 onTouch : function(e)
3456 Roo.log("menu.onTouch");
3457 //e.stopEvent(); this make the user popdown broken
3461 onClick : function(e)
3463 Roo.log("menu.onClick");
3465 var t = this.findTargetItem(e);
3466 if(!t || t.isContainer){
3471 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3472 if(t == this.activeItem && t.shouldDeactivate(e)){
3473 this.activeItem.deactivate();
3474 delete this.activeItem;
3478 this.setActiveItem(t, true);
3486 Roo.log('pass click event');
3490 this.fireEvent("click", this, t, e);
3494 if(!t.href.length || t.href == '#'){
3495 (function() { _this.hide(); }).defer(100);
3500 onMouseOver : function(e){
3501 var t = this.findTargetItem(e);
3504 // if(t.canActivate && !t.disabled){
3505 // this.setActiveItem(t, true);
3509 this.fireEvent("mouseover", this, e, t);
3511 isVisible : function(){
3512 return !this.hidden;
3514 onMouseOut : function(e){
3515 var t = this.findTargetItem(e);
3518 // if(t == this.activeItem && t.shouldDeactivate(e)){
3519 // this.activeItem.deactivate();
3520 // delete this.activeItem;
3523 this.fireEvent("mouseout", this, e, t);
3528 * Displays this menu relative to another element
3529 * @param {String/HTMLElement/Roo.Element} element The element to align to
3530 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3531 * the element (defaults to this.defaultAlign)
3532 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3534 show : function(el, pos, parentMenu)
3536 if (false === this.fireEvent("beforeshow", this)) {
3537 Roo.log("show canceled");
3540 this.parentMenu = parentMenu;
3545 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3548 * Displays this menu at a specific xy position
3549 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3550 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3552 showAt : function(xy, parentMenu, /* private: */_e){
3553 this.parentMenu = parentMenu;
3558 this.fireEvent("beforeshow", this);
3559 //xy = this.el.adjustForConstraints(xy);
3563 this.hideMenuItems();
3564 this.hidden = false;
3565 this.triggerEl.addClass('open');
3566 this.el.addClass('show');
3568 // reassign x when hitting right
3569 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3570 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3573 // reassign y when hitting bottom
3574 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3575 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3578 // but the list may align on trigger left or trigger top... should it be a properity?
3580 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3585 this.fireEvent("show", this);
3591 this.doFocus.defer(50, this);
3595 doFocus : function(){
3597 this.focusEl.focus();
3602 * Hides this menu and optionally all parent menus
3603 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3605 hide : function(deep)
3607 if (false === this.fireEvent("beforehide", this)) {
3608 Roo.log("hide canceled");
3611 this.hideMenuItems();
3612 if(this.el && this.isVisible()){
3614 if(this.activeItem){
3615 this.activeItem.deactivate();
3616 this.activeItem = null;
3618 this.triggerEl.removeClass('open');;
3619 this.el.removeClass('show');
3621 this.fireEvent("hide", this);
3623 if(deep === true && this.parentMenu){
3624 this.parentMenu.hide(true);
3628 onTriggerClick : function(e)
3630 Roo.log('trigger click');
3632 var target = e.getTarget();
3634 Roo.log(target.nodeName.toLowerCase());
3636 if(target.nodeName.toLowerCase() === 'i'){
3642 onTriggerPress : function(e)
3644 Roo.log('trigger press');
3645 //Roo.log(e.getTarget());
3646 // Roo.log(this.triggerEl.dom);
3648 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3649 var pel = Roo.get(e.getTarget());
3650 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3651 Roo.log('is treeview or dropdown?');
3655 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3659 if (this.isVisible()) {
3664 this.show(this.triggerEl, '?', false);
3667 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3674 hideMenuItems : function()
3676 Roo.log("hide Menu Items");
3681 this.el.select('.open',true).each(function(aa) {
3683 aa.removeClass('open');
3687 addxtypeChild : function (tree, cntr) {
3688 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3690 this.menuitems.add(comp);
3702 this.getEl().dom.innerHTML = '';
3703 this.menuitems.clear();
3717 * @class Roo.bootstrap.MenuItem
3718 * @extends Roo.bootstrap.Component
3719 * Bootstrap MenuItem class
3720 * @cfg {String} html the menu label
3721 * @cfg {String} href the link
3722 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3723 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3724 * @cfg {Boolean} active used on sidebars to highlight active itesm
3725 * @cfg {String} fa favicon to show on left of menu item.
3726 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3730 * Create a new MenuItem
3731 * @param {Object} config The config object
3735 Roo.bootstrap.MenuItem = function(config){
3736 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3741 * The raw click event for the entire grid.
3742 * @param {Roo.bootstrap.MenuItem} this
3743 * @param {Roo.EventObject} e
3749 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3753 preventDefault: false,
3754 isContainer : false,
3758 getAutoCreate : function(){
3760 if(this.isContainer){
3763 cls: 'dropdown-menu-item '
3773 cls : 'dropdown-item',
3778 if (this.fa !== false) {
3781 cls : 'fa fa-' + this.fa
3790 cls: 'dropdown-menu-item',
3793 if (this.parent().type == 'treeview') {
3794 cfg.cls = 'treeview-menu';
3797 cfg.cls += ' active';
3802 anc.href = this.href || cfg.cn[0].href ;
3803 ctag.html = this.html || cfg.cn[0].html ;
3807 initEvents: function()
3809 if (this.parent().type == 'treeview') {
3810 this.el.select('a').on('click', this.onClick, this);
3814 this.menu.parentType = this.xtype;
3815 this.menu.triggerEl = this.el;
3816 this.menu = this.addxtype(Roo.apply({}, this.menu));
3820 onClick : function(e)
3822 Roo.log('item on click ');
3824 if(this.preventDefault){
3827 //this.parent().hideMenuItems();
3829 this.fireEvent('click', this, e);
3848 * @class Roo.bootstrap.MenuSeparator
3849 * @extends Roo.bootstrap.Component
3850 * Bootstrap MenuSeparator class
3853 * Create a new MenuItem
3854 * @param {Object} config The config object
3858 Roo.bootstrap.MenuSeparator = function(config){
3859 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3862 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3864 getAutoCreate : function(){
3883 * @class Roo.bootstrap.Modal
3884 * @extends Roo.bootstrap.Component
3885 * Bootstrap Modal class
3886 * @cfg {String} title Title of dialog
3887 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3888 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3889 * @cfg {Boolean} specificTitle default false
3890 * @cfg {Array} buttons Array of buttons or standard button set..
3891 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3892 * @cfg {Boolean} animate default true
3893 * @cfg {Boolean} allow_close default true
3894 * @cfg {Boolean} fitwindow default false
3895 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3896 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3897 * @cfg {String} size (sm|lg|xl) default empty
3898 * @cfg {Number} max_width set the max width of modal
3899 * @cfg {Boolean} editableTitle can the title be edited
3904 * Create a new Modal Dialog
3905 * @param {Object} config The config object
3908 Roo.bootstrap.Modal = function(config){
3909 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3914 * The raw btnclick event for the button
3915 * @param {Roo.EventObject} e
3920 * Fire when dialog resize
3921 * @param {Roo.bootstrap.Modal} this
3922 * @param {Roo.EventObject} e
3926 * @event titlechanged
3927 * Fire when the editable title has been changed
3928 * @param {Roo.bootstrap.Modal} this
3929 * @param {Roo.EventObject} value
3931 "titlechanged" : true
3934 this.buttons = this.buttons || [];
3937 this.tmpl = Roo.factory(this.tmpl);
3942 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3944 title : 'test dialog',
3954 specificTitle: false,
3956 buttonPosition: 'right',
3978 editableTitle : false,
3980 onRender : function(ct, position)
3982 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3985 var cfg = Roo.apply({}, this.getAutoCreate());
3988 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3990 //if (!cfg.name.length) {
3994 cfg.cls += ' ' + this.cls;
3997 cfg.style = this.style;
3999 this.el = Roo.get(document.body).createChild(cfg, position);
4001 //var type = this.el.dom.type;
4004 if(this.tabIndex !== undefined){
4005 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4008 this.dialogEl = this.el.select('.modal-dialog',true).first();
4009 this.bodyEl = this.el.select('.modal-body',true).first();
4010 this.closeEl = this.el.select('.modal-header .close', true).first();
4011 this.headerEl = this.el.select('.modal-header',true).first();
4012 this.titleEl = this.el.select('.modal-title',true).first();
4013 this.footerEl = this.el.select('.modal-footer',true).first();
4015 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4017 //this.el.addClass("x-dlg-modal");
4019 if (this.buttons.length) {
4020 Roo.each(this.buttons, function(bb) {
4021 var b = Roo.apply({}, bb);
4022 b.xns = b.xns || Roo.bootstrap;
4023 b.xtype = b.xtype || 'Button';
4024 if (typeof(b.listeners) == 'undefined') {
4025 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4028 var btn = Roo.factory(b);
4030 btn.render(this.getButtonContainer());
4034 // render the children.
4037 if(typeof(this.items) != 'undefined'){
4038 var items = this.items;
4041 for(var i =0;i < items.length;i++) {
4042 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4046 this.items = nitems;
4048 // where are these used - they used to be body/close/footer
4052 //this.el.addClass([this.fieldClass, this.cls]);
4056 getAutoCreate : function()
4058 // we will default to modal-body-overflow - might need to remove or make optional later.
4060 cls : 'modal-body enable-modal-body-overflow ',
4061 html : this.html || ''
4066 cls : 'modal-title',
4070 if(this.specificTitle){ // WTF is this?
4075 if (this.allow_close && Roo.bootstrap.version == 3) {
4085 if (this.editableTitle) {
4087 cls: 'form-control roo-editable-title d-none',
4093 if (this.allow_close && Roo.bootstrap.version == 4) {
4103 if(this.size.length){
4104 size = 'modal-' + this.size;
4107 var footer = Roo.bootstrap.version == 3 ?
4109 cls : 'modal-footer',
4113 cls: 'btn-' + this.buttonPosition
4118 { // BS4 uses mr-auto on left buttons....
4119 cls : 'modal-footer'
4130 cls: "modal-dialog " + size,
4133 cls : "modal-content",
4136 cls : 'modal-header',
4151 modal.cls += ' fade';
4157 getChildContainer : function() {
4162 getButtonContainer : function() {
4164 return Roo.bootstrap.version == 4 ?
4165 this.el.select('.modal-footer',true).first()
4166 : this.el.select('.modal-footer div',true).first();
4169 initEvents : function()
4171 if (this.allow_close) {
4172 this.closeEl.on('click', this.hide, this);
4174 Roo.EventManager.onWindowResize(this.resize, this, true);
4175 if (this.editableTitle) {
4176 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4177 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4178 this.headerEditEl.on('keyup', function(e) {
4179 if(e.isNavKeyPress()){
4180 this.toggleHeaderInput(false)
4183 this.headerEditEl.on('blur', function(e) {
4184 this.toggleHeaderInput(false)
4193 this.maskEl.setSize(
4194 Roo.lib.Dom.getViewWidth(true),
4195 Roo.lib.Dom.getViewHeight(true)
4198 if (this.fitwindow) {
4202 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4203 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4208 if(this.max_width !== 0) {
4210 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4213 this.setSize(w, this.height);
4217 if(this.max_height) {
4218 this.setSize(w,Math.min(
4220 Roo.lib.Dom.getViewportHeight(true) - 60
4226 if(!this.fit_content) {
4227 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4231 this.setSize(w, Math.min(
4233 this.headerEl.getHeight() +
4234 this.footerEl.getHeight() +
4235 this.getChildHeight(this.bodyEl.dom.childNodes),
4236 Roo.lib.Dom.getViewportHeight(true) - 60)
4242 setSize : function(w,h)
4253 if (!this.rendered) {
4257 //this.el.setStyle('display', 'block');
4258 this.el.removeClass('hideing');
4259 this.el.dom.style.display='block';
4261 Roo.get(document.body).addClass('modal-open');
4263 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4266 this.el.addClass('show');
4267 this.el.addClass('in');
4270 this.el.addClass('show');
4271 this.el.addClass('in');
4274 // not sure how we can show data in here..
4276 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4279 Roo.get(document.body).addClass("x-body-masked");
4281 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4282 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4283 this.maskEl.dom.style.display = 'block';
4284 this.maskEl.addClass('show');
4289 this.fireEvent('show', this);
4291 // set zindex here - otherwise it appears to be ignored...
4292 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4295 this.items.forEach( function(e) {
4296 e.layout ? e.layout() : false;
4304 if(this.fireEvent("beforehide", this) !== false){
4306 this.maskEl.removeClass('show');
4308 this.maskEl.dom.style.display = '';
4309 Roo.get(document.body).removeClass("x-body-masked");
4310 this.el.removeClass('in');
4311 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4313 if(this.animate){ // why
4314 this.el.addClass('hideing');
4315 this.el.removeClass('show');
4317 if (!this.el.hasClass('hideing')) {
4318 return; // it's been shown again...
4321 this.el.dom.style.display='';
4323 Roo.get(document.body).removeClass('modal-open');
4324 this.el.removeClass('hideing');
4328 this.el.removeClass('show');
4329 this.el.dom.style.display='';
4330 Roo.get(document.body).removeClass('modal-open');
4333 this.fireEvent('hide', this);
4336 isVisible : function()
4339 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4343 addButton : function(str, cb)
4347 var b = Roo.apply({}, { html : str } );
4348 b.xns = b.xns || Roo.bootstrap;
4349 b.xtype = b.xtype || 'Button';
4350 if (typeof(b.listeners) == 'undefined') {
4351 b.listeners = { click : cb.createDelegate(this) };
4354 var btn = Roo.factory(b);
4356 btn.render(this.getButtonContainer());
4362 setDefaultButton : function(btn)
4364 //this.el.select('.modal-footer').()
4367 resizeTo: function(w,h)
4369 this.dialogEl.setWidth(w);
4371 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4373 this.bodyEl.setHeight(h - diff);
4375 this.fireEvent('resize', this);
4378 setContentSize : function(w, h)
4382 onButtonClick: function(btn,e)
4385 this.fireEvent('btnclick', btn.name, e);
4388 * Set the title of the Dialog
4389 * @param {String} str new Title
4391 setTitle: function(str) {
4392 this.titleEl.dom.innerHTML = str;
4396 * Set the body of the Dialog
4397 * @param {String} str new Title
4399 setBody: function(str) {
4400 this.bodyEl.dom.innerHTML = str;
4403 * Set the body of the Dialog using the template
4404 * @param {Obj} data - apply this data to the template and replace the body contents.
4406 applyBody: function(obj)
4409 Roo.log("Error - using apply Body without a template");
4412 this.tmpl.overwrite(this.bodyEl, obj);
4415 getChildHeight : function(child_nodes)
4419 child_nodes.length == 0
4424 var child_height = 0;
4426 for(var i = 0; i < child_nodes.length; i++) {
4429 * for modal with tabs...
4430 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4432 var layout_childs = child_nodes[i].childNodes;
4434 for(var j = 0; j < layout_childs.length; j++) {
4436 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4438 var layout_body_childs = layout_childs[j].childNodes;
4440 for(var k = 0; k < layout_body_childs.length; k++) {
4442 if(layout_body_childs[k].classList.contains('navbar')) {
4443 child_height += layout_body_childs[k].offsetHeight;
4447 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4449 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4451 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4453 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4454 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4469 child_height += child_nodes[i].offsetHeight;
4470 // Roo.log(child_nodes[i].offsetHeight);
4473 return child_height;
4475 toggleHeaderInput : function(is_edit)
4478 if (is_edit && this.is_header_editing) {
4479 return; // already editing..
4483 this.headerEditEl.dom.value = this.title;
4484 this.headerEditEl.removeClass('d-none');
4485 this.headerEditEl.dom.focus();
4486 this.titleEl.addClass('d-none');
4488 this.is_header_editing = true;
4491 // flip back to not editing.
4492 this.title = this.headerEditEl.dom.value;
4493 this.headerEditEl.addClass('d-none');
4494 this.titleEl.removeClass('d-none');
4495 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4496 this.is_header_editing = false;
4497 this.fireEvent('titlechanged', this, this.title);
4506 Roo.apply(Roo.bootstrap.Modal, {
4508 * Button config that displays a single OK button
4517 * Button config that displays Yes and No buttons
4533 * Button config that displays OK and Cancel buttons
4548 * Button config that displays Yes, No and Cancel buttons
4573 * messagebox - can be used as a replace
4577 * @class Roo.MessageBox
4578 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4582 Roo.Msg.alert('Status', 'Changes saved successfully.');
4584 // Prompt for user data:
4585 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4587 // process text value...
4591 // Show a dialog using config options:
4593 title:'Save Changes?',
4594 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4595 buttons: Roo.Msg.YESNOCANCEL,
4602 Roo.bootstrap.MessageBox = function(){
4603 var dlg, opt, mask, waitTimer;
4604 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4605 var buttons, activeTextEl, bwidth;
4609 var handleButton = function(button){
4611 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4615 var handleHide = function(){
4617 dlg.el.removeClass(opt.cls);
4620 // Roo.TaskMgr.stop(waitTimer);
4621 // waitTimer = null;
4626 var updateButtons = function(b){
4629 buttons["ok"].hide();
4630 buttons["cancel"].hide();
4631 buttons["yes"].hide();
4632 buttons["no"].hide();
4633 dlg.footerEl.hide();
4637 dlg.footerEl.show();
4638 for(var k in buttons){
4639 if(typeof buttons[k] != "function"){
4642 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4643 width += buttons[k].el.getWidth()+15;
4653 var handleEsc = function(d, k, e){
4654 if(opt && opt.closable !== false){
4664 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4665 * @return {Roo.BasicDialog} The BasicDialog element
4667 getDialog : function(){
4669 dlg = new Roo.bootstrap.Modal( {
4672 //constraintoviewport:false,
4674 //collapsible : false,
4679 //buttonAlign:"center",
4680 closeClick : function(){
4681 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4684 handleButton("cancel");
4689 dlg.on("hide", handleHide);
4691 //dlg.addKeyListener(27, handleEsc);
4693 this.buttons = buttons;
4694 var bt = this.buttonText;
4695 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4696 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4697 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4698 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4700 bodyEl = dlg.bodyEl.createChild({
4702 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4703 '<textarea class="roo-mb-textarea"></textarea>' +
4704 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4706 msgEl = bodyEl.dom.firstChild;
4707 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4708 textboxEl.enableDisplayMode();
4709 textboxEl.addKeyListener([10,13], function(){
4710 if(dlg.isVisible() && opt && opt.buttons){
4713 }else if(opt.buttons.yes){
4714 handleButton("yes");
4718 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4719 textareaEl.enableDisplayMode();
4720 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4721 progressEl.enableDisplayMode();
4723 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4724 var pf = progressEl.dom.firstChild;
4726 pp = Roo.get(pf.firstChild);
4727 pp.setHeight(pf.offsetHeight);
4735 * Updates the message box body text
4736 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4737 * the XHTML-compliant non-breaking space character '&#160;')
4738 * @return {Roo.MessageBox} This message box
4740 updateText : function(text)
4742 if(!dlg.isVisible() && !opt.width){
4743 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4744 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4746 msgEl.innerHTML = text || ' ';
4748 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4749 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4751 Math.min(opt.width || cw , this.maxWidth),
4752 Math.max(opt.minWidth || this.minWidth, bwidth)
4755 activeTextEl.setWidth(w);
4757 if(dlg.isVisible()){
4758 dlg.fixedcenter = false;
4760 // to big, make it scroll. = But as usual stupid IE does not support
4763 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4764 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4765 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4767 bodyEl.dom.style.height = '';
4768 bodyEl.dom.style.overflowY = '';
4771 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4773 bodyEl.dom.style.overflowX = '';
4776 dlg.setContentSize(w, bodyEl.getHeight());
4777 if(dlg.isVisible()){
4778 dlg.fixedcenter = true;
4784 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4785 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4786 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4787 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4788 * @return {Roo.MessageBox} This message box
4790 updateProgress : function(value, text){
4792 this.updateText(text);
4795 if (pp) { // weird bug on my firefox - for some reason this is not defined
4796 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4797 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4803 * Returns true if the message box is currently displayed
4804 * @return {Boolean} True if the message box is visible, else false
4806 isVisible : function(){
4807 return dlg && dlg.isVisible();
4811 * Hides the message box if it is displayed
4814 if(this.isVisible()){
4820 * Displays a new message box, or reinitializes an existing message box, based on the config options
4821 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4822 * The following config object properties are supported:
4824 Property Type Description
4825 ---------- --------------- ------------------------------------------------------------------------------------
4826 animEl String/Element An id or Element from which the message box should animate as it opens and
4827 closes (defaults to undefined)
4828 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4829 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4830 closable Boolean False to hide the top-right close button (defaults to true). Note that
4831 progress and wait dialogs will ignore this property and always hide the
4832 close button as they can only be closed programmatically.
4833 cls String A custom CSS class to apply to the message box element
4834 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4835 displayed (defaults to 75)
4836 fn Function A callback function to execute after closing the dialog. The arguments to the
4837 function will be btn (the name of the button that was clicked, if applicable,
4838 e.g. "ok"), and text (the value of the active text field, if applicable).
4839 Progress and wait dialogs will ignore this option since they do not respond to
4840 user actions and can only be closed programmatically, so any required function
4841 should be called by the same code after it closes the dialog.
4842 icon String A CSS class that provides a background image to be used as an icon for
4843 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4844 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4845 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4846 modal Boolean False to allow user interaction with the page while the message box is
4847 displayed (defaults to true)
4848 msg String A string that will replace the existing message box body text (defaults
4849 to the XHTML-compliant non-breaking space character ' ')
4850 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4851 progress Boolean True to display a progress bar (defaults to false)
4852 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4853 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4854 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4855 title String The title text
4856 value String The string value to set into the active textbox element if displayed
4857 wait Boolean True to display a progress bar (defaults to false)
4858 width Number The width of the dialog in pixels
4865 msg: 'Please enter your address:',
4867 buttons: Roo.MessageBox.OKCANCEL,
4870 animEl: 'addAddressBtn'
4873 * @param {Object} config Configuration options
4874 * @return {Roo.MessageBox} This message box
4876 show : function(options)
4879 // this causes nightmares if you show one dialog after another
4880 // especially on callbacks..
4882 if(this.isVisible()){
4885 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4886 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4887 Roo.log("New Dialog Message:" + options.msg )
4888 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4889 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4892 var d = this.getDialog();
4894 d.setTitle(opt.title || " ");
4895 d.closeEl.setDisplayed(opt.closable !== false);
4896 activeTextEl = textboxEl;
4897 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4902 textareaEl.setHeight(typeof opt.multiline == "number" ?
4903 opt.multiline : this.defaultTextHeight);
4904 activeTextEl = textareaEl;
4913 progressEl.setDisplayed(opt.progress === true);
4915 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4917 this.updateProgress(0);
4918 activeTextEl.dom.value = opt.value || "";
4920 dlg.setDefaultButton(activeTextEl);
4922 var bs = opt.buttons;
4926 }else if(bs && bs.yes){
4927 db = buttons["yes"];
4929 dlg.setDefaultButton(db);
4931 bwidth = updateButtons(opt.buttons);
4932 this.updateText(opt.msg);
4934 d.el.addClass(opt.cls);
4936 d.proxyDrag = opt.proxyDrag === true;
4937 d.modal = opt.modal !== false;
4938 d.mask = opt.modal !== false ? mask : false;
4940 // force it to the end of the z-index stack so it gets a cursor in FF
4941 document.body.appendChild(dlg.el.dom);
4942 d.animateTarget = null;
4943 d.show(options.animEl);
4949 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
4950 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4951 * and closing the message box when the process is complete.
4952 * @param {String} title The title bar text
4953 * @param {String} msg The message box body text
4954 * @return {Roo.MessageBox} This message box
4956 progress : function(title, msg){
4963 minWidth: this.minProgressWidth,
4970 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4971 * If a callback function is passed it will be called after the user clicks the button, and the
4972 * id of the button that was clicked will be passed as the only parameter to the callback
4973 * (could also be the top-right close button).
4974 * @param {String} title The title bar text
4975 * @param {String} msg The message box body text
4976 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4977 * @param {Object} scope (optional) The scope of the callback function
4978 * @return {Roo.MessageBox} This message box
4980 alert : function(title, msg, fn, scope)
4995 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
4996 * interaction while waiting for a long-running process to complete that does not have defined intervals.
4997 * You are responsible for closing the message box when the process is complete.
4998 * @param {String} msg The message box body text
4999 * @param {String} title (optional) The title bar text
5000 * @return {Roo.MessageBox} This message box
5002 wait : function(msg, title){
5013 waitTimer = Roo.TaskMgr.start({
5015 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5023 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5024 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5025 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5026 * @param {String} title The title bar text
5027 * @param {String} msg The message box body text
5028 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5029 * @param {Object} scope (optional) The scope of the callback function
5030 * @return {Roo.MessageBox} This message box
5032 confirm : function(title, msg, fn, scope){
5036 buttons: this.YESNO,
5045 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5046 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5047 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5048 * (could also be the top-right close button) and the text that was entered will be passed as the two
5049 * parameters to the callback.
5050 * @param {String} title The title bar text
5051 * @param {String} msg The message box body text
5052 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5053 * @param {Object} scope (optional) The scope of the callback function
5054 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5055 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5056 * @return {Roo.MessageBox} This message box
5058 prompt : function(title, msg, fn, scope, multiline){
5062 buttons: this.OKCANCEL,
5067 multiline: multiline,
5074 * Button config that displays a single OK button
5079 * Button config that displays Yes and No buttons
5082 YESNO : {yes:true, no:true},
5084 * Button config that displays OK and Cancel buttons
5087 OKCANCEL : {ok:true, cancel:true},
5089 * Button config that displays Yes, No and Cancel buttons
5092 YESNOCANCEL : {yes:true, no:true, cancel:true},
5095 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5098 defaultTextHeight : 75,
5100 * The maximum width in pixels of the message box (defaults to 600)
5105 * The minimum width in pixels of the message box (defaults to 100)
5110 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5111 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5114 minProgressWidth : 250,
5116 * An object containing the default button text strings that can be overriden for localized language support.
5117 * Supported properties are: ok, cancel, yes and no.
5118 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5131 * Shorthand for {@link Roo.MessageBox}
5133 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5134 Roo.Msg = Roo.Msg || Roo.MessageBox;
5143 * @class Roo.bootstrap.Navbar
5144 * @extends Roo.bootstrap.Component
5145 * Bootstrap Navbar class
5148 * Create a new Navbar
5149 * @param {Object} config The config object
5153 Roo.bootstrap.Navbar = function(config){
5154 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5158 * @event beforetoggle
5159 * Fire before toggle the menu
5160 * @param {Roo.EventObject} e
5162 "beforetoggle" : true
5166 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5175 getAutoCreate : function(){
5178 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5182 initEvents :function ()
5184 //Roo.log(this.el.select('.navbar-toggle',true));
5185 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5192 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5194 var size = this.el.getSize();
5195 this.maskEl.setSize(size.width, size.height);
5196 this.maskEl.enableDisplayMode("block");
5205 getChildContainer : function()
5207 if (this.el && this.el.select('.collapse').getCount()) {
5208 return this.el.select('.collapse',true).first();
5223 onToggle : function()
5226 if(this.fireEvent('beforetoggle', this) === false){
5229 var ce = this.el.select('.navbar-collapse',true).first();
5231 if (!ce.hasClass('show')) {
5241 * Expand the navbar pulldown
5243 expand : function ()
5246 var ce = this.el.select('.navbar-collapse',true).first();
5247 if (ce.hasClass('collapsing')) {
5250 ce.dom.style.height = '';
5252 ce.addClass('in'); // old...
5253 ce.removeClass('collapse');
5254 ce.addClass('show');
5255 var h = ce.getHeight();
5257 ce.removeClass('show');
5258 // at this point we should be able to see it..
5259 ce.addClass('collapsing');
5261 ce.setHeight(0); // resize it ...
5262 ce.on('transitionend', function() {
5263 //Roo.log('done transition');
5264 ce.removeClass('collapsing');
5265 ce.addClass('show');
5266 ce.removeClass('collapse');
5268 ce.dom.style.height = '';
5269 }, this, { single: true} );
5271 ce.dom.scrollTop = 0;
5274 * Collapse the navbar pulldown
5276 collapse : function()
5278 var ce = this.el.select('.navbar-collapse',true).first();
5280 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5281 // it's collapsed or collapsing..
5284 ce.removeClass('in'); // old...
5285 ce.setHeight(ce.getHeight());
5286 ce.removeClass('show');
5287 ce.addClass('collapsing');
5289 ce.on('transitionend', function() {
5290 ce.dom.style.height = '';
5291 ce.removeClass('collapsing');
5292 ce.addClass('collapse');
5293 }, this, { single: true} );
5313 * @class Roo.bootstrap.NavSimplebar
5314 * @extends Roo.bootstrap.Navbar
5315 * Bootstrap Sidebar class
5317 * @cfg {Boolean} inverse is inverted color
5319 * @cfg {String} type (nav | pills | tabs)
5320 * @cfg {Boolean} arrangement stacked | justified
5321 * @cfg {String} align (left | right) alignment
5323 * @cfg {Boolean} main (true|false) main nav bar? default false
5324 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5326 * @cfg {String} tag (header|footer|nav|div) default is nav
5328 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5332 * Create a new Sidebar
5333 * @param {Object} config The config object
5337 Roo.bootstrap.NavSimplebar = function(config){
5338 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5341 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5357 getAutoCreate : function(){
5361 tag : this.tag || 'div',
5362 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5364 if (['light','white'].indexOf(this.weight) > -1) {
5365 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5367 cfg.cls += ' bg-' + this.weight;
5370 cfg.cls += ' navbar-inverse';
5374 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5376 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5385 cls: 'nav nav-' + this.xtype,
5391 this.type = this.type || 'nav';
5392 if (['tabs','pills'].indexOf(this.type) != -1) {
5393 cfg.cn[0].cls += ' nav-' + this.type
5397 if (this.type!=='nav') {
5398 Roo.log('nav type must be nav/tabs/pills')
5400 cfg.cn[0].cls += ' navbar-nav'
5406 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5407 cfg.cn[0].cls += ' nav-' + this.arrangement;
5411 if (this.align === 'right') {
5412 cfg.cn[0].cls += ' navbar-right';
5437 * navbar-expand-md fixed-top
5441 * @class Roo.bootstrap.NavHeaderbar
5442 * @extends Roo.bootstrap.NavSimplebar
5443 * Bootstrap Sidebar class
5445 * @cfg {String} brand what is brand
5446 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5447 * @cfg {String} brand_href href of the brand
5448 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5449 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5450 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5451 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5454 * Create a new Sidebar
5455 * @param {Object} config The config object
5459 Roo.bootstrap.NavHeaderbar = function(config){
5460 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5464 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5471 desktopCenter : false,
5474 getAutoCreate : function(){
5477 tag: this.nav || 'nav',
5478 cls: 'navbar navbar-expand-md',
5484 if (this.desktopCenter) {
5485 cn.push({cls : 'container', cn : []});
5493 cls: 'navbar-toggle navbar-toggler',
5494 'data-toggle': 'collapse',
5499 html: 'Toggle navigation'
5503 cls: 'icon-bar navbar-toggler-icon'
5516 cn.push( Roo.bootstrap.version == 4 ? btn : {
5518 cls: 'navbar-header',
5527 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5531 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5533 if (['light','white'].indexOf(this.weight) > -1) {
5534 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5536 cfg.cls += ' bg-' + this.weight;
5539 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5540 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5542 // tag can override this..
5544 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5547 if (this.brand !== '') {
5548 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5549 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5551 href: this.brand_href ? this.brand_href : '#',
5552 cls: 'navbar-brand',
5560 cfg.cls += ' main-nav';
5568 getHeaderChildContainer : function()
5570 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5571 return this.el.select('.navbar-header',true).first();
5574 return this.getChildContainer();
5577 getChildContainer : function()
5580 return this.el.select('.roo-navbar-collapse',true).first();
5585 initEvents : function()
5587 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5589 if (this.autohide) {
5594 Roo.get(document).on('scroll',function(e) {
5595 var ns = Roo.get(document).getScroll().top;
5596 var os = prevScroll;
5600 ft.removeClass('slideDown');
5601 ft.addClass('slideUp');
5604 ft.removeClass('slideUp');
5605 ft.addClass('slideDown');
5626 * @class Roo.bootstrap.NavSidebar
5627 * @extends Roo.bootstrap.Navbar
5628 * Bootstrap Sidebar class
5631 * Create a new Sidebar
5632 * @param {Object} config The config object
5636 Roo.bootstrap.NavSidebar = function(config){
5637 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5640 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5642 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5644 getAutoCreate : function(){
5649 cls: 'sidebar sidebar-nav'
5671 * @class Roo.bootstrap.NavGroup
5672 * @extends Roo.bootstrap.Component
5673 * Bootstrap NavGroup class
5674 * @cfg {String} align (left|right)
5675 * @cfg {Boolean} inverse
5676 * @cfg {String} type (nav|pills|tab) default nav
5677 * @cfg {String} navId - reference Id for navbar.
5681 * Create a new nav group
5682 * @param {Object} config The config object
5685 Roo.bootstrap.NavGroup = function(config){
5686 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5689 Roo.bootstrap.NavGroup.register(this);
5693 * Fires when the active item changes
5694 * @param {Roo.bootstrap.NavGroup} this
5695 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5696 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5703 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5714 getAutoCreate : function()
5716 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5722 if (Roo.bootstrap.version == 4) {
5723 if (['tabs','pills'].indexOf(this.type) != -1) {
5724 cfg.cls += ' nav-' + this.type;
5726 // trying to remove so header bar can right align top?
5727 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5728 // do not use on header bar...
5729 cfg.cls += ' navbar-nav';
5734 if (['tabs','pills'].indexOf(this.type) != -1) {
5735 cfg.cls += ' nav-' + this.type
5737 if (this.type !== 'nav') {
5738 Roo.log('nav type must be nav/tabs/pills')
5740 cfg.cls += ' navbar-nav'
5744 if (this.parent() && this.parent().sidebar) {
5747 cls: 'dashboard-menu sidebar-menu'
5753 if (this.form === true) {
5756 cls: 'navbar-form form-inline'
5758 //nav navbar-right ml-md-auto
5759 if (this.align === 'right') {
5760 cfg.cls += ' navbar-right ml-md-auto';
5762 cfg.cls += ' navbar-left';
5766 if (this.align === 'right') {
5767 cfg.cls += ' navbar-right ml-md-auto';
5769 cfg.cls += ' mr-auto';
5773 cfg.cls += ' navbar-inverse';
5781 * sets the active Navigation item
5782 * @param {Roo.bootstrap.NavItem} the new current navitem
5784 setActiveItem : function(item)
5787 Roo.each(this.navItems, function(v){
5792 v.setActive(false, true);
5799 item.setActive(true, true);
5800 this.fireEvent('changed', this, item, prev);
5805 * gets the active Navigation item
5806 * @return {Roo.bootstrap.NavItem} the current navitem
5808 getActive : function()
5812 Roo.each(this.navItems, function(v){
5823 indexOfNav : function()
5827 Roo.each(this.navItems, function(v,i){
5838 * adds a Navigation item
5839 * @param {Roo.bootstrap.NavItem} the navitem to add
5841 addItem : function(cfg)
5843 if (this.form && Roo.bootstrap.version == 4) {
5846 var cn = new Roo.bootstrap.NavItem(cfg);
5848 cn.parentId = this.id;
5849 cn.onRender(this.el, null);
5853 * register a Navigation item
5854 * @param {Roo.bootstrap.NavItem} the navitem to add
5856 register : function(item)
5858 this.navItems.push( item);
5859 item.navId = this.navId;
5864 * clear all the Navigation item
5867 clearAll : function()
5870 this.el.dom.innerHTML = '';
5873 getNavItem: function(tabId)
5876 Roo.each(this.navItems, function(e) {
5877 if (e.tabId == tabId) {
5887 setActiveNext : function()
5889 var i = this.indexOfNav(this.getActive());
5890 if (i > this.navItems.length) {
5893 this.setActiveItem(this.navItems[i+1]);
5895 setActivePrev : function()
5897 var i = this.indexOfNav(this.getActive());
5901 this.setActiveItem(this.navItems[i-1]);
5903 clearWasActive : function(except) {
5904 Roo.each(this.navItems, function(e) {
5905 if (e.tabId != except.tabId && e.was_active) {
5906 e.was_active = false;
5913 getWasActive : function ()
5916 Roo.each(this.navItems, function(e) {
5931 Roo.apply(Roo.bootstrap.NavGroup, {
5935 * register a Navigation Group
5936 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5938 register : function(navgrp)
5940 this.groups[navgrp.navId] = navgrp;
5944 * fetch a Navigation Group based on the navigation ID
5945 * @param {string} the navgroup to add
5946 * @returns {Roo.bootstrap.NavGroup} the navgroup
5948 get: function(navId) {
5949 if (typeof(this.groups[navId]) == 'undefined') {
5951 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5953 return this.groups[navId] ;
5968 * @class Roo.bootstrap.NavItem
5969 * @extends Roo.bootstrap.Component
5970 * Bootstrap Navbar.NavItem class
5971 * @cfg {String} href link to
5972 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5974 * @cfg {String} html content of button
5975 * @cfg {String} badge text inside badge
5976 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5977 * @cfg {String} glyphicon DEPRICATED - use fa
5978 * @cfg {String} icon DEPRICATED - use fa
5979 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5980 * @cfg {Boolean} active Is item active
5981 * @cfg {Boolean} disabled Is item disabled
5983 * @cfg {Boolean} preventDefault (true | false) default false
5984 * @cfg {String} tabId the tab that this item activates.
5985 * @cfg {String} tagtype (a|span) render as a href or span?
5986 * @cfg {Boolean} animateRef (true|false) link to element default false
5989 * Create a new Navbar Item
5990 * @param {Object} config The config object
5992 Roo.bootstrap.NavItem = function(config){
5993 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5998 * The raw click event for the entire grid.
5999 * @param {Roo.EventObject} e
6004 * Fires when the active item active state changes
6005 * @param {Roo.bootstrap.NavItem} this
6006 * @param {boolean} state the new state
6012 * Fires when scroll to element
6013 * @param {Roo.bootstrap.NavItem} this
6014 * @param {Object} options
6015 * @param {Roo.EventObject} e
6023 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6032 preventDefault : false,
6040 button_outline : false,
6044 getAutoCreate : function(){
6052 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6054 if (this.disabled) {
6055 cfg.cls += ' disabled';
6059 if (this.button_weight.length) {
6060 cfg.tag = this.href ? 'a' : 'button';
6061 cfg.html = this.html || '';
6062 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6064 cfg.href = this.href;
6067 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6070 // menu .. should add dropdown-menu class - so no need for carat..
6072 if (this.badge !== '') {
6074 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6079 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6083 href : this.href || "#",
6084 html: this.html || ''
6087 if (this.tagtype == 'a') {
6088 cfg.cn[0].cls = 'nav-link';
6091 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6094 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6096 if(this.glyphicon) {
6097 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6102 cfg.cn[0].html += " <span class='caret'></span>";
6106 if (this.badge !== '') {
6108 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6116 onRender : function(ct, position)
6118 // Roo.log("Call onRender: " + this.xtype);
6119 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6123 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6124 this.navLink = this.el.select('.nav-link',true).first();
6129 initEvents: function()
6131 if (typeof (this.menu) != 'undefined') {
6132 this.menu.parentType = this.xtype;
6133 this.menu.triggerEl = this.el;
6134 this.menu = this.addxtype(Roo.apply({}, this.menu));
6137 this.el.select('a',true).on('click', this.onClick, this);
6139 if(this.tagtype == 'span'){
6140 this.el.select('span',true).on('click', this.onClick, this);
6143 // at this point parent should be available..
6144 this.parent().register(this);
6147 onClick : function(e)
6149 if (e.getTarget('.dropdown-menu-item')) {
6150 // did you click on a menu itemm.... - then don't trigger onclick..
6155 this.preventDefault ||
6158 Roo.log("NavItem - prevent Default?");
6162 if (this.disabled) {
6166 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6167 if (tg && tg.transition) {
6168 Roo.log("waiting for the transitionend");
6174 //Roo.log("fire event clicked");
6175 if(this.fireEvent('click', this, e) === false){
6179 if(this.tagtype == 'span'){
6183 //Roo.log(this.href);
6184 var ael = this.el.select('a',true).first();
6187 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6188 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6189 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6190 return; // ignore... - it's a 'hash' to another page.
6192 Roo.log("NavItem - prevent Default?");
6194 this.scrollToElement(e);
6198 var p = this.parent();
6200 if (['tabs','pills'].indexOf(p.type)!==-1) {
6201 if (typeof(p.setActiveItem) !== 'undefined') {
6202 p.setActiveItem(this);
6206 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6207 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6208 // remove the collapsed menu expand...
6209 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6213 isActive: function () {
6216 setActive : function(state, fire, is_was_active)
6218 if (this.active && !state && this.navId) {
6219 this.was_active = true;
6220 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6222 nv.clearWasActive(this);
6226 this.active = state;
6229 this.el.removeClass('active');
6230 this.navLink ? this.navLink.removeClass('active') : false;
6231 } else if (!this.el.hasClass('active')) {
6233 this.el.addClass('active');
6234 if (Roo.bootstrap.version == 4 && this.navLink ) {
6235 this.navLink.addClass('active');
6240 this.fireEvent('changed', this, state);
6243 // show a panel if it's registered and related..
6245 if (!this.navId || !this.tabId || !state || is_was_active) {
6249 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6253 var pan = tg.getPanelByName(this.tabId);
6257 // if we can not flip to new panel - go back to old nav highlight..
6258 if (false == tg.showPanel(pan)) {
6259 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6261 var onav = nv.getWasActive();
6263 onav.setActive(true, false, true);
6272 // this should not be here...
6273 setDisabled : function(state)
6275 this.disabled = state;
6277 this.el.removeClass('disabled');
6278 } else if (!this.el.hasClass('disabled')) {
6279 this.el.addClass('disabled');
6285 * Fetch the element to display the tooltip on.
6286 * @return {Roo.Element} defaults to this.el
6288 tooltipEl : function()
6290 return this.el.select('' + this.tagtype + '', true).first();
6293 scrollToElement : function(e)
6295 var c = document.body;
6298 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6300 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6301 c = document.documentElement;
6304 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6310 var o = target.calcOffsetsTo(c);
6317 this.fireEvent('scrollto', this, options, e);
6319 Roo.get(c).scrollTo('top', options.value, true);
6332 * <span> icon </span>
6333 * <span> text </span>
6334 * <span>badge </span>
6338 * @class Roo.bootstrap.NavSidebarItem
6339 * @extends Roo.bootstrap.NavItem
6340 * Bootstrap Navbar.NavSidebarItem class
6341 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6342 * {Boolean} open is the menu open
6343 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6344 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6345 * {String} buttonSize (sm|md|lg)the extra classes for the button
6346 * {Boolean} showArrow show arrow next to the text (default true)
6348 * Create a new Navbar Button
6349 * @param {Object} config The config object
6351 Roo.bootstrap.NavSidebarItem = function(config){
6352 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6357 * The raw click event for the entire grid.
6358 * @param {Roo.EventObject} e
6363 * Fires when the active item active state changes
6364 * @param {Roo.bootstrap.NavSidebarItem} this
6365 * @param {boolean} state the new state
6373 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6375 badgeWeight : 'default',
6381 buttonWeight : 'default',
6387 getAutoCreate : function(){
6392 href : this.href || '#',
6398 if(this.buttonView){
6401 href : this.href || '#',
6402 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6415 cfg.cls += ' active';
6418 if (this.disabled) {
6419 cfg.cls += ' disabled';
6422 cfg.cls += ' open x-open';
6425 if (this.glyphicon || this.icon) {
6426 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6427 a.cn.push({ tag : 'i', cls : c }) ;
6430 if(!this.buttonView){
6433 html : this.html || ''
6440 if (this.badge !== '') {
6441 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6447 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6450 a.cls += ' dropdown-toggle treeview' ;
6456 initEvents : function()
6458 if (typeof (this.menu) != 'undefined') {
6459 this.menu.parentType = this.xtype;
6460 this.menu.triggerEl = this.el;
6461 this.menu = this.addxtype(Roo.apply({}, this.menu));
6464 this.el.on('click', this.onClick, this);
6466 if(this.badge !== ''){
6467 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6472 onClick : function(e)
6479 if(this.preventDefault){
6483 this.fireEvent('click', this, e);
6486 disable : function()
6488 this.setDisabled(true);
6493 this.setDisabled(false);
6496 setDisabled : function(state)
6498 if(this.disabled == state){
6502 this.disabled = state;
6505 this.el.addClass('disabled');
6509 this.el.removeClass('disabled');
6514 setActive : function(state)
6516 if(this.active == state){
6520 this.active = state;
6523 this.el.addClass('active');
6527 this.el.removeClass('active');
6532 isActive: function ()
6537 setBadge : function(str)
6543 this.badgeEl.dom.innerHTML = str;
6560 * @class Roo.bootstrap.Row
6561 * @extends Roo.bootstrap.Component
6562 * Bootstrap Row class (contains columns...)
6566 * @param {Object} config The config object
6569 Roo.bootstrap.Row = function(config){
6570 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6573 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6575 getAutoCreate : function(){
6594 * @class Roo.bootstrap.Pagination
6595 * @extends Roo.bootstrap.Component
6596 * Bootstrap Pagination class
6597 * @cfg {String} size xs | sm | md | lg
6598 * @cfg {Boolean} inverse false | true
6601 * Create a new Pagination
6602 * @param {Object} config The config object
6605 Roo.bootstrap.Pagination = function(config){
6606 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6609 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6615 getAutoCreate : function(){
6621 cfg.cls += ' inverse';
6627 cfg.cls += " " + this.cls;
6645 * @class Roo.bootstrap.PaginationItem
6646 * @extends Roo.bootstrap.Component
6647 * Bootstrap PaginationItem class
6648 * @cfg {String} html text
6649 * @cfg {String} href the link
6650 * @cfg {Boolean} preventDefault (true | false) default true
6651 * @cfg {Boolean} active (true | false) default false
6652 * @cfg {Boolean} disabled default false
6656 * Create a new PaginationItem
6657 * @param {Object} config The config object
6661 Roo.bootstrap.PaginationItem = function(config){
6662 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6667 * The raw click event for the entire grid.
6668 * @param {Roo.EventObject} e
6674 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6678 preventDefault: true,
6683 getAutoCreate : function(){
6689 href : this.href ? this.href : '#',
6690 html : this.html ? this.html : ''
6700 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6704 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6710 initEvents: function() {
6712 this.el.on('click', this.onClick, this);
6715 onClick : function(e)
6717 Roo.log('PaginationItem on click ');
6718 if(this.preventDefault){
6726 this.fireEvent('click', this, e);
6742 * @class Roo.bootstrap.Slider
6743 * @extends Roo.bootstrap.Component
6744 * Bootstrap Slider class
6747 * Create a new Slider
6748 * @param {Object} config The config object
6751 Roo.bootstrap.Slider = function(config){
6752 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6755 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6757 getAutoCreate : function(){
6761 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6765 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6777 * Ext JS Library 1.1.1
6778 * Copyright(c) 2006-2007, Ext JS, LLC.
6780 * Originally Released Under LGPL - original licence link has changed is not relivant.
6783 * <script type="text/javascript">
6788 * @class Roo.grid.ColumnModel
6789 * @extends Roo.util.Observable
6790 * This is the default implementation of a ColumnModel used by the Grid. It defines
6791 * the columns in the grid.
6794 var colModel = new Roo.grid.ColumnModel([
6795 {header: "Ticker", width: 60, sortable: true, locked: true},
6796 {header: "Company Name", width: 150, sortable: true},
6797 {header: "Market Cap.", width: 100, sortable: true},
6798 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6799 {header: "Employees", width: 100, sortable: true, resizable: false}
6804 * The config options listed for this class are options which may appear in each
6805 * individual column definition.
6806 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6808 * @param {Object} config An Array of column config objects. See this class's
6809 * config objects for details.
6811 Roo.grid.ColumnModel = function(config){
6813 * The config passed into the constructor
6815 this.config = config;
6818 // if no id, create one
6819 // if the column does not have a dataIndex mapping,
6820 // map it to the order it is in the config
6821 for(var i = 0, len = config.length; i < len; i++){
6823 if(typeof c.dataIndex == "undefined"){
6826 if(typeof c.renderer == "string"){
6827 c.renderer = Roo.util.Format[c.renderer];
6829 if(typeof c.id == "undefined"){
6832 if(c.editor && c.editor.xtype){
6833 c.editor = Roo.factory(c.editor, Roo.grid);
6835 if(c.editor && c.editor.isFormField){
6836 c.editor = new Roo.grid.GridEditor(c.editor);
6838 this.lookup[c.id] = c;
6842 * The width of columns which have no width specified (defaults to 100)
6845 this.defaultWidth = 100;
6848 * Default sortable of columns which have no sortable specified (defaults to false)
6851 this.defaultSortable = false;
6855 * @event widthchange
6856 * Fires when the width of a column changes.
6857 * @param {ColumnModel} this
6858 * @param {Number} columnIndex The column index
6859 * @param {Number} newWidth The new width
6861 "widthchange": true,
6863 * @event headerchange
6864 * Fires when the text of a header changes.
6865 * @param {ColumnModel} this
6866 * @param {Number} columnIndex The column index
6867 * @param {Number} newText The new header text
6869 "headerchange": true,
6871 * @event hiddenchange
6872 * Fires when a column is hidden or "unhidden".
6873 * @param {ColumnModel} this
6874 * @param {Number} columnIndex The column index
6875 * @param {Boolean} hidden true if hidden, false otherwise
6877 "hiddenchange": true,
6879 * @event columnmoved
6880 * Fires when a column is moved.
6881 * @param {ColumnModel} this
6882 * @param {Number} oldIndex
6883 * @param {Number} newIndex
6885 "columnmoved" : true,
6887 * @event columlockchange
6888 * Fires when a column's locked state is changed
6889 * @param {ColumnModel} this
6890 * @param {Number} colIndex
6891 * @param {Boolean} locked true if locked
6893 "columnlockchange" : true
6895 Roo.grid.ColumnModel.superclass.constructor.call(this);
6897 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6899 * @cfg {String} header The header text to display in the Grid view.
6902 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6903 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6904 * specified, the column's index is used as an index into the Record's data Array.
6907 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6908 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6911 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6912 * Defaults to the value of the {@link #defaultSortable} property.
6913 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6916 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6919 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6922 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6925 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6928 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6929 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6930 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6931 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6934 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6937 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6940 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6943 * @cfg {String} cursor (Optional)
6946 * @cfg {String} tooltip (Optional)
6949 * @cfg {Number} xs (Optional)
6952 * @cfg {Number} sm (Optional)
6955 * @cfg {Number} md (Optional)
6958 * @cfg {Number} lg (Optional)
6961 * Returns the id of the column at the specified index.
6962 * @param {Number} index The column index
6963 * @return {String} the id
6965 getColumnId : function(index){
6966 return this.config[index].id;
6970 * Returns the column for a specified id.
6971 * @param {String} id The column id
6972 * @return {Object} the column
6974 getColumnById : function(id){
6975 return this.lookup[id];
6980 * Returns the column for a specified dataIndex.
6981 * @param {String} dataIndex The column dataIndex
6982 * @return {Object|Boolean} the column or false if not found
6984 getColumnByDataIndex: function(dataIndex){
6985 var index = this.findColumnIndex(dataIndex);
6986 return index > -1 ? this.config[index] : false;
6990 * Returns the index for a specified column id.
6991 * @param {String} id The column id
6992 * @return {Number} the index, or -1 if not found
6994 getIndexById : function(id){
6995 for(var i = 0, len = this.config.length; i < len; i++){
6996 if(this.config[i].id == id){
7004 * Returns the index for a specified column dataIndex.
7005 * @param {String} dataIndex The column dataIndex
7006 * @return {Number} the index, or -1 if not found
7009 findColumnIndex : function(dataIndex){
7010 for(var i = 0, len = this.config.length; i < len; i++){
7011 if(this.config[i].dataIndex == dataIndex){
7019 moveColumn : function(oldIndex, newIndex){
7020 var c = this.config[oldIndex];
7021 this.config.splice(oldIndex, 1);
7022 this.config.splice(newIndex, 0, c);
7023 this.dataMap = null;
7024 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7027 isLocked : function(colIndex){
7028 return this.config[colIndex].locked === true;
7031 setLocked : function(colIndex, value, suppressEvent){
7032 if(this.isLocked(colIndex) == value){
7035 this.config[colIndex].locked = value;
7037 this.fireEvent("columnlockchange", this, colIndex, value);
7041 getTotalLockedWidth : function(){
7043 for(var i = 0; i < this.config.length; i++){
7044 if(this.isLocked(i) && !this.isHidden(i)){
7045 this.totalWidth += this.getColumnWidth(i);
7051 getLockedCount : function(){
7052 for(var i = 0, len = this.config.length; i < len; i++){
7053 if(!this.isLocked(i)){
7058 return this.config.length;
7062 * Returns the number of columns.
7065 getColumnCount : function(visibleOnly){
7066 if(visibleOnly === true){
7068 for(var i = 0, len = this.config.length; i < len; i++){
7069 if(!this.isHidden(i)){
7075 return this.config.length;
7079 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7080 * @param {Function} fn
7081 * @param {Object} scope (optional)
7082 * @return {Array} result
7084 getColumnsBy : function(fn, scope){
7086 for(var i = 0, len = this.config.length; i < len; i++){
7087 var c = this.config[i];
7088 if(fn.call(scope||this, c, i) === true){
7096 * Returns true if the specified column is sortable.
7097 * @param {Number} col The column index
7100 isSortable : function(col){
7101 if(typeof this.config[col].sortable == "undefined"){
7102 return this.defaultSortable;
7104 return this.config[col].sortable;
7108 * Returns the rendering (formatting) function defined for the column.
7109 * @param {Number} col The column index.
7110 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7112 getRenderer : function(col){
7113 if(!this.config[col].renderer){
7114 return Roo.grid.ColumnModel.defaultRenderer;
7116 return this.config[col].renderer;
7120 * Sets the rendering (formatting) function for a column.
7121 * @param {Number} col The column index
7122 * @param {Function} fn The function to use to process the cell's raw data
7123 * to return HTML markup for the grid view. The render function is called with
7124 * the following parameters:<ul>
7125 * <li>Data value.</li>
7126 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7127 * <li>css A CSS style string to apply to the table cell.</li>
7128 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7129 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7130 * <li>Row index</li>
7131 * <li>Column index</li>
7132 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7134 setRenderer : function(col, fn){
7135 this.config[col].renderer = fn;
7139 * Returns the width for the specified column.
7140 * @param {Number} col The column index
7143 getColumnWidth : function(col){
7144 return this.config[col].width * 1 || this.defaultWidth;
7148 * Sets the width for a column.
7149 * @param {Number} col The column index
7150 * @param {Number} width The new width
7152 setColumnWidth : function(col, width, suppressEvent){
7153 this.config[col].width = width;
7154 this.totalWidth = null;
7156 this.fireEvent("widthchange", this, col, width);
7161 * Returns the total width of all columns.
7162 * @param {Boolean} includeHidden True to include hidden column widths
7165 getTotalWidth : function(includeHidden){
7166 if(!this.totalWidth){
7167 this.totalWidth = 0;
7168 for(var i = 0, len = this.config.length; i < len; i++){
7169 if(includeHidden || !this.isHidden(i)){
7170 this.totalWidth += this.getColumnWidth(i);
7174 return this.totalWidth;
7178 * Returns the header for the specified column.
7179 * @param {Number} col The column index
7182 getColumnHeader : function(col){
7183 return this.config[col].header;
7187 * Sets the header for a column.
7188 * @param {Number} col The column index
7189 * @param {String} header The new header
7191 setColumnHeader : function(col, header){
7192 this.config[col].header = header;
7193 this.fireEvent("headerchange", this, col, header);
7197 * Returns the tooltip for the specified column.
7198 * @param {Number} col The column index
7201 getColumnTooltip : function(col){
7202 return this.config[col].tooltip;
7205 * Sets the tooltip for a column.
7206 * @param {Number} col The column index
7207 * @param {String} tooltip The new tooltip
7209 setColumnTooltip : function(col, tooltip){
7210 this.config[col].tooltip = tooltip;
7214 * Returns the dataIndex for the specified column.
7215 * @param {Number} col The column index
7218 getDataIndex : function(col){
7219 return this.config[col].dataIndex;
7223 * Sets the dataIndex for a column.
7224 * @param {Number} col The column index
7225 * @param {Number} dataIndex The new dataIndex
7227 setDataIndex : function(col, dataIndex){
7228 this.config[col].dataIndex = dataIndex;
7234 * Returns true if the cell is editable.
7235 * @param {Number} colIndex The column index
7236 * @param {Number} rowIndex The row index - this is nto actually used..?
7239 isCellEditable : function(colIndex, rowIndex){
7240 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7244 * Returns the editor defined for the cell/column.
7245 * return false or null to disable editing.
7246 * @param {Number} colIndex The column index
7247 * @param {Number} rowIndex The row index
7250 getCellEditor : function(colIndex, rowIndex){
7251 return this.config[colIndex].editor;
7255 * Sets if a column is editable.
7256 * @param {Number} col The column index
7257 * @param {Boolean} editable True if the column is editable
7259 setEditable : function(col, editable){
7260 this.config[col].editable = editable;
7265 * Returns true if the column is hidden.
7266 * @param {Number} colIndex The column index
7269 isHidden : function(colIndex){
7270 return this.config[colIndex].hidden;
7275 * Returns true if the column width cannot be changed
7277 isFixed : function(colIndex){
7278 return this.config[colIndex].fixed;
7282 * Returns true if the column can be resized
7285 isResizable : function(colIndex){
7286 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7289 * Sets if a column is hidden.
7290 * @param {Number} colIndex The column index
7291 * @param {Boolean} hidden True if the column is hidden
7293 setHidden : function(colIndex, hidden){
7294 this.config[colIndex].hidden = hidden;
7295 this.totalWidth = null;
7296 this.fireEvent("hiddenchange", this, colIndex, hidden);
7300 * Sets the editor for a column.
7301 * @param {Number} col The column index
7302 * @param {Object} editor The editor object
7304 setEditor : function(col, editor){
7305 this.config[col].editor = editor;
7309 Roo.grid.ColumnModel.defaultRenderer = function(value)
7311 if(typeof value == "object") {
7314 if(typeof value == "string" && value.length < 1){
7318 return String.format("{0}", value);
7321 // Alias for backwards compatibility
7322 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7325 * Ext JS Library 1.1.1
7326 * Copyright(c) 2006-2007, Ext JS, LLC.
7328 * Originally Released Under LGPL - original licence link has changed is not relivant.
7331 * <script type="text/javascript">
7335 * @class Roo.LoadMask
7336 * A simple utility class for generically masking elements while loading data. If the element being masked has
7337 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7338 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7339 * element's UpdateManager load indicator and will be destroyed after the initial load.
7341 * Create a new LoadMask
7342 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7343 * @param {Object} config The config object
7345 Roo.LoadMask = function(el, config){
7346 this.el = Roo.get(el);
7347 Roo.apply(this, config);
7349 this.store.on('beforeload', this.onBeforeLoad, this);
7350 this.store.on('load', this.onLoad, this);
7351 this.store.on('loadexception', this.onLoadException, this);
7352 this.removeMask = false;
7354 var um = this.el.getUpdateManager();
7355 um.showLoadIndicator = false; // disable the default indicator
7356 um.on('beforeupdate', this.onBeforeLoad, this);
7357 um.on('update', this.onLoad, this);
7358 um.on('failure', this.onLoad, this);
7359 this.removeMask = true;
7363 Roo.LoadMask.prototype = {
7365 * @cfg {Boolean} removeMask
7366 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7367 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7371 * The text to display in a centered loading message box (defaults to 'Loading...')
7375 * @cfg {String} msgCls
7376 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7378 msgCls : 'x-mask-loading',
7381 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7387 * Disables the mask to prevent it from being displayed
7389 disable : function(){
7390 this.disabled = true;
7394 * Enables the mask so that it can be displayed
7396 enable : function(){
7397 this.disabled = false;
7400 onLoadException : function()
7404 if (typeof(arguments[3]) != 'undefined') {
7405 Roo.MessageBox.alert("Error loading",arguments[3]);
7409 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7410 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7417 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7422 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7426 onBeforeLoad : function(){
7428 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7433 destroy : function(){
7435 this.store.un('beforeload', this.onBeforeLoad, this);
7436 this.store.un('load', this.onLoad, this);
7437 this.store.un('loadexception', this.onLoadException, this);
7439 var um = this.el.getUpdateManager();
7440 um.un('beforeupdate', this.onBeforeLoad, this);
7441 um.un('update', this.onLoad, this);
7442 um.un('failure', this.onLoad, this);
7453 * @class Roo.bootstrap.Table
7454 * @extends Roo.bootstrap.Component
7455 * Bootstrap Table class
7456 * @cfg {String} cls table class
7457 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7458 * @cfg {String} bgcolor Specifies the background color for a table
7459 * @cfg {Number} border Specifies whether the table cells should have borders or not
7460 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7461 * @cfg {Number} cellspacing Specifies the space between cells
7462 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7463 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7464 * @cfg {String} sortable Specifies that the table should be sortable
7465 * @cfg {String} summary Specifies a summary of the content of a table
7466 * @cfg {Number} width Specifies the width of a table
7467 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7469 * @cfg {boolean} striped Should the rows be alternative striped
7470 * @cfg {boolean} bordered Add borders to the table
7471 * @cfg {boolean} hover Add hover highlighting
7472 * @cfg {boolean} condensed Format condensed
7473 * @cfg {boolean} responsive Format condensed
7474 * @cfg {Boolean} loadMask (true|false) default false
7475 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7476 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7477 * @cfg {Boolean} rowSelection (true|false) default false
7478 * @cfg {Boolean} cellSelection (true|false) default false
7479 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7480 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7481 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7482 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7486 * Create a new Table
7487 * @param {Object} config The config object
7490 Roo.bootstrap.Table = function(config){
7491 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7496 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7497 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7498 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7499 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7501 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7503 this.sm.grid = this;
7504 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7505 this.sm = this.selModel;
7506 this.sm.xmodule = this.xmodule || false;
7509 if (this.cm && typeof(this.cm.config) == 'undefined') {
7510 this.colModel = new Roo.grid.ColumnModel(this.cm);
7511 this.cm = this.colModel;
7512 this.cm.xmodule = this.xmodule || false;
7515 this.store= Roo.factory(this.store, Roo.data);
7516 this.ds = this.store;
7517 this.ds.xmodule = this.xmodule || false;
7520 if (this.footer && this.store) {
7521 this.footer.dataSource = this.ds;
7522 this.footer = Roo.factory(this.footer);
7529 * Fires when a cell is clicked
7530 * @param {Roo.bootstrap.Table} this
7531 * @param {Roo.Element} el
7532 * @param {Number} rowIndex
7533 * @param {Number} columnIndex
7534 * @param {Roo.EventObject} e
7538 * @event celldblclick
7539 * Fires when a cell is double clicked
7540 * @param {Roo.bootstrap.Table} this
7541 * @param {Roo.Element} el
7542 * @param {Number} rowIndex
7543 * @param {Number} columnIndex
7544 * @param {Roo.EventObject} e
7546 "celldblclick" : true,
7549 * Fires when a row is clicked
7550 * @param {Roo.bootstrap.Table} this
7551 * @param {Roo.Element} el
7552 * @param {Number} rowIndex
7553 * @param {Roo.EventObject} e
7557 * @event rowdblclick
7558 * Fires when a row is double clicked
7559 * @param {Roo.bootstrap.Table} this
7560 * @param {Roo.Element} el
7561 * @param {Number} rowIndex
7562 * @param {Roo.EventObject} e
7564 "rowdblclick" : true,
7567 * Fires when a mouseover occur
7568 * @param {Roo.bootstrap.Table} this
7569 * @param {Roo.Element} el
7570 * @param {Number} rowIndex
7571 * @param {Number} columnIndex
7572 * @param {Roo.EventObject} e
7577 * Fires when a mouseout occur
7578 * @param {Roo.bootstrap.Table} this
7579 * @param {Roo.Element} el
7580 * @param {Number} rowIndex
7581 * @param {Number} columnIndex
7582 * @param {Roo.EventObject} e
7587 * Fires when a row is rendered, so you can change add a style to it.
7588 * @param {Roo.bootstrap.Table} this
7589 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7593 * @event rowsrendered
7594 * Fires when all the rows have been rendered
7595 * @param {Roo.bootstrap.Table} this
7597 'rowsrendered' : true,
7599 * @event contextmenu
7600 * The raw contextmenu event for the entire grid.
7601 * @param {Roo.EventObject} e
7603 "contextmenu" : true,
7605 * @event rowcontextmenu
7606 * Fires when a row is right clicked
7607 * @param {Roo.bootstrap.Table} this
7608 * @param {Number} rowIndex
7609 * @param {Roo.EventObject} e
7611 "rowcontextmenu" : true,
7613 * @event cellcontextmenu
7614 * Fires when a cell is right clicked
7615 * @param {Roo.bootstrap.Table} this
7616 * @param {Number} rowIndex
7617 * @param {Number} cellIndex
7618 * @param {Roo.EventObject} e
7620 "cellcontextmenu" : true,
7622 * @event headercontextmenu
7623 * Fires when a header is right clicked
7624 * @param {Roo.bootstrap.Table} this
7625 * @param {Number} columnIndex
7626 * @param {Roo.EventObject} e
7628 "headercontextmenu" : true
7632 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7658 rowSelection : false,
7659 cellSelection : false,
7662 // Roo.Element - the tbody
7664 // Roo.Element - thead element
7667 container: false, // used by gridpanel...
7673 auto_hide_footer : false,
7675 getAutoCreate : function()
7677 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7684 if (this.scrollBody) {
7685 cfg.cls += ' table-body-fixed';
7688 cfg.cls += ' table-striped';
7692 cfg.cls += ' table-hover';
7694 if (this.bordered) {
7695 cfg.cls += ' table-bordered';
7697 if (this.condensed) {
7698 cfg.cls += ' table-condensed';
7700 if (this.responsive) {
7701 cfg.cls += ' table-responsive';
7705 cfg.cls+= ' ' +this.cls;
7708 // this lot should be simplifed...
7721 ].forEach(function(k) {
7729 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7732 if(this.store || this.cm){
7733 if(this.headerShow){
7734 cfg.cn.push(this.renderHeader());
7737 cfg.cn.push(this.renderBody());
7739 if(this.footerShow){
7740 cfg.cn.push(this.renderFooter());
7742 // where does this come from?
7743 //cfg.cls+= ' TableGrid';
7746 return { cn : [ cfg ] };
7749 initEvents : function()
7751 if(!this.store || !this.cm){
7754 if (this.selModel) {
7755 this.selModel.initEvents();
7759 //Roo.log('initEvents with ds!!!!');
7761 this.mainBody = this.el.select('tbody', true).first();
7762 this.mainHead = this.el.select('thead', true).first();
7763 this.mainFoot = this.el.select('tfoot', true).first();
7769 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7770 e.on('click', _this.sort, _this);
7773 this.mainBody.on("click", this.onClick, this);
7774 this.mainBody.on("dblclick", this.onDblClick, this);
7776 // why is this done????? = it breaks dialogs??
7777 //this.parent().el.setStyle('position', 'relative');
7781 this.footer.parentId = this.id;
7782 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7785 this.el.select('tfoot tr td').first().addClass('hide');
7790 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7793 this.store.on('load', this.onLoad, this);
7794 this.store.on('beforeload', this.onBeforeLoad, this);
7795 this.store.on('update', this.onUpdate, this);
7796 this.store.on('add', this.onAdd, this);
7797 this.store.on("clear", this.clear, this);
7799 this.el.on("contextmenu", this.onContextMenu, this);
7801 this.mainBody.on('scroll', this.onBodyScroll, this);
7803 this.cm.on("headerchange", this.onHeaderChange, this);
7805 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7809 onContextMenu : function(e, t)
7811 this.processEvent("contextmenu", e);
7814 processEvent : function(name, e)
7816 if (name != 'touchstart' ) {
7817 this.fireEvent(name, e);
7820 var t = e.getTarget();
7822 var cell = Roo.get(t);
7828 if(cell.findParent('tfoot', false, true)){
7832 if(cell.findParent('thead', false, true)){
7834 if(e.getTarget().nodeName.toLowerCase() != 'th'){
7835 cell = Roo.get(t).findParent('th', false, true);
7837 Roo.log("failed to find th in thead?");
7838 Roo.log(e.getTarget());
7843 var cellIndex = cell.dom.cellIndex;
7845 var ename = name == 'touchstart' ? 'click' : name;
7846 this.fireEvent("header" + ename, this, cellIndex, e);
7851 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7852 cell = Roo.get(t).findParent('td', false, true);
7854 Roo.log("failed to find th in tbody?");
7855 Roo.log(e.getTarget());
7860 var row = cell.findParent('tr', false, true);
7861 var cellIndex = cell.dom.cellIndex;
7862 var rowIndex = row.dom.rowIndex - 1;
7866 this.fireEvent("row" + name, this, rowIndex, e);
7870 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7876 onMouseover : function(e, el)
7878 var cell = Roo.get(el);
7884 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7885 cell = cell.findParent('td', false, true);
7888 var row = cell.findParent('tr', false, true);
7889 var cellIndex = cell.dom.cellIndex;
7890 var rowIndex = row.dom.rowIndex - 1; // start from 0
7892 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7896 onMouseout : function(e, el)
7898 var cell = Roo.get(el);
7904 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7905 cell = cell.findParent('td', false, true);
7908 var row = cell.findParent('tr', false, true);
7909 var cellIndex = cell.dom.cellIndex;
7910 var rowIndex = row.dom.rowIndex - 1; // start from 0
7912 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7916 onClick : function(e, el)
7918 var cell = Roo.get(el);
7920 if(!cell || (!this.cellSelection && !this.rowSelection)){
7924 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7925 cell = cell.findParent('td', false, true);
7928 if(!cell || typeof(cell) == 'undefined'){
7932 var row = cell.findParent('tr', false, true);
7934 if(!row || typeof(row) == 'undefined'){
7938 var cellIndex = cell.dom.cellIndex;
7939 var rowIndex = this.getRowIndex(row);
7941 // why??? - should these not be based on SelectionModel?
7942 if(this.cellSelection){
7943 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7946 if(this.rowSelection){
7947 this.fireEvent('rowclick', this, row, rowIndex, e);
7953 onDblClick : function(e,el)
7955 var cell = Roo.get(el);
7957 if(!cell || (!this.cellSelection && !this.rowSelection)){
7961 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7962 cell = cell.findParent('td', false, true);
7965 if(!cell || typeof(cell) == 'undefined'){
7969 var row = cell.findParent('tr', false, true);
7971 if(!row || typeof(row) == 'undefined'){
7975 var cellIndex = cell.dom.cellIndex;
7976 var rowIndex = this.getRowIndex(row);
7978 if(this.cellSelection){
7979 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7982 if(this.rowSelection){
7983 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7987 sort : function(e,el)
7989 var col = Roo.get(el);
7991 if(!col.hasClass('sortable')){
7995 var sort = col.attr('sort');
7998 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8002 this.store.sortInfo = {field : sort, direction : dir};
8005 Roo.log("calling footer first");
8006 this.footer.onClick('first');
8009 this.store.load({ params : { start : 0 } });
8013 renderHeader : function()
8021 this.totalWidth = 0;
8023 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8025 var config = cm.config[i];
8029 cls : 'x-hcol-' + i,
8031 html: cm.getColumnHeader(i)
8036 if(typeof(config.sortable) != 'undefined' && config.sortable){
8038 c.html = '<i class="glyphicon"></i>' + c.html;
8041 // could use BS4 hidden-..-down
8043 if(typeof(config.lgHeader) != 'undefined'){
8044 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8047 if(typeof(config.mdHeader) != 'undefined'){
8048 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8051 if(typeof(config.smHeader) != 'undefined'){
8052 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8055 if(typeof(config.xsHeader) != 'undefined'){
8056 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8063 if(typeof(config.tooltip) != 'undefined'){
8064 c.tooltip = config.tooltip;
8067 if(typeof(config.colspan) != 'undefined'){
8068 c.colspan = config.colspan;
8071 if(typeof(config.hidden) != 'undefined' && config.hidden){
8072 c.style += ' display:none;';
8075 if(typeof(config.dataIndex) != 'undefined'){
8076 c.sort = config.dataIndex;
8081 if(typeof(config.align) != 'undefined' && config.align.length){
8082 c.style += ' text-align:' + config.align + ';';
8085 if(typeof(config.width) != 'undefined'){
8086 c.style += ' width:' + config.width + 'px;';
8087 this.totalWidth += config.width;
8089 this.totalWidth += 100; // assume minimum of 100 per column?
8092 if(typeof(config.cls) != 'undefined'){
8093 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8096 ['xs','sm','md','lg'].map(function(size){
8098 if(typeof(config[size]) == 'undefined'){
8102 if (!config[size]) { // 0 = hidden
8103 // BS 4 '0' is treated as hide that column and below.
8104 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8108 c.cls += ' col-' + size + '-' + config[size] + (
8109 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8121 renderBody : function()
8131 colspan : this.cm.getColumnCount()
8141 renderFooter : function()
8151 colspan : this.cm.getColumnCount()
8165 // Roo.log('ds onload');
8170 var ds = this.store;
8172 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8173 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8174 if (_this.store.sortInfo) {
8176 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8177 e.select('i', true).addClass(['glyphicon-arrow-up']);
8180 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8181 e.select('i', true).addClass(['glyphicon-arrow-down']);
8186 var tbody = this.mainBody;
8188 if(ds.getCount() > 0){
8189 ds.data.each(function(d,rowIndex){
8190 var row = this.renderRow(cm, ds, rowIndex);
8192 tbody.createChild(row);
8196 if(row.cellObjects.length){
8197 Roo.each(row.cellObjects, function(r){
8198 _this.renderCellObject(r);
8205 var tfoot = this.el.select('tfoot', true).first();
8207 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8209 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8211 var total = this.ds.getTotalCount();
8213 if(this.footer.pageSize < total){
8214 this.mainFoot.show();
8218 Roo.each(this.el.select('tbody td', true).elements, function(e){
8219 e.on('mouseover', _this.onMouseover, _this);
8222 Roo.each(this.el.select('tbody td', true).elements, function(e){
8223 e.on('mouseout', _this.onMouseout, _this);
8225 this.fireEvent('rowsrendered', this);
8231 onUpdate : function(ds,record)
8233 this.refreshRow(record);
8237 onRemove : function(ds, record, index, isUpdate){
8238 if(isUpdate !== true){
8239 this.fireEvent("beforerowremoved", this, index, record);
8241 var bt = this.mainBody.dom;
8243 var rows = this.el.select('tbody > tr', true).elements;
8245 if(typeof(rows[index]) != 'undefined'){
8246 bt.removeChild(rows[index].dom);
8249 // if(bt.rows[index]){
8250 // bt.removeChild(bt.rows[index]);
8253 if(isUpdate !== true){
8254 //this.stripeRows(index);
8255 //this.syncRowHeights(index, index);
8257 this.fireEvent("rowremoved", this, index, record);
8261 onAdd : function(ds, records, rowIndex)
8263 //Roo.log('on Add called');
8264 // - note this does not handle multiple adding very well..
8265 var bt = this.mainBody.dom;
8266 for (var i =0 ; i < records.length;i++) {
8267 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8268 //Roo.log(records[i]);
8269 //Roo.log(this.store.getAt(rowIndex+i));
8270 this.insertRow(this.store, rowIndex + i, false);
8277 refreshRow : function(record){
8278 var ds = this.store, index;
8279 if(typeof record == 'number'){
8281 record = ds.getAt(index);
8283 index = ds.indexOf(record);
8285 return; // should not happen - but seems to
8288 this.insertRow(ds, index, true);
8290 this.onRemove(ds, record, index+1, true);
8292 //this.syncRowHeights(index, index);
8294 this.fireEvent("rowupdated", this, index, record);
8297 insertRow : function(dm, rowIndex, isUpdate){
8300 this.fireEvent("beforerowsinserted", this, rowIndex);
8302 //var s = this.getScrollState();
8303 var row = this.renderRow(this.cm, this.store, rowIndex);
8304 // insert before rowIndex..
8305 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8309 if(row.cellObjects.length){
8310 Roo.each(row.cellObjects, function(r){
8311 _this.renderCellObject(r);
8316 this.fireEvent("rowsinserted", this, rowIndex);
8317 //this.syncRowHeights(firstRow, lastRow);
8318 //this.stripeRows(firstRow);
8325 getRowDom : function(rowIndex)
8327 var rows = this.el.select('tbody > tr', true).elements;
8329 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8332 // returns the object tree for a tr..
8335 renderRow : function(cm, ds, rowIndex)
8337 var d = ds.getAt(rowIndex);
8341 cls : 'x-row-' + rowIndex,
8345 var cellObjects = [];
8347 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8348 var config = cm.config[i];
8350 var renderer = cm.getRenderer(i);
8354 if(typeof(renderer) !== 'undefined'){
8355 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8357 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8358 // and are rendered into the cells after the row is rendered - using the id for the element.
8360 if(typeof(value) === 'object'){
8370 rowIndex : rowIndex,
8375 this.fireEvent('rowclass', this, rowcfg);
8379 cls : rowcfg.rowClass + ' x-col-' + i,
8381 html: (typeof(value) === 'object') ? '' : value
8388 if(typeof(config.colspan) != 'undefined'){
8389 td.colspan = config.colspan;
8392 if(typeof(config.hidden) != 'undefined' && config.hidden){
8393 td.style += ' display:none;';
8396 if(typeof(config.align) != 'undefined' && config.align.length){
8397 td.style += ' text-align:' + config.align + ';';
8399 if(typeof(config.valign) != 'undefined' && config.valign.length){
8400 td.style += ' vertical-align:' + config.valign + ';';
8403 if(typeof(config.width) != 'undefined'){
8404 td.style += ' width:' + config.width + 'px;';
8407 if(typeof(config.cursor) != 'undefined'){
8408 td.style += ' cursor:' + config.cursor + ';';
8411 if(typeof(config.cls) != 'undefined'){
8412 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8415 ['xs','sm','md','lg'].map(function(size){
8417 if(typeof(config[size]) == 'undefined'){
8423 if (!config[size]) { // 0 = hidden
8424 // BS 4 '0' is treated as hide that column and below.
8425 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8429 td.cls += ' col-' + size + '-' + config[size] + (
8430 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8440 row.cellObjects = cellObjects;
8448 onBeforeLoad : function()
8457 this.el.select('tbody', true).first().dom.innerHTML = '';
8460 * Show or hide a row.
8461 * @param {Number} rowIndex to show or hide
8462 * @param {Boolean} state hide
8464 setRowVisibility : function(rowIndex, state)
8466 var bt = this.mainBody.dom;
8468 var rows = this.el.select('tbody > tr', true).elements;
8470 if(typeof(rows[rowIndex]) == 'undefined'){
8473 rows[rowIndex].dom.style.display = state ? '' : 'none';
8477 getSelectionModel : function(){
8479 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8481 return this.selModel;
8484 * Render the Roo.bootstrap object from renderder
8486 renderCellObject : function(r)
8490 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8492 var t = r.cfg.render(r.container);
8495 Roo.each(r.cfg.cn, function(c){
8497 container: t.getChildContainer(),
8500 _this.renderCellObject(child);
8505 getRowIndex : function(row)
8509 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8520 * Returns the grid's underlying element = used by panel.Grid
8521 * @return {Element} The element
8523 getGridEl : function(){
8527 * Forces a resize - used by panel.Grid
8528 * @return {Element} The element
8530 autoSize : function()
8532 //var ctr = Roo.get(this.container.dom.parentElement);
8533 var ctr = Roo.get(this.el.dom);
8535 var thd = this.getGridEl().select('thead',true).first();
8536 var tbd = this.getGridEl().select('tbody', true).first();
8537 var tfd = this.getGridEl().select('tfoot', true).first();
8539 var cw = ctr.getWidth();
8543 tbd.setWidth(ctr.getWidth());
8544 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8545 // this needs fixing for various usage - currently only hydra job advers I think..
8547 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8549 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8552 cw = Math.max(cw, this.totalWidth);
8553 this.getGridEl().select('tr',true).setWidth(cw);
8554 // resize 'expandable coloumn?
8556 return; // we doe not have a view in this design..
8559 onBodyScroll: function()
8561 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8563 this.mainHead.setStyle({
8564 'position' : 'relative',
8565 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8571 var scrollHeight = this.mainBody.dom.scrollHeight;
8573 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8575 var height = this.mainBody.getHeight();
8577 if(scrollHeight - height == scrollTop) {
8579 var total = this.ds.getTotalCount();
8581 if(this.footer.cursor + this.footer.pageSize < total){
8583 this.footer.ds.load({
8585 start : this.footer.cursor + this.footer.pageSize,
8586 limit : this.footer.pageSize
8596 onHeaderChange : function()
8598 var header = this.renderHeader();
8599 var table = this.el.select('table', true).first();
8601 this.mainHead.remove();
8602 this.mainHead = table.createChild(header, this.mainBody, false);
8605 onHiddenChange : function(colModel, colIndex, hidden)
8607 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8608 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8610 this.CSS.updateRule(thSelector, "display", "");
8611 this.CSS.updateRule(tdSelector, "display", "");
8614 this.CSS.updateRule(thSelector, "display", "none");
8615 this.CSS.updateRule(tdSelector, "display", "none");
8618 this.onHeaderChange();
8622 setColumnWidth: function(col_index, width)
8624 // width = "md-2 xs-2..."
8625 if(!this.colModel.config[col_index]) {
8629 var w = width.split(" ");
8631 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8633 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8636 for(var j = 0; j < w.length; j++) {
8642 var size_cls = w[j].split("-");
8644 if(!Number.isInteger(size_cls[1] * 1)) {
8648 if(!this.colModel.config[col_index][size_cls[0]]) {
8652 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8656 h_row[0].classList.replace(
8657 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8658 "col-"+size_cls[0]+"-"+size_cls[1]
8661 for(var i = 0; i < rows.length; i++) {
8663 var size_cls = w[j].split("-");
8665 if(!Number.isInteger(size_cls[1] * 1)) {
8669 if(!this.colModel.config[col_index][size_cls[0]]) {
8673 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8677 rows[i].classList.replace(
8678 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8679 "col-"+size_cls[0]+"-"+size_cls[1]
8683 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8698 * @class Roo.bootstrap.TableCell
8699 * @extends Roo.bootstrap.Component
8700 * Bootstrap TableCell class
8701 * @cfg {String} html cell contain text
8702 * @cfg {String} cls cell class
8703 * @cfg {String} tag cell tag (td|th) default td
8704 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8705 * @cfg {String} align Aligns the content in a cell
8706 * @cfg {String} axis Categorizes cells
8707 * @cfg {String} bgcolor Specifies the background color of a cell
8708 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8709 * @cfg {Number} colspan Specifies the number of columns a cell should span
8710 * @cfg {String} headers Specifies one or more header cells a cell is related to
8711 * @cfg {Number} height Sets the height of a cell
8712 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8713 * @cfg {Number} rowspan Sets the number of rows a cell should span
8714 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8715 * @cfg {String} valign Vertical aligns the content in a cell
8716 * @cfg {Number} width Specifies the width of a cell
8719 * Create a new TableCell
8720 * @param {Object} config The config object
8723 Roo.bootstrap.TableCell = function(config){
8724 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8727 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8747 getAutoCreate : function(){
8748 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8768 cfg.align=this.align
8774 cfg.bgcolor=this.bgcolor
8777 cfg.charoff=this.charoff
8780 cfg.colspan=this.colspan
8783 cfg.headers=this.headers
8786 cfg.height=this.height
8789 cfg.nowrap=this.nowrap
8792 cfg.rowspan=this.rowspan
8795 cfg.scope=this.scope
8798 cfg.valign=this.valign
8801 cfg.width=this.width
8820 * @class Roo.bootstrap.TableRow
8821 * @extends Roo.bootstrap.Component
8822 * Bootstrap TableRow class
8823 * @cfg {String} cls row class
8824 * @cfg {String} align Aligns the content in a table row
8825 * @cfg {String} bgcolor Specifies a background color for a table row
8826 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8827 * @cfg {String} valign Vertical aligns the content in a table row
8830 * Create a new TableRow
8831 * @param {Object} config The config object
8834 Roo.bootstrap.TableRow = function(config){
8835 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8838 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
8846 getAutoCreate : function(){
8847 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8857 cfg.align = this.align;
8860 cfg.bgcolor = this.bgcolor;
8863 cfg.charoff = this.charoff;
8866 cfg.valign = this.valign;
8884 * @class Roo.bootstrap.TableBody
8885 * @extends Roo.bootstrap.Component
8886 * Bootstrap TableBody class
8887 * @cfg {String} cls element class
8888 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8889 * @cfg {String} align Aligns the content inside the element
8890 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8891 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8894 * Create a new TableBody
8895 * @param {Object} config The config object
8898 Roo.bootstrap.TableBody = function(config){
8899 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8902 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8910 getAutoCreate : function(){
8911 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8925 cfg.align = this.align;
8928 cfg.charoff = this.charoff;
8931 cfg.valign = this.valign;
8938 // initEvents : function()
8945 // this.store = Roo.factory(this.store, Roo.data);
8946 // this.store.on('load', this.onLoad, this);
8948 // this.store.load();
8952 // onLoad: function ()
8954 // this.fireEvent('load', this);
8964 * Ext JS Library 1.1.1
8965 * Copyright(c) 2006-2007, Ext JS, LLC.
8967 * Originally Released Under LGPL - original licence link has changed is not relivant.
8970 * <script type="text/javascript">
8973 // as we use this in bootstrap.
8974 Roo.namespace('Roo.form');
8976 * @class Roo.form.Action
8977 * Internal Class used to handle form actions
8979 * @param {Roo.form.BasicForm} el The form element or its id
8980 * @param {Object} config Configuration options
8985 // define the action interface
8986 Roo.form.Action = function(form, options){
8988 this.options = options || {};
8991 * Client Validation Failed
8994 Roo.form.Action.CLIENT_INVALID = 'client';
8996 * Server Validation Failed
8999 Roo.form.Action.SERVER_INVALID = 'server';
9001 * Connect to Server Failed
9004 Roo.form.Action.CONNECT_FAILURE = 'connect';
9006 * Reading Data from Server Failed
9009 Roo.form.Action.LOAD_FAILURE = 'load';
9011 Roo.form.Action.prototype = {
9013 failureType : undefined,
9014 response : undefined,
9018 run : function(options){
9023 success : function(response){
9028 handleResponse : function(response){
9032 // default connection failure
9033 failure : function(response){
9035 this.response = response;
9036 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9037 this.form.afterAction(this, false);
9040 processResponse : function(response){
9041 this.response = response;
9042 if(!response.responseText){
9045 this.result = this.handleResponse(response);
9049 // utility functions used internally
9050 getUrl : function(appendParams){
9051 var url = this.options.url || this.form.url || this.form.el.dom.action;
9053 var p = this.getParams();
9055 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9061 getMethod : function(){
9062 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9065 getParams : function(){
9066 var bp = this.form.baseParams;
9067 var p = this.options.params;
9069 if(typeof p == "object"){
9070 p = Roo.urlEncode(Roo.applyIf(p, bp));
9071 }else if(typeof p == 'string' && bp){
9072 p += '&' + Roo.urlEncode(bp);
9075 p = Roo.urlEncode(bp);
9080 createCallback : function(){
9082 success: this.success,
9083 failure: this.failure,
9085 timeout: (this.form.timeout*1000),
9086 upload: this.form.fileUpload ? this.success : undefined
9091 Roo.form.Action.Submit = function(form, options){
9092 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9095 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9098 haveProgress : false,
9099 uploadComplete : false,
9101 // uploadProgress indicator.
9102 uploadProgress : function()
9104 if (!this.form.progressUrl) {
9108 if (!this.haveProgress) {
9109 Roo.MessageBox.progress("Uploading", "Uploading");
9111 if (this.uploadComplete) {
9112 Roo.MessageBox.hide();
9116 this.haveProgress = true;
9118 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9120 var c = new Roo.data.Connection();
9122 url : this.form.progressUrl,
9127 success : function(req){
9128 //console.log(data);
9132 rdata = Roo.decode(req.responseText)
9134 Roo.log("Invalid data from server..");
9138 if (!rdata || !rdata.success) {
9140 Roo.MessageBox.alert(Roo.encode(rdata));
9143 var data = rdata.data;
9145 if (this.uploadComplete) {
9146 Roo.MessageBox.hide();
9151 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9152 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9155 this.uploadProgress.defer(2000,this);
9158 failure: function(data) {
9159 Roo.log('progress url failed ');
9170 // run get Values on the form, so it syncs any secondary forms.
9171 this.form.getValues();
9173 var o = this.options;
9174 var method = this.getMethod();
9175 var isPost = method == 'POST';
9176 if(o.clientValidation === false || this.form.isValid()){
9178 if (this.form.progressUrl) {
9179 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9180 (new Date() * 1) + '' + Math.random());
9185 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9186 form:this.form.el.dom,
9187 url:this.getUrl(!isPost),
9189 params:isPost ? this.getParams() : null,
9190 isUpload: this.form.fileUpload,
9191 formData : this.form.formData
9194 this.uploadProgress();
9196 }else if (o.clientValidation !== false){ // client validation failed
9197 this.failureType = Roo.form.Action.CLIENT_INVALID;
9198 this.form.afterAction(this, false);
9202 success : function(response)
9204 this.uploadComplete= true;
9205 if (this.haveProgress) {
9206 Roo.MessageBox.hide();
9210 var result = this.processResponse(response);
9211 if(result === true || result.success){
9212 this.form.afterAction(this, true);
9216 this.form.markInvalid(result.errors);
9217 this.failureType = Roo.form.Action.SERVER_INVALID;
9219 this.form.afterAction(this, false);
9221 failure : function(response)
9223 this.uploadComplete= true;
9224 if (this.haveProgress) {
9225 Roo.MessageBox.hide();
9228 this.response = response;
9229 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9230 this.form.afterAction(this, false);
9233 handleResponse : function(response){
9234 if(this.form.errorReader){
9235 var rs = this.form.errorReader.read(response);
9238 for(var i = 0, len = rs.records.length; i < len; i++) {
9239 var r = rs.records[i];
9243 if(errors.length < 1){
9247 success : rs.success,
9253 ret = Roo.decode(response.responseText);
9257 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9267 Roo.form.Action.Load = function(form, options){
9268 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9269 this.reader = this.form.reader;
9272 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9277 Roo.Ajax.request(Roo.apply(
9278 this.createCallback(), {
9279 method:this.getMethod(),
9280 url:this.getUrl(false),
9281 params:this.getParams()
9285 success : function(response){
9287 var result = this.processResponse(response);
9288 if(result === true || !result.success || !result.data){
9289 this.failureType = Roo.form.Action.LOAD_FAILURE;
9290 this.form.afterAction(this, false);
9293 this.form.clearInvalid();
9294 this.form.setValues(result.data);
9295 this.form.afterAction(this, true);
9298 handleResponse : function(response){
9299 if(this.form.reader){
9300 var rs = this.form.reader.read(response);
9301 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9303 success : rs.success,
9307 return Roo.decode(response.responseText);
9311 Roo.form.Action.ACTION_TYPES = {
9312 'load' : Roo.form.Action.Load,
9313 'submit' : Roo.form.Action.Submit
9322 * @class Roo.bootstrap.Form
9323 * @extends Roo.bootstrap.Component
9324 * Bootstrap Form class
9325 * @cfg {String} method GET | POST (default POST)
9326 * @cfg {String} labelAlign top | left (default top)
9327 * @cfg {String} align left | right - for navbars
9328 * @cfg {Boolean} loadMask load mask when submit (default true)
9333 * @param {Object} config The config object
9337 Roo.bootstrap.Form = function(config){
9339 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9341 Roo.bootstrap.Form.popover.apply();
9345 * @event clientvalidation
9346 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9347 * @param {Form} this
9348 * @param {Boolean} valid true if the form has passed client-side validation
9350 clientvalidation: true,
9352 * @event beforeaction
9353 * Fires before any action is performed. Return false to cancel the action.
9354 * @param {Form} this
9355 * @param {Action} action The action to be performed
9359 * @event actionfailed
9360 * Fires when an action fails.
9361 * @param {Form} this
9362 * @param {Action} action The action that failed
9364 actionfailed : true,
9366 * @event actioncomplete
9367 * Fires when an action is completed.
9368 * @param {Form} this
9369 * @param {Action} action The action that completed
9371 actioncomplete : true
9375 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9378 * @cfg {String} method
9379 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9384 * The URL to use for form actions if one isn't supplied in the action options.
9387 * @cfg {Boolean} fileUpload
9388 * Set to true if this form is a file upload.
9392 * @cfg {Object} baseParams
9393 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9397 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9401 * @cfg {Sting} align (left|right) for navbar forms
9406 activeAction : null,
9409 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9410 * element by passing it or its id or mask the form itself by passing in true.
9413 waitMsgTarget : false,
9418 * @cfg {Boolean} errorMask (true|false) default false
9423 * @cfg {Number} maskOffset Default 100
9428 * @cfg {Boolean} maskBody
9432 getAutoCreate : function(){
9436 method : this.method || 'POST',
9437 id : this.id || Roo.id(),
9440 if (this.parent().xtype.match(/^Nav/)) {
9441 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9445 if (this.labelAlign == 'left' ) {
9446 cfg.cls += ' form-horizontal';
9452 initEvents : function()
9454 this.el.on('submit', this.onSubmit, this);
9455 // this was added as random key presses on the form where triggering form submit.
9456 this.el.on('keypress', function(e) {
9457 if (e.getCharCode() != 13) {
9460 // we might need to allow it for textareas.. and some other items.
9461 // check e.getTarget().
9463 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9467 Roo.log("keypress blocked");
9475 onSubmit : function(e){
9480 * Returns true if client-side validation on the form is successful.
9483 isValid : function(){
9484 var items = this.getItems();
9488 items.each(function(f){
9494 Roo.log('invalid field: ' + f.name);
9498 if(!target && f.el.isVisible(true)){
9504 if(this.errorMask && !valid){
9505 Roo.bootstrap.Form.popover.mask(this, target);
9512 * Returns true if any fields in this form have changed since their original load.
9515 isDirty : function(){
9517 var items = this.getItems();
9518 items.each(function(f){
9528 * Performs a predefined action (submit or load) or custom actions you define on this form.
9529 * @param {String} actionName The name of the action type
9530 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9531 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9532 * accept other config options):
9534 Property Type Description
9535 ---------------- --------------- ----------------------------------------------------------------------------------
9536 url String The url for the action (defaults to the form's url)
9537 method String The form method to use (defaults to the form's method, or POST if not defined)
9538 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9539 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9540 validate the form on the client (defaults to false)
9542 * @return {BasicForm} this
9544 doAction : function(action, options){
9545 if(typeof action == 'string'){
9546 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9548 if(this.fireEvent('beforeaction', this, action) !== false){
9549 this.beforeAction(action);
9550 action.run.defer(100, action);
9556 beforeAction : function(action){
9557 var o = action.options;
9562 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9564 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9567 // not really supported yet.. ??
9569 //if(this.waitMsgTarget === true){
9570 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9571 //}else if(this.waitMsgTarget){
9572 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9573 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9575 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9581 afterAction : function(action, success){
9582 this.activeAction = null;
9583 var o = action.options;
9588 Roo.get(document.body).unmask();
9594 //if(this.waitMsgTarget === true){
9595 // this.el.unmask();
9596 //}else if(this.waitMsgTarget){
9597 // this.waitMsgTarget.unmask();
9599 // Roo.MessageBox.updateProgress(1);
9600 // Roo.MessageBox.hide();
9607 Roo.callback(o.success, o.scope, [this, action]);
9608 this.fireEvent('actioncomplete', this, action);
9612 // failure condition..
9613 // we have a scenario where updates need confirming.
9614 // eg. if a locking scenario exists..
9615 // we look for { errors : { needs_confirm : true }} in the response.
9617 (typeof(action.result) != 'undefined') &&
9618 (typeof(action.result.errors) != 'undefined') &&
9619 (typeof(action.result.errors.needs_confirm) != 'undefined')
9622 Roo.log("not supported yet");
9625 Roo.MessageBox.confirm(
9626 "Change requires confirmation",
9627 action.result.errorMsg,
9632 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9642 Roo.callback(o.failure, o.scope, [this, action]);
9643 // show an error message if no failed handler is set..
9644 if (!this.hasListener('actionfailed')) {
9645 Roo.log("need to add dialog support");
9647 Roo.MessageBox.alert("Error",
9648 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9649 action.result.errorMsg :
9650 "Saving Failed, please check your entries or try again"
9655 this.fireEvent('actionfailed', this, action);
9660 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9661 * @param {String} id The value to search for
9664 findField : function(id){
9665 var items = this.getItems();
9666 var field = items.get(id);
9668 items.each(function(f){
9669 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9676 return field || null;
9679 * Mark fields in this form invalid in bulk.
9680 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9681 * @return {BasicForm} this
9683 markInvalid : function(errors){
9684 if(errors instanceof Array){
9685 for(var i = 0, len = errors.length; i < len; i++){
9686 var fieldError = errors[i];
9687 var f = this.findField(fieldError.id);
9689 f.markInvalid(fieldError.msg);
9695 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9696 field.markInvalid(errors[id]);
9700 //Roo.each(this.childForms || [], function (f) {
9701 // f.markInvalid(errors);
9708 * Set values for fields in this form in bulk.
9709 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9710 * @return {BasicForm} this
9712 setValues : function(values){
9713 if(values instanceof Array){ // array of objects
9714 for(var i = 0, len = values.length; i < len; i++){
9716 var f = this.findField(v.id);
9718 f.setValue(v.value);
9719 if(this.trackResetOnLoad){
9720 f.originalValue = f.getValue();
9724 }else{ // object hash
9727 if(typeof values[id] != 'function' && (field = this.findField(id))){
9729 if (field.setFromData &&
9731 field.displayField &&
9732 // combos' with local stores can
9733 // be queried via setValue()
9734 // to set their value..
9735 (field.store && !field.store.isLocal)
9739 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9740 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9741 field.setFromData(sd);
9743 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9745 field.setFromData(values);
9748 field.setValue(values[id]);
9752 if(this.trackResetOnLoad){
9753 field.originalValue = field.getValue();
9759 //Roo.each(this.childForms || [], function (f) {
9760 // f.setValues(values);
9767 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9768 * they are returned as an array.
9769 * @param {Boolean} asString
9772 getValues : function(asString){
9773 //if (this.childForms) {
9774 // copy values from the child forms
9775 // Roo.each(this.childForms, function (f) {
9776 // this.setValues(f.getValues());
9782 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9783 if(asString === true){
9786 return Roo.urlDecode(fs);
9790 * Returns the fields in this form as an object with key/value pairs.
9791 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9794 getFieldValues : function(with_hidden)
9796 var items = this.getItems();
9798 items.each(function(f){
9804 var v = f.getValue();
9806 if (f.inputType =='radio') {
9807 if (typeof(ret[f.getName()]) == 'undefined') {
9808 ret[f.getName()] = ''; // empty..
9811 if (!f.el.dom.checked) {
9819 if(f.xtype == 'MoneyField'){
9820 ret[f.currencyName] = f.getCurrency();
9823 // not sure if this supported any more..
9824 if ((typeof(v) == 'object') && f.getRawValue) {
9825 v = f.getRawValue() ; // dates..
9827 // combo boxes where name != hiddenName...
9828 if (f.name !== false && f.name != '' && f.name != f.getName()) {
9829 ret[f.name] = f.getRawValue();
9831 ret[f.getName()] = v;
9838 * Clears all invalid messages in this form.
9839 * @return {BasicForm} this
9841 clearInvalid : function(){
9842 var items = this.getItems();
9844 items.each(function(f){
9853 * @return {BasicForm} this
9856 var items = this.getItems();
9857 items.each(function(f){
9861 Roo.each(this.childForms || [], function (f) {
9869 getItems : function()
9871 var r=new Roo.util.MixedCollection(false, function(o){
9872 return o.id || (o.id = Roo.id());
9874 var iter = function(el) {
9881 Roo.each(el.items,function(e) {
9890 hideFields : function(items)
9892 Roo.each(items, function(i){
9894 var f = this.findField(i);
9905 showFields : function(items)
9907 Roo.each(items, function(i){
9909 var f = this.findField(i);
9922 Roo.apply(Roo.bootstrap.Form, {
9949 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9950 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9951 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9952 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9955 this.maskEl.top.enableDisplayMode("block");
9956 this.maskEl.left.enableDisplayMode("block");
9957 this.maskEl.bottom.enableDisplayMode("block");
9958 this.maskEl.right.enableDisplayMode("block");
9960 this.toolTip = new Roo.bootstrap.Tooltip({
9961 cls : 'roo-form-error-popover',
9963 'left' : ['r-l', [-2,0], 'right'],
9964 'right' : ['l-r', [2,0], 'left'],
9965 'bottom' : ['tl-bl', [0,2], 'top'],
9966 'top' : [ 'bl-tl', [0,-2], 'bottom']
9970 this.toolTip.render(Roo.get(document.body));
9972 this.toolTip.el.enableDisplayMode("block");
9974 Roo.get(document.body).on('click', function(){
9978 Roo.get(document.body).on('touchstart', function(){
9982 this.isApplied = true
9985 mask : function(form, target)
9989 this.target = target;
9991 if(!this.form.errorMask || !target.el){
9995 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9997 Roo.log(scrollable);
9999 var ot = this.target.el.calcOffsetsTo(scrollable);
10001 var scrollTo = ot[1] - this.form.maskOffset;
10003 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10005 scrollable.scrollTo('top', scrollTo);
10007 var box = this.target.el.getBox();
10009 var zIndex = Roo.bootstrap.Modal.zIndex++;
10012 this.maskEl.top.setStyle('position', 'absolute');
10013 this.maskEl.top.setStyle('z-index', zIndex);
10014 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10015 this.maskEl.top.setLeft(0);
10016 this.maskEl.top.setTop(0);
10017 this.maskEl.top.show();
10019 this.maskEl.left.setStyle('position', 'absolute');
10020 this.maskEl.left.setStyle('z-index', zIndex);
10021 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10022 this.maskEl.left.setLeft(0);
10023 this.maskEl.left.setTop(box.y - this.padding);
10024 this.maskEl.left.show();
10026 this.maskEl.bottom.setStyle('position', 'absolute');
10027 this.maskEl.bottom.setStyle('z-index', zIndex);
10028 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10029 this.maskEl.bottom.setLeft(0);
10030 this.maskEl.bottom.setTop(box.bottom + this.padding);
10031 this.maskEl.bottom.show();
10033 this.maskEl.right.setStyle('position', 'absolute');
10034 this.maskEl.right.setStyle('z-index', zIndex);
10035 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10036 this.maskEl.right.setLeft(box.right + this.padding);
10037 this.maskEl.right.setTop(box.y - this.padding);
10038 this.maskEl.right.show();
10040 this.toolTip.bindEl = this.target.el;
10042 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10044 var tip = this.target.blankText;
10046 if(this.target.getValue() !== '' ) {
10048 if (this.target.invalidText.length) {
10049 tip = this.target.invalidText;
10050 } else if (this.target.regexText.length){
10051 tip = this.target.regexText;
10055 this.toolTip.show(tip);
10057 this.intervalID = window.setInterval(function() {
10058 Roo.bootstrap.Form.popover.unmask();
10061 window.onwheel = function(){ return false;};
10063 (function(){ this.isMasked = true; }).defer(500, this);
10067 unmask : function()
10069 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10073 this.maskEl.top.setStyle('position', 'absolute');
10074 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10075 this.maskEl.top.hide();
10077 this.maskEl.left.setStyle('position', 'absolute');
10078 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10079 this.maskEl.left.hide();
10081 this.maskEl.bottom.setStyle('position', 'absolute');
10082 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10083 this.maskEl.bottom.hide();
10085 this.maskEl.right.setStyle('position', 'absolute');
10086 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10087 this.maskEl.right.hide();
10089 this.toolTip.hide();
10091 this.toolTip.el.hide();
10093 window.onwheel = function(){ return true;};
10095 if(this.intervalID){
10096 window.clearInterval(this.intervalID);
10097 this.intervalID = false;
10100 this.isMasked = false;
10110 * Ext JS Library 1.1.1
10111 * Copyright(c) 2006-2007, Ext JS, LLC.
10113 * Originally Released Under LGPL - original licence link has changed is not relivant.
10116 * <script type="text/javascript">
10119 * @class Roo.form.VTypes
10120 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10123 Roo.form.VTypes = function(){
10124 // closure these in so they are only created once.
10125 var alpha = /^[a-zA-Z_]+$/;
10126 var alphanum = /^[a-zA-Z0-9_]+$/;
10127 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10128 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10130 // All these messages and functions are configurable
10133 * The function used to validate email addresses
10134 * @param {String} value The email address
10136 'email' : function(v){
10137 return email.test(v);
10140 * The error text to display when the email validation function returns false
10143 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10145 * The keystroke filter mask to be applied on email input
10148 'emailMask' : /[a-z0-9_\.\-@]/i,
10151 * The function used to validate URLs
10152 * @param {String} value The URL
10154 'url' : function(v){
10155 return url.test(v);
10158 * The error text to display when the url validation function returns false
10161 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10164 * The function used to validate alpha values
10165 * @param {String} value The value
10167 'alpha' : function(v){
10168 return alpha.test(v);
10171 * The error text to display when the alpha validation function returns false
10174 'alphaText' : 'This field should only contain letters and _',
10176 * The keystroke filter mask to be applied on alpha input
10179 'alphaMask' : /[a-z_]/i,
10182 * The function used to validate alphanumeric values
10183 * @param {String} value The value
10185 'alphanum' : function(v){
10186 return alphanum.test(v);
10189 * The error text to display when the alphanumeric validation function returns false
10192 'alphanumText' : 'This field should only contain letters, numbers and _',
10194 * The keystroke filter mask to be applied on alphanumeric input
10197 'alphanumMask' : /[a-z0-9_]/i
10207 * @class Roo.bootstrap.Input
10208 * @extends Roo.bootstrap.Component
10209 * Bootstrap Input class
10210 * @cfg {Boolean} disabled is it disabled
10211 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10212 * @cfg {String} name name of the input
10213 * @cfg {string} fieldLabel - the label associated
10214 * @cfg {string} placeholder - placeholder to put in text.
10215 * @cfg {string} before - input group add on before
10216 * @cfg {string} after - input group add on after
10217 * @cfg {string} size - (lg|sm) or leave empty..
10218 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10219 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10220 * @cfg {Number} md colspan out of 12 for computer-sized screens
10221 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10222 * @cfg {string} value default value of the input
10223 * @cfg {Number} labelWidth set the width of label
10224 * @cfg {Number} labellg set the width of label (1-12)
10225 * @cfg {Number} labelmd set the width of label (1-12)
10226 * @cfg {Number} labelsm set the width of label (1-12)
10227 * @cfg {Number} labelxs set the width of label (1-12)
10228 * @cfg {String} labelAlign (top|left)
10229 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10230 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10231 * @cfg {String} indicatorpos (left|right) default left
10232 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10233 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10234 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10236 * @cfg {String} align (left|center|right) Default left
10237 * @cfg {Boolean} forceFeedback (true|false) Default false
10240 * Create a new Input
10241 * @param {Object} config The config object
10244 Roo.bootstrap.Input = function(config){
10246 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10251 * Fires when this field receives input focus.
10252 * @param {Roo.form.Field} this
10257 * Fires when this field loses input focus.
10258 * @param {Roo.form.Field} this
10262 * @event specialkey
10263 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10264 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10265 * @param {Roo.form.Field} this
10266 * @param {Roo.EventObject} e The event object
10271 * Fires just before the field blurs if the field value has changed.
10272 * @param {Roo.form.Field} this
10273 * @param {Mixed} newValue The new value
10274 * @param {Mixed} oldValue The original value
10279 * Fires after the field has been marked as invalid.
10280 * @param {Roo.form.Field} this
10281 * @param {String} msg The validation message
10286 * Fires after the field has been validated with no errors.
10287 * @param {Roo.form.Field} this
10292 * Fires after the key up
10293 * @param {Roo.form.Field} this
10294 * @param {Roo.EventObject} e The event Object
10300 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10302 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10303 automatic validation (defaults to "keyup").
10305 validationEvent : "keyup",
10307 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10309 validateOnBlur : true,
10311 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10313 validationDelay : 250,
10315 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10317 focusClass : "x-form-focus", // not needed???
10321 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10323 invalidClass : "has-warning",
10326 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10328 validClass : "has-success",
10331 * @cfg {Boolean} hasFeedback (true|false) default true
10333 hasFeedback : true,
10336 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10338 invalidFeedbackClass : "glyphicon-warning-sign",
10341 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10343 validFeedbackClass : "glyphicon-ok",
10346 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10348 selectOnFocus : false,
10351 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10355 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10360 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10362 disableKeyFilter : false,
10365 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10369 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10373 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10375 blankText : "Please complete this mandatory field",
10378 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10382 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10384 maxLength : Number.MAX_VALUE,
10386 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10388 minLengthText : "The minimum length for this field is {0}",
10390 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10392 maxLengthText : "The maximum length for this field is {0}",
10396 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10397 * If available, this function will be called only after the basic validators all return true, and will be passed the
10398 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10402 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10403 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10404 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10408 * @cfg {String} regexText -- Depricated - use Invalid Text
10413 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10419 autocomplete: false,
10423 inputType : 'text',
10426 placeholder: false,
10431 preventMark: false,
10432 isFormField : true,
10435 labelAlign : false,
10438 formatedValue : false,
10439 forceFeedback : false,
10441 indicatorpos : 'left',
10451 parentLabelAlign : function()
10454 while (parent.parent()) {
10455 parent = parent.parent();
10456 if (typeof(parent.labelAlign) !='undefined') {
10457 return parent.labelAlign;
10464 getAutoCreate : function()
10466 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10472 if(this.inputType != 'hidden'){
10473 cfg.cls = 'form-group' //input-group
10479 type : this.inputType,
10480 value : this.value,
10481 cls : 'form-control',
10482 placeholder : this.placeholder || '',
10483 autocomplete : this.autocomplete || 'new-password'
10485 if (this.inputType == 'file') {
10486 input.style = 'overflow:hidden'; // why not in CSS?
10489 if(this.capture.length){
10490 input.capture = this.capture;
10493 if(this.accept.length){
10494 input.accept = this.accept + "/*";
10498 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10501 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10502 input.maxLength = this.maxLength;
10505 if (this.disabled) {
10506 input.disabled=true;
10509 if (this.readOnly) {
10510 input.readonly=true;
10514 input.name = this.name;
10518 input.cls += ' input-' + this.size;
10522 ['xs','sm','md','lg'].map(function(size){
10523 if (settings[size]) {
10524 cfg.cls += ' col-' + size + '-' + settings[size];
10528 var inputblock = input;
10532 cls: 'glyphicon form-control-feedback'
10535 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10538 cls : 'has-feedback',
10546 if (this.before || this.after) {
10549 cls : 'input-group',
10553 if (this.before && typeof(this.before) == 'string') {
10555 inputblock.cn.push({
10557 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10561 if (this.before && typeof(this.before) == 'object') {
10562 this.before = Roo.factory(this.before);
10564 inputblock.cn.push({
10566 cls : 'roo-input-before input-group-prepend input-group-' +
10567 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10571 inputblock.cn.push(input);
10573 if (this.after && typeof(this.after) == 'string') {
10574 inputblock.cn.push({
10576 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10580 if (this.after && typeof(this.after) == 'object') {
10581 this.after = Roo.factory(this.after);
10583 inputblock.cn.push({
10585 cls : 'roo-input-after input-group-append input-group-' +
10586 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10590 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10591 inputblock.cls += ' has-feedback';
10592 inputblock.cn.push(feedback);
10597 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10598 tooltip : 'This field is required'
10600 if (this.allowBlank ) {
10601 indicator.style = this.allowBlank ? ' display:none' : '';
10603 if (align ==='left' && this.fieldLabel.length) {
10605 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10612 cls : 'control-label col-form-label',
10613 html : this.fieldLabel
10624 var labelCfg = cfg.cn[1];
10625 var contentCfg = cfg.cn[2];
10627 if(this.indicatorpos == 'right'){
10632 cls : 'control-label col-form-label',
10636 html : this.fieldLabel
10650 labelCfg = cfg.cn[0];
10651 contentCfg = cfg.cn[1];
10655 if(this.labelWidth > 12){
10656 labelCfg.style = "width: " + this.labelWidth + 'px';
10659 if(this.labelWidth < 13 && this.labelmd == 0){
10660 this.labelmd = this.labelWidth;
10663 if(this.labellg > 0){
10664 labelCfg.cls += ' col-lg-' + this.labellg;
10665 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10668 if(this.labelmd > 0){
10669 labelCfg.cls += ' col-md-' + this.labelmd;
10670 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10673 if(this.labelsm > 0){
10674 labelCfg.cls += ' col-sm-' + this.labelsm;
10675 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10678 if(this.labelxs > 0){
10679 labelCfg.cls += ' col-xs-' + this.labelxs;
10680 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10684 } else if ( this.fieldLabel.length) {
10691 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10692 tooltip : 'This field is required',
10693 style : this.allowBlank ? ' display:none' : ''
10697 //cls : 'input-group-addon',
10698 html : this.fieldLabel
10706 if(this.indicatorpos == 'right'){
10711 //cls : 'input-group-addon',
10712 html : this.fieldLabel
10717 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10718 tooltip : 'This field is required',
10719 style : this.allowBlank ? ' display:none' : ''
10739 if (this.parentType === 'Navbar' && this.parent().bar) {
10740 cfg.cls += ' navbar-form';
10743 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10744 // on BS4 we do this only if not form
10745 cfg.cls += ' navbar-form';
10753 * return the real input element.
10755 inputEl: function ()
10757 return this.el.select('input.form-control',true).first();
10760 tooltipEl : function()
10762 return this.inputEl();
10765 indicatorEl : function()
10767 if (Roo.bootstrap.version == 4) {
10768 return false; // not enabled in v4 yet.
10771 var indicator = this.el.select('i.roo-required-indicator',true).first();
10781 setDisabled : function(v)
10783 var i = this.inputEl().dom;
10785 i.removeAttribute('disabled');
10789 i.setAttribute('disabled','true');
10791 initEvents : function()
10794 this.inputEl().on("keydown" , this.fireKey, this);
10795 this.inputEl().on("focus", this.onFocus, this);
10796 this.inputEl().on("blur", this.onBlur, this);
10798 this.inputEl().relayEvent('keyup', this);
10800 this.indicator = this.indicatorEl();
10802 if(this.indicator){
10803 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10806 // reference to original value for reset
10807 this.originalValue = this.getValue();
10808 //Roo.form.TextField.superclass.initEvents.call(this);
10809 if(this.validationEvent == 'keyup'){
10810 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10811 this.inputEl().on('keyup', this.filterValidation, this);
10813 else if(this.validationEvent !== false){
10814 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10817 if(this.selectOnFocus){
10818 this.on("focus", this.preFocus, this);
10821 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10822 this.inputEl().on("keypress", this.filterKeys, this);
10824 this.inputEl().relayEvent('keypress', this);
10827 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
10828 this.el.on("click", this.autoSize, this);
10831 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10832 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10835 if (typeof(this.before) == 'object') {
10836 this.before.render(this.el.select('.roo-input-before',true).first());
10838 if (typeof(this.after) == 'object') {
10839 this.after.render(this.el.select('.roo-input-after',true).first());
10842 this.inputEl().on('change', this.onChange, this);
10845 filterValidation : function(e){
10846 if(!e.isNavKeyPress()){
10847 this.validationTask.delay(this.validationDelay);
10851 * Validates the field value
10852 * @return {Boolean} True if the value is valid, else false
10854 validate : function(){
10855 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10856 if(this.disabled || this.validateValue(this.getRawValue())){
10861 this.markInvalid();
10867 * Validates a value according to the field's validation rules and marks the field as invalid
10868 * if the validation fails
10869 * @param {Mixed} value The value to validate
10870 * @return {Boolean} True if the value is valid, else false
10872 validateValue : function(value)
10874 if(this.getVisibilityEl().hasClass('hidden')){
10878 if(value.length < 1) { // if it's blank
10879 if(this.allowBlank){
10885 if(value.length < this.minLength){
10888 if(value.length > this.maxLength){
10892 var vt = Roo.form.VTypes;
10893 if(!vt[this.vtype](value, this)){
10897 if(typeof this.validator == "function"){
10898 var msg = this.validator(value);
10902 if (typeof(msg) == 'string') {
10903 this.invalidText = msg;
10907 if(this.regex && !this.regex.test(value)){
10915 fireKey : function(e){
10916 //Roo.log('field ' + e.getKey());
10917 if(e.isNavKeyPress()){
10918 this.fireEvent("specialkey", this, e);
10921 focus : function (selectText){
10923 this.inputEl().focus();
10924 if(selectText === true){
10925 this.inputEl().dom.select();
10931 onFocus : function(){
10932 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10933 // this.el.addClass(this.focusClass);
10935 if(!this.hasFocus){
10936 this.hasFocus = true;
10937 this.startValue = this.getValue();
10938 this.fireEvent("focus", this);
10942 beforeBlur : Roo.emptyFn,
10946 onBlur : function(){
10948 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10949 //this.el.removeClass(this.focusClass);
10951 this.hasFocus = false;
10952 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10955 var v = this.getValue();
10956 if(String(v) !== String(this.startValue)){
10957 this.fireEvent('change', this, v, this.startValue);
10959 this.fireEvent("blur", this);
10962 onChange : function(e)
10964 var v = this.getValue();
10965 if(String(v) !== String(this.startValue)){
10966 this.fireEvent('change', this, v, this.startValue);
10972 * Resets the current field value to the originally loaded value and clears any validation messages
10974 reset : function(){
10975 this.setValue(this.originalValue);
10979 * Returns the name of the field
10980 * @return {Mixed} name The name field
10982 getName: function(){
10986 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10987 * @return {Mixed} value The field value
10989 getValue : function(){
10991 var v = this.inputEl().getValue();
10996 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10997 * @return {Mixed} value The field value
10999 getRawValue : function(){
11000 var v = this.inputEl().getValue();
11006 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11007 * @param {Mixed} value The value to set
11009 setRawValue : function(v){
11010 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11013 selectText : function(start, end){
11014 var v = this.getRawValue();
11016 start = start === undefined ? 0 : start;
11017 end = end === undefined ? v.length : end;
11018 var d = this.inputEl().dom;
11019 if(d.setSelectionRange){
11020 d.setSelectionRange(start, end);
11021 }else if(d.createTextRange){
11022 var range = d.createTextRange();
11023 range.moveStart("character", start);
11024 range.moveEnd("character", v.length-end);
11031 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11032 * @param {Mixed} value The value to set
11034 setValue : function(v){
11037 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11043 processValue : function(value){
11044 if(this.stripCharsRe){
11045 var newValue = value.replace(this.stripCharsRe, '');
11046 if(newValue !== value){
11047 this.setRawValue(newValue);
11054 preFocus : function(){
11056 if(this.selectOnFocus){
11057 this.inputEl().dom.select();
11060 filterKeys : function(e){
11061 var k = e.getKey();
11062 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11065 var c = e.getCharCode(), cc = String.fromCharCode(c);
11066 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11069 if(!this.maskRe.test(cc)){
11074 * Clear any invalid styles/messages for this field
11076 clearInvalid : function(){
11078 if(!this.el || this.preventMark){ // not rendered
11083 this.el.removeClass([this.invalidClass, 'is-invalid']);
11085 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11087 var feedback = this.el.select('.form-control-feedback', true).first();
11090 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11095 if(this.indicator){
11096 this.indicator.removeClass('visible');
11097 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11100 this.fireEvent('valid', this);
11104 * Mark this field as valid
11106 markValid : function()
11108 if(!this.el || this.preventMark){ // not rendered...
11112 this.el.removeClass([this.invalidClass, this.validClass]);
11113 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11115 var feedback = this.el.select('.form-control-feedback', true).first();
11118 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11121 if(this.indicator){
11122 this.indicator.removeClass('visible');
11123 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11131 if(this.allowBlank && !this.getRawValue().length){
11134 if (Roo.bootstrap.version == 3) {
11135 this.el.addClass(this.validClass);
11137 this.inputEl().addClass('is-valid');
11140 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11142 var feedback = this.el.select('.form-control-feedback', true).first();
11145 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11146 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11151 this.fireEvent('valid', this);
11155 * Mark this field as invalid
11156 * @param {String} msg The validation message
11158 markInvalid : function(msg)
11160 if(!this.el || this.preventMark){ // not rendered
11164 this.el.removeClass([this.invalidClass, this.validClass]);
11165 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11167 var feedback = this.el.select('.form-control-feedback', true).first();
11170 this.el.select('.form-control-feedback', true).first().removeClass(
11171 [this.invalidFeedbackClass, this.validFeedbackClass]);
11178 if(this.allowBlank && !this.getRawValue().length){
11182 if(this.indicator){
11183 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11184 this.indicator.addClass('visible');
11186 if (Roo.bootstrap.version == 3) {
11187 this.el.addClass(this.invalidClass);
11189 this.inputEl().addClass('is-invalid');
11194 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11196 var feedback = this.el.select('.form-control-feedback', true).first();
11199 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11201 if(this.getValue().length || this.forceFeedback){
11202 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11209 this.fireEvent('invalid', this, msg);
11212 SafariOnKeyDown : function(event)
11214 // this is a workaround for a password hang bug on chrome/ webkit.
11215 if (this.inputEl().dom.type != 'password') {
11219 var isSelectAll = false;
11221 if(this.inputEl().dom.selectionEnd > 0){
11222 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11224 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11225 event.preventDefault();
11230 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11232 event.preventDefault();
11233 // this is very hacky as keydown always get's upper case.
11235 var cc = String.fromCharCode(event.getCharCode());
11236 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11240 adjustWidth : function(tag, w){
11241 tag = tag.toLowerCase();
11242 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11243 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11244 if(tag == 'input'){
11247 if(tag == 'textarea'){
11250 }else if(Roo.isOpera){
11251 if(tag == 'input'){
11254 if(tag == 'textarea'){
11262 setFieldLabel : function(v)
11264 if(!this.rendered){
11268 if(this.indicatorEl()){
11269 var ar = this.el.select('label > span',true);
11271 if (ar.elements.length) {
11272 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11273 this.fieldLabel = v;
11277 var br = this.el.select('label',true);
11279 if(br.elements.length) {
11280 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11281 this.fieldLabel = v;
11285 Roo.log('Cannot Found any of label > span || label in input');
11289 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11290 this.fieldLabel = v;
11305 * @class Roo.bootstrap.TextArea
11306 * @extends Roo.bootstrap.Input
11307 * Bootstrap TextArea class
11308 * @cfg {Number} cols Specifies the visible width of a text area
11309 * @cfg {Number} rows Specifies the visible number of lines in a text area
11310 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11311 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11312 * @cfg {string} html text
11315 * Create a new TextArea
11316 * @param {Object} config The config object
11319 Roo.bootstrap.TextArea = function(config){
11320 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11324 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11334 getAutoCreate : function(){
11336 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11342 if(this.inputType != 'hidden'){
11343 cfg.cls = 'form-group' //input-group
11351 value : this.value || '',
11352 html: this.html || '',
11353 cls : 'form-control',
11354 placeholder : this.placeholder || ''
11358 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11359 input.maxLength = this.maxLength;
11363 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11367 input.cols = this.cols;
11370 if (this.readOnly) {
11371 input.readonly = true;
11375 input.name = this.name;
11379 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11383 ['xs','sm','md','lg'].map(function(size){
11384 if (settings[size]) {
11385 cfg.cls += ' col-' + size + '-' + settings[size];
11389 var inputblock = input;
11391 if(this.hasFeedback && !this.allowBlank){
11395 cls: 'glyphicon form-control-feedback'
11399 cls : 'has-feedback',
11408 if (this.before || this.after) {
11411 cls : 'input-group',
11415 inputblock.cn.push({
11417 cls : 'input-group-addon',
11422 inputblock.cn.push(input);
11424 if(this.hasFeedback && !this.allowBlank){
11425 inputblock.cls += ' has-feedback';
11426 inputblock.cn.push(feedback);
11430 inputblock.cn.push({
11432 cls : 'input-group-addon',
11439 if (align ==='left' && this.fieldLabel.length) {
11444 cls : 'control-label',
11445 html : this.fieldLabel
11456 if(this.labelWidth > 12){
11457 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11460 if(this.labelWidth < 13 && this.labelmd == 0){
11461 this.labelmd = this.labelWidth;
11464 if(this.labellg > 0){
11465 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11466 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11469 if(this.labelmd > 0){
11470 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11471 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11474 if(this.labelsm > 0){
11475 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11476 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11479 if(this.labelxs > 0){
11480 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11481 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11484 } else if ( this.fieldLabel.length) {
11489 //cls : 'input-group-addon',
11490 html : this.fieldLabel
11508 if (this.disabled) {
11509 input.disabled=true;
11516 * return the real textarea element.
11518 inputEl: function ()
11520 return this.el.select('textarea.form-control',true).first();
11524 * Clear any invalid styles/messages for this field
11526 clearInvalid : function()
11529 if(!this.el || this.preventMark){ // not rendered
11533 var label = this.el.select('label', true).first();
11534 var icon = this.el.select('i.fa-star', true).first();
11539 this.el.removeClass( this.validClass);
11540 this.inputEl().removeClass('is-invalid');
11542 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11544 var feedback = this.el.select('.form-control-feedback', true).first();
11547 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11552 this.fireEvent('valid', this);
11556 * Mark this field as valid
11558 markValid : function()
11560 if(!this.el || this.preventMark){ // not rendered
11564 this.el.removeClass([this.invalidClass, this.validClass]);
11565 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11567 var feedback = this.el.select('.form-control-feedback', true).first();
11570 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11573 if(this.disabled || this.allowBlank){
11577 var label = this.el.select('label', true).first();
11578 var icon = this.el.select('i.fa-star', true).first();
11583 if (Roo.bootstrap.version == 3) {
11584 this.el.addClass(this.validClass);
11586 this.inputEl().addClass('is-valid');
11590 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11592 var feedback = this.el.select('.form-control-feedback', true).first();
11595 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11596 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11601 this.fireEvent('valid', this);
11605 * Mark this field as invalid
11606 * @param {String} msg The validation message
11608 markInvalid : function(msg)
11610 if(!this.el || this.preventMark){ // not rendered
11614 this.el.removeClass([this.invalidClass, this.validClass]);
11615 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11617 var feedback = this.el.select('.form-control-feedback', true).first();
11620 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11623 if(this.disabled || this.allowBlank){
11627 var label = this.el.select('label', true).first();
11628 var icon = this.el.select('i.fa-star', true).first();
11630 if(!this.getValue().length && label && !icon){
11631 this.el.createChild({
11633 cls : 'text-danger fa fa-lg fa-star',
11634 tooltip : 'This field is required',
11635 style : 'margin-right:5px;'
11639 if (Roo.bootstrap.version == 3) {
11640 this.el.addClass(this.invalidClass);
11642 this.inputEl().addClass('is-invalid');
11645 // fixme ... this may be depricated need to test..
11646 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11648 var feedback = this.el.select('.form-control-feedback', true).first();
11651 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11653 if(this.getValue().length || this.forceFeedback){
11654 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11661 this.fireEvent('invalid', this, msg);
11669 * trigger field - base class for combo..
11674 * @class Roo.bootstrap.TriggerField
11675 * @extends Roo.bootstrap.Input
11676 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11677 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11678 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11679 * for which you can provide a custom implementation. For example:
11681 var trigger = new Roo.bootstrap.TriggerField();
11682 trigger.onTriggerClick = myTriggerFn;
11683 trigger.applyTo('my-field');
11686 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11687 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11688 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11689 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11690 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11693 * Create a new TriggerField.
11694 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11695 * to the base TextField)
11697 Roo.bootstrap.TriggerField = function(config){
11698 this.mimicing = false;
11699 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11702 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11704 * @cfg {String} triggerClass A CSS class to apply to the trigger
11707 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11712 * @cfg {Boolean} removable (true|false) special filter default false
11716 /** @cfg {Boolean} grow @hide */
11717 /** @cfg {Number} growMin @hide */
11718 /** @cfg {Number} growMax @hide */
11724 autoSize: Roo.emptyFn,
11728 deferHeight : true,
11731 actionMode : 'wrap',
11736 getAutoCreate : function(){
11738 var align = this.labelAlign || this.parentLabelAlign();
11743 cls: 'form-group' //input-group
11750 type : this.inputType,
11751 cls : 'form-control',
11752 autocomplete: 'new-password',
11753 placeholder : this.placeholder || ''
11757 input.name = this.name;
11760 input.cls += ' input-' + this.size;
11763 if (this.disabled) {
11764 input.disabled=true;
11767 var inputblock = input;
11769 if(this.hasFeedback && !this.allowBlank){
11773 cls: 'glyphicon form-control-feedback'
11776 if(this.removable && !this.editable ){
11778 cls : 'has-feedback',
11784 cls : 'roo-combo-removable-btn close'
11791 cls : 'has-feedback',
11800 if(this.removable && !this.editable ){
11802 cls : 'roo-removable',
11808 cls : 'roo-combo-removable-btn close'
11815 if (this.before || this.after) {
11818 cls : 'input-group',
11822 inputblock.cn.push({
11824 cls : 'input-group-addon input-group-prepend input-group-text',
11829 inputblock.cn.push(input);
11831 if(this.hasFeedback && !this.allowBlank){
11832 inputblock.cls += ' has-feedback';
11833 inputblock.cn.push(feedback);
11837 inputblock.cn.push({
11839 cls : 'input-group-addon input-group-append input-group-text',
11848 var ibwrap = inputblock;
11853 cls: 'roo-select2-choices',
11857 cls: 'roo-select2-search-field',
11869 cls: 'roo-select2-container input-group',
11874 cls: 'form-hidden-field'
11880 if(!this.multiple && this.showToggleBtn){
11886 if (this.caret != false) {
11889 cls: 'fa fa-' + this.caret
11896 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11898 Roo.bootstrap.version == 3 ? caret : '',
11901 cls: 'combobox-clear',
11915 combobox.cls += ' roo-select2-container-multi';
11919 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11920 tooltip : 'This field is required'
11922 if (Roo.bootstrap.version == 4) {
11925 style : 'display:none'
11930 if (align ==='left' && this.fieldLabel.length) {
11932 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11939 cls : 'control-label',
11940 html : this.fieldLabel
11952 var labelCfg = cfg.cn[1];
11953 var contentCfg = cfg.cn[2];
11955 if(this.indicatorpos == 'right'){
11960 cls : 'control-label',
11964 html : this.fieldLabel
11978 labelCfg = cfg.cn[0];
11979 contentCfg = cfg.cn[1];
11982 if(this.labelWidth > 12){
11983 labelCfg.style = "width: " + this.labelWidth + 'px';
11986 if(this.labelWidth < 13 && this.labelmd == 0){
11987 this.labelmd = this.labelWidth;
11990 if(this.labellg > 0){
11991 labelCfg.cls += ' col-lg-' + this.labellg;
11992 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11995 if(this.labelmd > 0){
11996 labelCfg.cls += ' col-md-' + this.labelmd;
11997 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12000 if(this.labelsm > 0){
12001 labelCfg.cls += ' col-sm-' + this.labelsm;
12002 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12005 if(this.labelxs > 0){
12006 labelCfg.cls += ' col-xs-' + this.labelxs;
12007 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12010 } else if ( this.fieldLabel.length) {
12011 // Roo.log(" label");
12016 //cls : 'input-group-addon',
12017 html : this.fieldLabel
12025 if(this.indicatorpos == 'right'){
12033 html : this.fieldLabel
12047 // Roo.log(" no label && no align");
12054 ['xs','sm','md','lg'].map(function(size){
12055 if (settings[size]) {
12056 cfg.cls += ' col-' + size + '-' + settings[size];
12067 onResize : function(w, h){
12068 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12069 // if(typeof w == 'number'){
12070 // var x = w - this.trigger.getWidth();
12071 // this.inputEl().setWidth(this.adjustWidth('input', x));
12072 // this.trigger.setStyle('left', x+'px');
12077 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12080 getResizeEl : function(){
12081 return this.inputEl();
12085 getPositionEl : function(){
12086 return this.inputEl();
12090 alignErrorIcon : function(){
12091 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12095 initEvents : function(){
12099 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12100 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12101 if(!this.multiple && this.showToggleBtn){
12102 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12103 if(this.hideTrigger){
12104 this.trigger.setDisplayed(false);
12106 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12110 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12113 if(this.removable && !this.editable && !this.tickable){
12114 var close = this.closeTriggerEl();
12117 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12118 close.on('click', this.removeBtnClick, this, close);
12122 //this.trigger.addClassOnOver('x-form-trigger-over');
12123 //this.trigger.addClassOnClick('x-form-trigger-click');
12126 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12130 closeTriggerEl : function()
12132 var close = this.el.select('.roo-combo-removable-btn', true).first();
12133 return close ? close : false;
12136 removeBtnClick : function(e, h, el)
12138 e.preventDefault();
12140 if(this.fireEvent("remove", this) !== false){
12142 this.fireEvent("afterremove", this)
12146 createList : function()
12148 this.list = Roo.get(document.body).createChild({
12149 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12150 cls: 'typeahead typeahead-long dropdown-menu',
12151 style: 'display:none'
12154 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12159 initTrigger : function(){
12164 onDestroy : function(){
12166 this.trigger.removeAllListeners();
12167 // this.trigger.remove();
12170 // this.wrap.remove();
12172 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12176 onFocus : function(){
12177 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12179 if(!this.mimicing){
12180 this.wrap.addClass('x-trigger-wrap-focus');
12181 this.mimicing = true;
12182 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12183 if(this.monitorTab){
12184 this.el.on("keydown", this.checkTab, this);
12191 checkTab : function(e){
12192 if(e.getKey() == e.TAB){
12193 this.triggerBlur();
12198 onBlur : function(){
12203 mimicBlur : function(e, t){
12205 if(!this.wrap.contains(t) && this.validateBlur()){
12206 this.triggerBlur();
12212 triggerBlur : function(){
12213 this.mimicing = false;
12214 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12215 if(this.monitorTab){
12216 this.el.un("keydown", this.checkTab, this);
12218 //this.wrap.removeClass('x-trigger-wrap-focus');
12219 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12223 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12224 validateBlur : function(e, t){
12229 onDisable : function(){
12230 this.inputEl().dom.disabled = true;
12231 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12233 // this.wrap.addClass('x-item-disabled');
12238 onEnable : function(){
12239 this.inputEl().dom.disabled = false;
12240 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12242 // this.el.removeClass('x-item-disabled');
12247 onShow : function(){
12248 var ae = this.getActionEl();
12251 ae.dom.style.display = '';
12252 ae.dom.style.visibility = 'visible';
12258 onHide : function(){
12259 var ae = this.getActionEl();
12260 ae.dom.style.display = 'none';
12264 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12265 * by an implementing function.
12267 * @param {EventObject} e
12269 onTriggerClick : Roo.emptyFn
12277 * @class Roo.bootstrap.CardUploader
12278 * @extends Roo.bootstrap.Button
12279 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12280 * @cfg {Number} errorTimeout default 3000
12281 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12282 * @cfg {Array} html The button text.
12286 * Create a new CardUploader
12287 * @param {Object} config The config object
12290 Roo.bootstrap.CardUploader = function(config){
12294 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12297 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12304 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12307 errorTimeout : 3000,
12311 fileCollection : false,
12314 getAutoCreate : function()
12318 cls :'form-group' ,
12323 //cls : 'input-group-addon',
12324 html : this.fieldLabel
12331 value : this.value,
12332 cls : 'd-none form-control'
12337 multiple : 'multiple',
12339 cls : 'd-none roo-card-upload-selector'
12343 cls : 'roo-card-uploader-button-container w-100 mb-2'
12346 cls : 'card-columns roo-card-uploader-container'
12356 getChildContainer : function() /// what children are added to.
12358 return this.containerEl;
12361 getButtonContainer : function() /// what children are added to.
12363 return this.el.select(".roo-card-uploader-button-container").first();
12366 initEvents : function()
12369 Roo.bootstrap.Input.prototype.initEvents.call(this);
12373 xns: Roo.bootstrap,
12376 container_method : 'getButtonContainer' ,
12377 html : this.html, // fix changable?
12380 'click' : function(btn, e) {
12389 this.urlAPI = (window.createObjectURL && window) ||
12390 (window.URL && URL.revokeObjectURL && URL) ||
12391 (window.webkitURL && webkitURL);
12396 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12398 this.selectorEl.on('change', this.onFileSelected, this);
12401 this.images.forEach(function(img) {
12404 this.images = false;
12406 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12412 onClick : function(e)
12414 e.preventDefault();
12416 this.selectorEl.dom.click();
12420 onFileSelected : function(e)
12422 e.preventDefault();
12424 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12428 Roo.each(this.selectorEl.dom.files, function(file){
12429 this.addFile(file);
12438 addFile : function(file)
12441 if(typeof(file) === 'string'){
12442 throw "Add file by name?"; // should not happen
12446 if(!file || !this.urlAPI){
12456 var url = _this.urlAPI.createObjectURL( file);
12459 id : Roo.bootstrap.CardUploader.ID--,
12460 is_uploaded : false,
12463 mimetype : file.type,
12470 addCard : function (data)
12472 // hidden input element?
12473 // if the file is not an image...
12474 //then we need to use something other that and header_image
12479 xns : Roo.bootstrap,
12480 xtype : 'CardFooter',
12483 xns : Roo.bootstrap,
12489 xns : Roo.bootstrap,
12491 html : String.format("<small>{0}</small>", data.title),
12492 cls : 'col-11 text-left',
12497 click : function() {
12498 this.downloadCard(data.id)
12504 xns : Roo.bootstrap,
12512 click : function() {
12513 t.removeCard(data.id)
12525 var cn = this.addxtype(
12528 xns : Roo.bootstrap,
12531 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12532 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12533 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12538 initEvents : function() {
12539 Roo.bootstrap.Card.prototype.initEvents.call(this);
12540 this.imgEl = this.el.select('.card-img-top').first();
12542 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12543 this.imgEl.set({ 'pointer' : 'cursor' });
12552 // dont' really need ot update items.
12553 // this.items.push(cn);
12554 this.fileCollection.add(cn);
12555 this.updateInput();
12558 removeCard : function(id)
12561 var card = this.fileCollection.get(id);
12562 card.data.is_deleted = 1;
12563 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12564 this.fileCollection.remove(card);
12565 //this.items = this.items.filter(function(e) { return e != card });
12566 // dont' really need ot update items.
12567 card.el.dom.parentNode.removeChild(card.el.dom);
12572 this.fileCollection.each(function(card) {
12573 card.el.dom.parentNode.removeChild(card.el.dom);
12575 this.fileCollection.clear();
12576 this.updateInput();
12579 updateInput : function()
12582 this.fileCollection.each(function(e) {
12586 this.inputEl().dom.value = JSON.stringify(data);
12593 Roo.bootstrap.CardUploader.ID = -1;/*
12595 * Ext JS Library 1.1.1
12596 * Copyright(c) 2006-2007, Ext JS, LLC.
12598 * Originally Released Under LGPL - original licence link has changed is not relivant.
12601 * <script type="text/javascript">
12606 * @class Roo.data.SortTypes
12608 * Defines the default sorting (casting?) comparison functions used when sorting data.
12610 Roo.data.SortTypes = {
12612 * Default sort that does nothing
12613 * @param {Mixed} s The value being converted
12614 * @return {Mixed} The comparison value
12616 none : function(s){
12621 * The regular expression used to strip tags
12625 stripTagsRE : /<\/?[^>]+>/gi,
12628 * Strips all HTML tags to sort on text only
12629 * @param {Mixed} s The value being converted
12630 * @return {String} The comparison value
12632 asText : function(s){
12633 return String(s).replace(this.stripTagsRE, "");
12637 * Strips all HTML tags to sort on text only - Case insensitive
12638 * @param {Mixed} s The value being converted
12639 * @return {String} The comparison value
12641 asUCText : function(s){
12642 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12646 * Case insensitive string
12647 * @param {Mixed} s The value being converted
12648 * @return {String} The comparison value
12650 asUCString : function(s) {
12651 return String(s).toUpperCase();
12656 * @param {Mixed} s The value being converted
12657 * @return {Number} The comparison value
12659 asDate : function(s) {
12663 if(s instanceof Date){
12664 return s.getTime();
12666 return Date.parse(String(s));
12671 * @param {Mixed} s The value being converted
12672 * @return {Float} The comparison value
12674 asFloat : function(s) {
12675 var val = parseFloat(String(s).replace(/,/g, ""));
12684 * @param {Mixed} s The value being converted
12685 * @return {Number} The comparison value
12687 asInt : function(s) {
12688 var val = parseInt(String(s).replace(/,/g, ""));
12696 * Ext JS Library 1.1.1
12697 * Copyright(c) 2006-2007, Ext JS, LLC.
12699 * Originally Released Under LGPL - original licence link has changed is not relivant.
12702 * <script type="text/javascript">
12706 * @class Roo.data.Record
12707 * Instances of this class encapsulate both record <em>definition</em> information, and record
12708 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12709 * to access Records cached in an {@link Roo.data.Store} object.<br>
12711 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12712 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12715 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12717 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12718 * {@link #create}. The parameters are the same.
12719 * @param {Array} data An associative Array of data values keyed by the field name.
12720 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12721 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12722 * not specified an integer id is generated.
12724 Roo.data.Record = function(data, id){
12725 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12730 * Generate a constructor for a specific record layout.
12731 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12732 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12733 * Each field definition object may contain the following properties: <ul>
12734 * <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,
12735 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12736 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12737 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12738 * is being used, then this is a string containing the javascript expression to reference the data relative to
12739 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12740 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12741 * this may be omitted.</p></li>
12742 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12743 * <ul><li>auto (Default, implies no conversion)</li>
12748 * <li>date</li></ul></p></li>
12749 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12750 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12751 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12752 * by the Reader into an object that will be stored in the Record. It is passed the
12753 * following parameters:<ul>
12754 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12756 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12758 * <br>usage:<br><pre><code>
12759 var TopicRecord = Roo.data.Record.create(
12760 {name: 'title', mapping: 'topic_title'},
12761 {name: 'author', mapping: 'username'},
12762 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12763 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12764 {name: 'lastPoster', mapping: 'user2'},
12765 {name: 'excerpt', mapping: 'post_text'}
12768 var myNewRecord = new TopicRecord({
12769 title: 'Do my job please',
12772 lastPost: new Date(),
12773 lastPoster: 'Animal',
12774 excerpt: 'No way dude!'
12776 myStore.add(myNewRecord);
12781 Roo.data.Record.create = function(o){
12782 var f = function(){
12783 f.superclass.constructor.apply(this, arguments);
12785 Roo.extend(f, Roo.data.Record);
12786 var p = f.prototype;
12787 p.fields = new Roo.util.MixedCollection(false, function(field){
12790 for(var i = 0, len = o.length; i < len; i++){
12791 p.fields.add(new Roo.data.Field(o[i]));
12793 f.getField = function(name){
12794 return p.fields.get(name);
12799 Roo.data.Record.AUTO_ID = 1000;
12800 Roo.data.Record.EDIT = 'edit';
12801 Roo.data.Record.REJECT = 'reject';
12802 Roo.data.Record.COMMIT = 'commit';
12804 Roo.data.Record.prototype = {
12806 * Readonly flag - true if this record has been modified.
12815 join : function(store){
12816 this.store = store;
12820 * Set the named field to the specified value.
12821 * @param {String} name The name of the field to set.
12822 * @param {Object} value The value to set the field to.
12824 set : function(name, value){
12825 if(this.data[name] == value){
12829 if(!this.modified){
12830 this.modified = {};
12832 if(typeof this.modified[name] == 'undefined'){
12833 this.modified[name] = this.data[name];
12835 this.data[name] = value;
12836 if(!this.editing && this.store){
12837 this.store.afterEdit(this);
12842 * Get the value of the named field.
12843 * @param {String} name The name of the field to get the value of.
12844 * @return {Object} The value of the field.
12846 get : function(name){
12847 return this.data[name];
12851 beginEdit : function(){
12852 this.editing = true;
12853 this.modified = {};
12857 cancelEdit : function(){
12858 this.editing = false;
12859 delete this.modified;
12863 endEdit : function(){
12864 this.editing = false;
12865 if(this.dirty && this.store){
12866 this.store.afterEdit(this);
12871 * Usually called by the {@link Roo.data.Store} which owns the Record.
12872 * Rejects all changes made to the Record since either creation, or the last commit operation.
12873 * Modified fields are reverted to their original values.
12875 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12876 * of reject operations.
12878 reject : function(){
12879 var m = this.modified;
12881 if(typeof m[n] != "function"){
12882 this.data[n] = m[n];
12885 this.dirty = false;
12886 delete this.modified;
12887 this.editing = false;
12889 this.store.afterReject(this);
12894 * Usually called by the {@link Roo.data.Store} which owns the Record.
12895 * Commits all changes made to the Record since either creation, or the last commit operation.
12897 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12898 * of commit operations.
12900 commit : function(){
12901 this.dirty = false;
12902 delete this.modified;
12903 this.editing = false;
12905 this.store.afterCommit(this);
12910 hasError : function(){
12911 return this.error != null;
12915 clearError : function(){
12920 * Creates a copy of this record.
12921 * @param {String} id (optional) A new record id if you don't want to use this record's id
12924 copy : function(newId) {
12925 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12929 * Ext JS Library 1.1.1
12930 * Copyright(c) 2006-2007, Ext JS, LLC.
12932 * Originally Released Under LGPL - original licence link has changed is not relivant.
12935 * <script type="text/javascript">
12941 * @class Roo.data.Store
12942 * @extends Roo.util.Observable
12943 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12944 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12946 * 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
12947 * has no knowledge of the format of the data returned by the Proxy.<br>
12949 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12950 * instances from the data object. These records are cached and made available through accessor functions.
12952 * Creates a new Store.
12953 * @param {Object} config A config object containing the objects needed for the Store to access data,
12954 * and read the data into Records.
12956 Roo.data.Store = function(config){
12957 this.data = new Roo.util.MixedCollection(false);
12958 this.data.getKey = function(o){
12961 this.baseParams = {};
12963 this.paramNames = {
12968 "multisort" : "_multisort"
12971 if(config && config.data){
12972 this.inlineData = config.data;
12973 delete config.data;
12976 Roo.apply(this, config);
12978 if(this.reader){ // reader passed
12979 this.reader = Roo.factory(this.reader, Roo.data);
12980 this.reader.xmodule = this.xmodule || false;
12981 if(!this.recordType){
12982 this.recordType = this.reader.recordType;
12984 if(this.reader.onMetaChange){
12985 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12989 if(this.recordType){
12990 this.fields = this.recordType.prototype.fields;
12992 this.modified = [];
12996 * @event datachanged
12997 * Fires when the data cache has changed, and a widget which is using this Store
12998 * as a Record cache should refresh its view.
12999 * @param {Store} this
13001 datachanged : true,
13003 * @event metachange
13004 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13005 * @param {Store} this
13006 * @param {Object} meta The JSON metadata
13011 * Fires when Records have been added to the Store
13012 * @param {Store} this
13013 * @param {Roo.data.Record[]} records The array of Records added
13014 * @param {Number} index The index at which the record(s) were added
13019 * Fires when a Record has been removed from the Store
13020 * @param {Store} this
13021 * @param {Roo.data.Record} record The Record that was removed
13022 * @param {Number} index The index at which the record was removed
13027 * Fires when a Record has been updated
13028 * @param {Store} this
13029 * @param {Roo.data.Record} record The Record that was updated
13030 * @param {String} operation The update operation being performed. Value may be one of:
13032 Roo.data.Record.EDIT
13033 Roo.data.Record.REJECT
13034 Roo.data.Record.COMMIT
13040 * Fires when the data cache has been cleared.
13041 * @param {Store} this
13045 * @event beforeload
13046 * Fires before a request is made for a new data object. If the beforeload handler returns false
13047 * the load action will be canceled.
13048 * @param {Store} this
13049 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13053 * @event beforeloadadd
13054 * Fires after a new set of Records has been loaded.
13055 * @param {Store} this
13056 * @param {Roo.data.Record[]} records The Records that were loaded
13057 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13059 beforeloadadd : true,
13062 * Fires after a new set of Records has been loaded, before they are added to the store.
13063 * @param {Store} this
13064 * @param {Roo.data.Record[]} records The Records that were loaded
13065 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13066 * @params {Object} return from reader
13070 * @event loadexception
13071 * Fires if an exception occurs in the Proxy during loading.
13072 * Called with the signature of the Proxy's "loadexception" event.
13073 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13076 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13077 * @param {Object} load options
13078 * @param {Object} jsonData from your request (normally this contains the Exception)
13080 loadexception : true
13084 this.proxy = Roo.factory(this.proxy, Roo.data);
13085 this.proxy.xmodule = this.xmodule || false;
13086 this.relayEvents(this.proxy, ["loadexception"]);
13088 this.sortToggle = {};
13089 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13091 Roo.data.Store.superclass.constructor.call(this);
13093 if(this.inlineData){
13094 this.loadData(this.inlineData);
13095 delete this.inlineData;
13099 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13101 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13102 * without a remote query - used by combo/forms at present.
13106 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13109 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13112 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13113 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13116 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13117 * on any HTTP request
13120 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13123 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13127 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13128 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13130 remoteSort : false,
13133 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13134 * loaded or when a record is removed. (defaults to false).
13136 pruneModifiedRecords : false,
13139 lastOptions : null,
13142 * Add Records to the Store and fires the add event.
13143 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13145 add : function(records){
13146 records = [].concat(records);
13147 for(var i = 0, len = records.length; i < len; i++){
13148 records[i].join(this);
13150 var index = this.data.length;
13151 this.data.addAll(records);
13152 this.fireEvent("add", this, records, index);
13156 * Remove a Record from the Store and fires the remove event.
13157 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13159 remove : function(record){
13160 var index = this.data.indexOf(record);
13161 this.data.removeAt(index);
13163 if(this.pruneModifiedRecords){
13164 this.modified.remove(record);
13166 this.fireEvent("remove", this, record, index);
13170 * Remove all Records from the Store and fires the clear event.
13172 removeAll : function(){
13174 if(this.pruneModifiedRecords){
13175 this.modified = [];
13177 this.fireEvent("clear", this);
13181 * Inserts Records to the Store at the given index and fires the add event.
13182 * @param {Number} index The start index at which to insert the passed Records.
13183 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13185 insert : function(index, records){
13186 records = [].concat(records);
13187 for(var i = 0, len = records.length; i < len; i++){
13188 this.data.insert(index, records[i]);
13189 records[i].join(this);
13191 this.fireEvent("add", this, records, index);
13195 * Get the index within the cache of the passed Record.
13196 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13197 * @return {Number} The index of the passed Record. Returns -1 if not found.
13199 indexOf : function(record){
13200 return this.data.indexOf(record);
13204 * Get the index within the cache of the Record with the passed id.
13205 * @param {String} id The id of the Record to find.
13206 * @return {Number} The index of the Record. Returns -1 if not found.
13208 indexOfId : function(id){
13209 return this.data.indexOfKey(id);
13213 * Get the Record with the specified id.
13214 * @param {String} id The id of the Record to find.
13215 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13217 getById : function(id){
13218 return this.data.key(id);
13222 * Get the Record at the specified index.
13223 * @param {Number} index The index of the Record to find.
13224 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13226 getAt : function(index){
13227 return this.data.itemAt(index);
13231 * Returns a range of Records between specified indices.
13232 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13233 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13234 * @return {Roo.data.Record[]} An array of Records
13236 getRange : function(start, end){
13237 return this.data.getRange(start, end);
13241 storeOptions : function(o){
13242 o = Roo.apply({}, o);
13245 this.lastOptions = o;
13249 * Loads the Record cache from the configured Proxy using the configured Reader.
13251 * If using remote paging, then the first load call must specify the <em>start</em>
13252 * and <em>limit</em> properties in the options.params property to establish the initial
13253 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13255 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13256 * and this call will return before the new data has been loaded. Perform any post-processing
13257 * in a callback function, or in a "load" event handler.</strong>
13259 * @param {Object} options An object containing properties which control loading options:<ul>
13260 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13261 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13262 * passed the following arguments:<ul>
13263 * <li>r : Roo.data.Record[]</li>
13264 * <li>options: Options object from the load call</li>
13265 * <li>success: Boolean success indicator</li></ul></li>
13266 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13267 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13270 load : function(options){
13271 options = options || {};
13272 if(this.fireEvent("beforeload", this, options) !== false){
13273 this.storeOptions(options);
13274 var p = Roo.apply(options.params || {}, this.baseParams);
13275 // if meta was not loaded from remote source.. try requesting it.
13276 if (!this.reader.metaFromRemote) {
13277 p._requestMeta = 1;
13279 if(this.sortInfo && this.remoteSort){
13280 var pn = this.paramNames;
13281 p[pn["sort"]] = this.sortInfo.field;
13282 p[pn["dir"]] = this.sortInfo.direction;
13284 if (this.multiSort) {
13285 var pn = this.paramNames;
13286 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13289 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13294 * Reloads the Record cache from the configured Proxy using the configured Reader and
13295 * the options from the last load operation performed.
13296 * @param {Object} options (optional) An object containing properties which may override the options
13297 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13298 * the most recently used options are reused).
13300 reload : function(options){
13301 this.load(Roo.applyIf(options||{}, this.lastOptions));
13305 // Called as a callback by the Reader during a load operation.
13306 loadRecords : function(o, options, success){
13307 if(!o || success === false){
13308 if(success !== false){
13309 this.fireEvent("load", this, [], options, o);
13311 if(options.callback){
13312 options.callback.call(options.scope || this, [], options, false);
13316 // if data returned failure - throw an exception.
13317 if (o.success === false) {
13318 // show a message if no listener is registered.
13319 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13320 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13322 // loadmask wil be hooked into this..
13323 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13326 var r = o.records, t = o.totalRecords || r.length;
13328 this.fireEvent("beforeloadadd", this, r, options, o);
13330 if(!options || options.add !== true){
13331 if(this.pruneModifiedRecords){
13332 this.modified = [];
13334 for(var i = 0, len = r.length; i < len; i++){
13338 this.data = this.snapshot;
13339 delete this.snapshot;
13342 this.data.addAll(r);
13343 this.totalLength = t;
13345 this.fireEvent("datachanged", this);
13347 this.totalLength = Math.max(t, this.data.length+r.length);
13351 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13353 var e = new Roo.data.Record({});
13355 e.set(this.parent.displayField, this.parent.emptyTitle);
13356 e.set(this.parent.valueField, '');
13361 this.fireEvent("load", this, r, options, o);
13362 if(options.callback){
13363 options.callback.call(options.scope || this, r, options, true);
13369 * Loads data from a passed data block. A Reader which understands the format of the data
13370 * must have been configured in the constructor.
13371 * @param {Object} data The data block from which to read the Records. The format of the data expected
13372 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13373 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13375 loadData : function(o, append){
13376 var r = this.reader.readRecords(o);
13377 this.loadRecords(r, {add: append}, true);
13381 * using 'cn' the nested child reader read the child array into it's child stores.
13382 * @param {Object} rec The record with a 'children array
13384 loadDataFromChildren : function(rec)
13386 this.loadData(this.reader.toLoadData(rec));
13391 * Gets the number of cached records.
13393 * <em>If using paging, this may not be the total size of the dataset. If the data object
13394 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13395 * the data set size</em>
13397 getCount : function(){
13398 return this.data.length || 0;
13402 * Gets the total number of records in the dataset as returned by the server.
13404 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13405 * the dataset size</em>
13407 getTotalCount : function(){
13408 return this.totalLength || 0;
13412 * Returns the sort state of the Store as an object with two properties:
13414 field {String} The name of the field by which the Records are sorted
13415 direction {String} The sort order, "ASC" or "DESC"
13418 getSortState : function(){
13419 return this.sortInfo;
13423 applySort : function(){
13424 if(this.sortInfo && !this.remoteSort){
13425 var s = this.sortInfo, f = s.field;
13426 var st = this.fields.get(f).sortType;
13427 var fn = function(r1, r2){
13428 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13429 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13431 this.data.sort(s.direction, fn);
13432 if(this.snapshot && this.snapshot != this.data){
13433 this.snapshot.sort(s.direction, fn);
13439 * Sets the default sort column and order to be used by the next load operation.
13440 * @param {String} fieldName The name of the field to sort by.
13441 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13443 setDefaultSort : function(field, dir){
13444 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13448 * Sort the Records.
13449 * If remote sorting is used, the sort is performed on the server, and the cache is
13450 * reloaded. If local sorting is used, the cache is sorted internally.
13451 * @param {String} fieldName The name of the field to sort by.
13452 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13454 sort : function(fieldName, dir){
13455 var f = this.fields.get(fieldName);
13457 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13459 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13460 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13465 this.sortToggle[f.name] = dir;
13466 this.sortInfo = {field: f.name, direction: dir};
13467 if(!this.remoteSort){
13469 this.fireEvent("datachanged", this);
13471 this.load(this.lastOptions);
13476 * Calls the specified function for each of the Records in the cache.
13477 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13478 * Returning <em>false</em> aborts and exits the iteration.
13479 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13481 each : function(fn, scope){
13482 this.data.each(fn, scope);
13486 * Gets all records modified since the last commit. Modified records are persisted across load operations
13487 * (e.g., during paging).
13488 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13490 getModifiedRecords : function(){
13491 return this.modified;
13495 createFilterFn : function(property, value, anyMatch){
13496 if(!value.exec){ // not a regex
13497 value = String(value);
13498 if(value.length == 0){
13501 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13503 return function(r){
13504 return value.test(r.data[property]);
13509 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13510 * @param {String} property A field on your records
13511 * @param {Number} start The record index to start at (defaults to 0)
13512 * @param {Number} end The last record index to include (defaults to length - 1)
13513 * @return {Number} The sum
13515 sum : function(property, start, end){
13516 var rs = this.data.items, v = 0;
13517 start = start || 0;
13518 end = (end || end === 0) ? end : rs.length-1;
13520 for(var i = start; i <= end; i++){
13521 v += (rs[i].data[property] || 0);
13527 * Filter the records by a specified property.
13528 * @param {String} field A field on your records
13529 * @param {String/RegExp} value Either a string that the field
13530 * should start with or a RegExp to test against the field
13531 * @param {Boolean} anyMatch True to match any part not just the beginning
13533 filter : function(property, value, anyMatch){
13534 var fn = this.createFilterFn(property, value, anyMatch);
13535 return fn ? this.filterBy(fn) : this.clearFilter();
13539 * Filter by a function. The specified function will be called with each
13540 * record in this data source. If the function returns true the record is included,
13541 * otherwise it is filtered.
13542 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13543 * @param {Object} scope (optional) The scope of the function (defaults to this)
13545 filterBy : function(fn, scope){
13546 this.snapshot = this.snapshot || this.data;
13547 this.data = this.queryBy(fn, scope||this);
13548 this.fireEvent("datachanged", this);
13552 * Query the records by a specified property.
13553 * @param {String} field A field on your records
13554 * @param {String/RegExp} value Either a string that the field
13555 * should start with or a RegExp to test against the field
13556 * @param {Boolean} anyMatch True to match any part not just the beginning
13557 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13559 query : function(property, value, anyMatch){
13560 var fn = this.createFilterFn(property, value, anyMatch);
13561 return fn ? this.queryBy(fn) : this.data.clone();
13565 * Query by a function. The specified function will be called with each
13566 * record in this data source. If the function returns true the record is included
13568 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13569 * @param {Object} scope (optional) The scope of the function (defaults to this)
13570 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13572 queryBy : function(fn, scope){
13573 var data = this.snapshot || this.data;
13574 return data.filterBy(fn, scope||this);
13578 * Collects unique values for a particular dataIndex from this store.
13579 * @param {String} dataIndex The property to collect
13580 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13581 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13582 * @return {Array} An array of the unique values
13584 collect : function(dataIndex, allowNull, bypassFilter){
13585 var d = (bypassFilter === true && this.snapshot) ?
13586 this.snapshot.items : this.data.items;
13587 var v, sv, r = [], l = {};
13588 for(var i = 0, len = d.length; i < len; i++){
13589 v = d[i].data[dataIndex];
13591 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13600 * Revert to a view of the Record cache with no filtering applied.
13601 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13603 clearFilter : function(suppressEvent){
13604 if(this.snapshot && this.snapshot != this.data){
13605 this.data = this.snapshot;
13606 delete this.snapshot;
13607 if(suppressEvent !== true){
13608 this.fireEvent("datachanged", this);
13614 afterEdit : function(record){
13615 if(this.modified.indexOf(record) == -1){
13616 this.modified.push(record);
13618 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13622 afterReject : function(record){
13623 this.modified.remove(record);
13624 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13628 afterCommit : function(record){
13629 this.modified.remove(record);
13630 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13634 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13635 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13637 commitChanges : function(){
13638 var m = this.modified.slice(0);
13639 this.modified = [];
13640 for(var i = 0, len = m.length; i < len; i++){
13646 * Cancel outstanding changes on all changed records.
13648 rejectChanges : function(){
13649 var m = this.modified.slice(0);
13650 this.modified = [];
13651 for(var i = 0, len = m.length; i < len; i++){
13656 onMetaChange : function(meta, rtype, o){
13657 this.recordType = rtype;
13658 this.fields = rtype.prototype.fields;
13659 delete this.snapshot;
13660 this.sortInfo = meta.sortInfo || this.sortInfo;
13661 this.modified = [];
13662 this.fireEvent('metachange', this, this.reader.meta);
13665 moveIndex : function(data, type)
13667 var index = this.indexOf(data);
13669 var newIndex = index + type;
13673 this.insert(newIndex, data);
13678 * Ext JS Library 1.1.1
13679 * Copyright(c) 2006-2007, Ext JS, LLC.
13681 * Originally Released Under LGPL - original licence link has changed is not relivant.
13684 * <script type="text/javascript">
13688 * @class Roo.data.SimpleStore
13689 * @extends Roo.data.Store
13690 * Small helper class to make creating Stores from Array data easier.
13691 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13692 * @cfg {Array} fields An array of field definition objects, or field name strings.
13693 * @cfg {Object} an existing reader (eg. copied from another store)
13694 * @cfg {Array} data The multi-dimensional array of data
13696 * @param {Object} config
13698 Roo.data.SimpleStore = function(config)
13700 Roo.data.SimpleStore.superclass.constructor.call(this, {
13702 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13705 Roo.data.Record.create(config.fields)
13707 proxy : new Roo.data.MemoryProxy(config.data)
13711 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13713 * Ext JS Library 1.1.1
13714 * Copyright(c) 2006-2007, Ext JS, LLC.
13716 * Originally Released Under LGPL - original licence link has changed is not relivant.
13719 * <script type="text/javascript">
13724 * @extends Roo.data.Store
13725 * @class Roo.data.JsonStore
13726 * Small helper class to make creating Stores for JSON data easier. <br/>
13728 var store = new Roo.data.JsonStore({
13729 url: 'get-images.php',
13731 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13734 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13735 * JsonReader and HttpProxy (unless inline data is provided).</b>
13736 * @cfg {Array} fields An array of field definition objects, or field name strings.
13738 * @param {Object} config
13740 Roo.data.JsonStore = function(c){
13741 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13742 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13743 reader: new Roo.data.JsonReader(c, c.fields)
13746 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13748 * Ext JS Library 1.1.1
13749 * Copyright(c) 2006-2007, Ext JS, LLC.
13751 * Originally Released Under LGPL - original licence link has changed is not relivant.
13754 * <script type="text/javascript">
13758 Roo.data.Field = function(config){
13759 if(typeof config == "string"){
13760 config = {name: config};
13762 Roo.apply(this, config);
13765 this.type = "auto";
13768 var st = Roo.data.SortTypes;
13769 // named sortTypes are supported, here we look them up
13770 if(typeof this.sortType == "string"){
13771 this.sortType = st[this.sortType];
13774 // set default sortType for strings and dates
13775 if(!this.sortType){
13778 this.sortType = st.asUCString;
13781 this.sortType = st.asDate;
13784 this.sortType = st.none;
13789 var stripRe = /[\$,%]/g;
13791 // prebuilt conversion function for this field, instead of
13792 // switching every time we're reading a value
13794 var cv, dateFormat = this.dateFormat;
13799 cv = function(v){ return v; };
13802 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13806 return v !== undefined && v !== null && v !== '' ?
13807 parseInt(String(v).replace(stripRe, ""), 10) : '';
13812 return v !== undefined && v !== null && v !== '' ?
13813 parseFloat(String(v).replace(stripRe, ""), 10) : '';
13818 cv = function(v){ return v === true || v === "true" || v == 1; };
13825 if(v instanceof Date){
13829 if(dateFormat == "timestamp"){
13830 return new Date(v*1000);
13832 return Date.parseDate(v, dateFormat);
13834 var parsed = Date.parse(v);
13835 return parsed ? new Date(parsed) : null;
13844 Roo.data.Field.prototype = {
13852 * Ext JS Library 1.1.1
13853 * Copyright(c) 2006-2007, Ext JS, LLC.
13855 * Originally Released Under LGPL - original licence link has changed is not relivant.
13858 * <script type="text/javascript">
13861 // Base class for reading structured data from a data source. This class is intended to be
13862 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13865 * @class Roo.data.DataReader
13866 * Base class for reading structured data from a data source. This class is intended to be
13867 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13870 Roo.data.DataReader = function(meta, recordType){
13874 this.recordType = recordType instanceof Array ?
13875 Roo.data.Record.create(recordType) : recordType;
13878 Roo.data.DataReader.prototype = {
13881 readerType : 'Data',
13883 * Create an empty record
13884 * @param {Object} data (optional) - overlay some values
13885 * @return {Roo.data.Record} record created.
13887 newRow : function(d) {
13889 this.recordType.prototype.fields.each(function(c) {
13891 case 'int' : da[c.name] = 0; break;
13892 case 'date' : da[c.name] = new Date(); break;
13893 case 'float' : da[c.name] = 0.0; break;
13894 case 'boolean' : da[c.name] = false; break;
13895 default : da[c.name] = ""; break;
13899 return new this.recordType(Roo.apply(da, d));
13905 * Ext JS Library 1.1.1
13906 * Copyright(c) 2006-2007, Ext JS, LLC.
13908 * Originally Released Under LGPL - original licence link has changed is not relivant.
13911 * <script type="text/javascript">
13915 * @class Roo.data.DataProxy
13916 * @extends Roo.data.Observable
13917 * This class is an abstract base class for implementations which provide retrieval of
13918 * unformatted data objects.<br>
13920 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13921 * (of the appropriate type which knows how to parse the data object) to provide a block of
13922 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13924 * Custom implementations must implement the load method as described in
13925 * {@link Roo.data.HttpProxy#load}.
13927 Roo.data.DataProxy = function(){
13930 * @event beforeload
13931 * Fires before a network request is made to retrieve a data object.
13932 * @param {Object} This DataProxy object.
13933 * @param {Object} params The params parameter to the load function.
13938 * Fires before the load method's callback is called.
13939 * @param {Object} This DataProxy object.
13940 * @param {Object} o The data object.
13941 * @param {Object} arg The callback argument object passed to the load function.
13945 * @event loadexception
13946 * Fires if an Exception occurs during data retrieval.
13947 * @param {Object} This DataProxy object.
13948 * @param {Object} o The data object.
13949 * @param {Object} arg The callback argument object passed to the load function.
13950 * @param {Object} e The Exception.
13952 loadexception : true
13954 Roo.data.DataProxy.superclass.constructor.call(this);
13957 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13960 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13964 * Ext JS Library 1.1.1
13965 * Copyright(c) 2006-2007, Ext JS, LLC.
13967 * Originally Released Under LGPL - original licence link has changed is not relivant.
13970 * <script type="text/javascript">
13973 * @class Roo.data.MemoryProxy
13974 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13975 * to the Reader when its load method is called.
13977 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13979 Roo.data.MemoryProxy = function(data){
13983 Roo.data.MemoryProxy.superclass.constructor.call(this);
13987 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13990 * Load data from the requested source (in this case an in-memory
13991 * data object passed to the constructor), read the data object into
13992 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13993 * process that block using the passed callback.
13994 * @param {Object} params This parameter is not used by the MemoryProxy class.
13995 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13996 * object into a block of Roo.data.Records.
13997 * @param {Function} callback The function into which to pass the block of Roo.data.records.
13998 * The function must be passed <ul>
13999 * <li>The Record block object</li>
14000 * <li>The "arg" argument from the load function</li>
14001 * <li>A boolean success indicator</li>
14003 * @param {Object} scope The scope in which to call the callback
14004 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14006 load : function(params, reader, callback, scope, arg){
14007 params = params || {};
14010 result = reader.readRecords(params.data ? params.data :this.data);
14012 this.fireEvent("loadexception", this, arg, null, e);
14013 callback.call(scope, null, arg, false);
14016 callback.call(scope, result, arg, true);
14020 update : function(params, records){
14025 * Ext JS Library 1.1.1
14026 * Copyright(c) 2006-2007, Ext JS, LLC.
14028 * Originally Released Under LGPL - original licence link has changed is not relivant.
14031 * <script type="text/javascript">
14034 * @class Roo.data.HttpProxy
14035 * @extends Roo.data.DataProxy
14036 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14037 * configured to reference a certain URL.<br><br>
14039 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14040 * from which the running page was served.<br><br>
14042 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14044 * Be aware that to enable the browser to parse an XML document, the server must set
14045 * the Content-Type header in the HTTP response to "text/xml".
14047 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14048 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14049 * will be used to make the request.
14051 Roo.data.HttpProxy = function(conn){
14052 Roo.data.HttpProxy.superclass.constructor.call(this);
14053 // is conn a conn config or a real conn?
14055 this.useAjax = !conn || !conn.events;
14059 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14060 // thse are take from connection...
14063 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14066 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14067 * extra parameters to each request made by this object. (defaults to undefined)
14070 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14071 * to each request made by this object. (defaults to undefined)
14074 * @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)
14077 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14080 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14086 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14090 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14091 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14092 * a finer-grained basis than the DataProxy events.
14094 getConnection : function(){
14095 return this.useAjax ? Roo.Ajax : this.conn;
14099 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14100 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14101 * process that block using the passed callback.
14102 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14103 * for the request to the remote server.
14104 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14105 * object into a block of Roo.data.Records.
14106 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14107 * The function must be passed <ul>
14108 * <li>The Record block object</li>
14109 * <li>The "arg" argument from the load function</li>
14110 * <li>A boolean success indicator</li>
14112 * @param {Object} scope The scope in which to call the callback
14113 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14115 load : function(params, reader, callback, scope, arg){
14116 if(this.fireEvent("beforeload", this, params) !== false){
14118 params : params || {},
14120 callback : callback,
14125 callback : this.loadResponse,
14129 Roo.applyIf(o, this.conn);
14130 if(this.activeRequest){
14131 Roo.Ajax.abort(this.activeRequest);
14133 this.activeRequest = Roo.Ajax.request(o);
14135 this.conn.request(o);
14138 callback.call(scope||this, null, arg, false);
14143 loadResponse : function(o, success, response){
14144 delete this.activeRequest;
14146 this.fireEvent("loadexception", this, o, response);
14147 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14152 result = o.reader.read(response);
14154 this.fireEvent("loadexception", this, o, response, e);
14155 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14159 this.fireEvent("load", this, o, o.request.arg);
14160 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14164 update : function(dataSet){
14169 updateResponse : function(dataSet){
14174 * Ext JS Library 1.1.1
14175 * Copyright(c) 2006-2007, Ext JS, LLC.
14177 * Originally Released Under LGPL - original licence link has changed is not relivant.
14180 * <script type="text/javascript">
14184 * @class Roo.data.ScriptTagProxy
14185 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14186 * other than the originating domain of the running page.<br><br>
14188 * <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
14189 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14191 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14192 * source code that is used as the source inside a <script> tag.<br><br>
14194 * In order for the browser to process the returned data, the server must wrap the data object
14195 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14196 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14197 * depending on whether the callback name was passed:
14200 boolean scriptTag = false;
14201 String cb = request.getParameter("callback");
14204 response.setContentType("text/javascript");
14206 response.setContentType("application/x-json");
14208 Writer out = response.getWriter();
14210 out.write(cb + "(");
14212 out.print(dataBlock.toJsonString());
14219 * @param {Object} config A configuration object.
14221 Roo.data.ScriptTagProxy = function(config){
14222 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14223 Roo.apply(this, config);
14224 this.head = document.getElementsByTagName("head")[0];
14227 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14229 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14231 * @cfg {String} url The URL from which to request the data object.
14234 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14238 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14239 * the server the name of the callback function set up by the load call to process the returned data object.
14240 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14241 * javascript output which calls this named function passing the data object as its only parameter.
14243 callbackParam : "callback",
14245 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14246 * name to the request.
14251 * Load data from the configured URL, read the data object into
14252 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14253 * process that block using the passed callback.
14254 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14255 * for the request to the remote server.
14256 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14257 * object into a block of Roo.data.Records.
14258 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14259 * The function must be passed <ul>
14260 * <li>The Record block object</li>
14261 * <li>The "arg" argument from the load function</li>
14262 * <li>A boolean success indicator</li>
14264 * @param {Object} scope The scope in which to call the callback
14265 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14267 load : function(params, reader, callback, scope, arg){
14268 if(this.fireEvent("beforeload", this, params) !== false){
14270 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14272 var url = this.url;
14273 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14275 url += "&_dc=" + (new Date().getTime());
14277 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14280 cb : "stcCallback"+transId,
14281 scriptId : "stcScript"+transId,
14285 callback : callback,
14291 window[trans.cb] = function(o){
14292 conn.handleResponse(o, trans);
14295 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14297 if(this.autoAbort !== false){
14301 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14303 var script = document.createElement("script");
14304 script.setAttribute("src", url);
14305 script.setAttribute("type", "text/javascript");
14306 script.setAttribute("id", trans.scriptId);
14307 this.head.appendChild(script);
14309 this.trans = trans;
14311 callback.call(scope||this, null, arg, false);
14316 isLoading : function(){
14317 return this.trans ? true : false;
14321 * Abort the current server request.
14323 abort : function(){
14324 if(this.isLoading()){
14325 this.destroyTrans(this.trans);
14330 destroyTrans : function(trans, isLoaded){
14331 this.head.removeChild(document.getElementById(trans.scriptId));
14332 clearTimeout(trans.timeoutId);
14334 window[trans.cb] = undefined;
14336 delete window[trans.cb];
14339 // if hasn't been loaded, wait for load to remove it to prevent script error
14340 window[trans.cb] = function(){
14341 window[trans.cb] = undefined;
14343 delete window[trans.cb];
14350 handleResponse : function(o, trans){
14351 this.trans = false;
14352 this.destroyTrans(trans, true);
14355 result = trans.reader.readRecords(o);
14357 this.fireEvent("loadexception", this, o, trans.arg, e);
14358 trans.callback.call(trans.scope||window, null, trans.arg, false);
14361 this.fireEvent("load", this, o, trans.arg);
14362 trans.callback.call(trans.scope||window, result, trans.arg, true);
14366 handleFailure : function(trans){
14367 this.trans = false;
14368 this.destroyTrans(trans, false);
14369 this.fireEvent("loadexception", this, null, trans.arg);
14370 trans.callback.call(trans.scope||window, null, trans.arg, false);
14374 * Ext JS Library 1.1.1
14375 * Copyright(c) 2006-2007, Ext JS, LLC.
14377 * Originally Released Under LGPL - original licence link has changed is not relivant.
14380 * <script type="text/javascript">
14384 * @class Roo.data.JsonReader
14385 * @extends Roo.data.DataReader
14386 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14387 * based on mappings in a provided Roo.data.Record constructor.
14389 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14390 * in the reply previously.
14395 var RecordDef = Roo.data.Record.create([
14396 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14397 {name: 'occupation'} // This field will use "occupation" as the mapping.
14399 var myReader = new Roo.data.JsonReader({
14400 totalProperty: "results", // The property which contains the total dataset size (optional)
14401 root: "rows", // The property which contains an Array of row objects
14402 id: "id" // The property within each row object that provides an ID for the record (optional)
14406 * This would consume a JSON file like this:
14408 { 'results': 2, 'rows': [
14409 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14410 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14413 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14414 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14415 * paged from the remote server.
14416 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14417 * @cfg {String} root name of the property which contains the Array of row objects.
14418 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14419 * @cfg {Array} fields Array of field definition objects
14421 * Create a new JsonReader
14422 * @param {Object} meta Metadata configuration options
14423 * @param {Object} recordType Either an Array of field definition objects,
14424 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14426 Roo.data.JsonReader = function(meta, recordType){
14429 // set some defaults:
14430 Roo.applyIf(meta, {
14431 totalProperty: 'total',
14432 successProperty : 'success',
14437 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14439 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14441 readerType : 'Json',
14444 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14445 * Used by Store query builder to append _requestMeta to params.
14448 metaFromRemote : false,
14450 * This method is only used by a DataProxy which has retrieved data from a remote server.
14451 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14452 * @return {Object} data A data block which is used by an Roo.data.Store object as
14453 * a cache of Roo.data.Records.
14455 read : function(response){
14456 var json = response.responseText;
14458 var o = /* eval:var:o */ eval("("+json+")");
14460 throw {message: "JsonReader.read: Json object not found"};
14466 this.metaFromRemote = true;
14467 this.meta = o.metaData;
14468 this.recordType = Roo.data.Record.create(o.metaData.fields);
14469 this.onMetaChange(this.meta, this.recordType, o);
14471 return this.readRecords(o);
14474 // private function a store will implement
14475 onMetaChange : function(meta, recordType, o){
14482 simpleAccess: function(obj, subsc) {
14489 getJsonAccessor: function(){
14491 return function(expr) {
14493 return(re.test(expr))
14494 ? new Function("obj", "return obj." + expr)
14499 return Roo.emptyFn;
14504 * Create a data block containing Roo.data.Records from an XML document.
14505 * @param {Object} o An object which contains an Array of row objects in the property specified
14506 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14507 * which contains the total size of the dataset.
14508 * @return {Object} data A data block which is used by an Roo.data.Store object as
14509 * a cache of Roo.data.Records.
14511 readRecords : function(o){
14513 * After any data loads, the raw JSON data is available for further custom processing.
14517 var s = this.meta, Record = this.recordType,
14518 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14520 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14522 if(s.totalProperty) {
14523 this.getTotal = this.getJsonAccessor(s.totalProperty);
14525 if(s.successProperty) {
14526 this.getSuccess = this.getJsonAccessor(s.successProperty);
14528 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14530 var g = this.getJsonAccessor(s.id);
14531 this.getId = function(rec) {
14533 return (r === undefined || r === "") ? null : r;
14536 this.getId = function(){return null;};
14539 for(var jj = 0; jj < fl; jj++){
14541 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14542 this.ef[jj] = this.getJsonAccessor(map);
14546 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14547 if(s.totalProperty){
14548 var vt = parseInt(this.getTotal(o), 10);
14553 if(s.successProperty){
14554 var vs = this.getSuccess(o);
14555 if(vs === false || vs === 'false'){
14560 for(var i = 0; i < c; i++){
14563 var id = this.getId(n);
14564 for(var j = 0; j < fl; j++){
14566 var v = this.ef[j](n);
14568 Roo.log('missing convert for ' + f.name);
14572 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14574 var record = new Record(values, id);
14576 records[i] = record;
14582 totalRecords : totalRecords
14585 // used when loading children.. @see loadDataFromChildren
14586 toLoadData: function(rec)
14588 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14589 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14590 return { data : data, total : data.length };
14595 * Ext JS Library 1.1.1
14596 * Copyright(c) 2006-2007, Ext JS, LLC.
14598 * Originally Released Under LGPL - original licence link has changed is not relivant.
14601 * <script type="text/javascript">
14605 * @class Roo.data.ArrayReader
14606 * @extends Roo.data.DataReader
14607 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14608 * Each element of that Array represents a row of data fields. The
14609 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14610 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14614 var RecordDef = Roo.data.Record.create([
14615 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14616 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14618 var myReader = new Roo.data.ArrayReader({
14619 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14623 * This would consume an Array like this:
14625 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14629 * Create a new JsonReader
14630 * @param {Object} meta Metadata configuration options.
14631 * @param {Object|Array} recordType Either an Array of field definition objects
14633 * @cfg {Array} fields Array of field definition objects
14634 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14635 * as specified to {@link Roo.data.Record#create},
14636 * or an {@link Roo.data.Record} object
14639 * created using {@link Roo.data.Record#create}.
14641 Roo.data.ArrayReader = function(meta, recordType)
14643 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14646 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14649 * Create a data block containing Roo.data.Records from an XML document.
14650 * @param {Object} o An Array of row objects which represents the dataset.
14651 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14652 * a cache of Roo.data.Records.
14654 readRecords : function(o)
14656 var sid = this.meta ? this.meta.id : null;
14657 var recordType = this.recordType, fields = recordType.prototype.fields;
14660 for(var i = 0; i < root.length; i++){
14663 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14664 for(var j = 0, jlen = fields.length; j < jlen; j++){
14665 var f = fields.items[j];
14666 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14667 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14669 values[f.name] = v;
14671 var record = new recordType(values, id);
14673 records[records.length] = record;
14677 totalRecords : records.length
14680 // used when loading children.. @see loadDataFromChildren
14681 toLoadData: function(rec)
14683 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14684 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14695 * @class Roo.bootstrap.ComboBox
14696 * @extends Roo.bootstrap.TriggerField
14697 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14698 * @cfg {Boolean} append (true|false) default false
14699 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14700 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14701 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14702 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14703 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14704 * @cfg {Boolean} animate default true
14705 * @cfg {Boolean} emptyResultText only for touch device
14706 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14707 * @cfg {String} emptyTitle default ''
14709 * Create a new ComboBox.
14710 * @param {Object} config Configuration options
14712 Roo.bootstrap.ComboBox = function(config){
14713 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14717 * Fires when the dropdown list is expanded
14718 * @param {Roo.bootstrap.ComboBox} combo This combo box
14723 * Fires when the dropdown list is collapsed
14724 * @param {Roo.bootstrap.ComboBox} combo This combo box
14728 * @event beforeselect
14729 * Fires before a list item is selected. Return false to cancel the selection.
14730 * @param {Roo.bootstrap.ComboBox} combo This combo box
14731 * @param {Roo.data.Record} record The data record returned from the underlying store
14732 * @param {Number} index The index of the selected item in the dropdown list
14734 'beforeselect' : true,
14737 * Fires when a list item is selected
14738 * @param {Roo.bootstrap.ComboBox} combo This combo box
14739 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14740 * @param {Number} index The index of the selected item in the dropdown list
14744 * @event beforequery
14745 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14746 * The event object passed has these properties:
14747 * @param {Roo.bootstrap.ComboBox} combo This combo box
14748 * @param {String} query The query
14749 * @param {Boolean} forceAll true to force "all" query
14750 * @param {Boolean} cancel true to cancel the query
14751 * @param {Object} e The query event object
14753 'beforequery': true,
14756 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14757 * @param {Roo.bootstrap.ComboBox} combo This combo box
14762 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14763 * @param {Roo.bootstrap.ComboBox} combo This combo box
14764 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14769 * Fires when the remove value from the combobox array
14770 * @param {Roo.bootstrap.ComboBox} combo This combo box
14774 * @event afterremove
14775 * Fires when the remove value from the combobox array
14776 * @param {Roo.bootstrap.ComboBox} combo This combo box
14778 'afterremove' : true,
14780 * @event specialfilter
14781 * Fires when specialfilter
14782 * @param {Roo.bootstrap.ComboBox} combo This combo box
14784 'specialfilter' : true,
14787 * Fires when tick the element
14788 * @param {Roo.bootstrap.ComboBox} combo This combo box
14792 * @event touchviewdisplay
14793 * Fires when touch view require special display (default is using displayField)
14794 * @param {Roo.bootstrap.ComboBox} combo This combo box
14795 * @param {Object} cfg set html .
14797 'touchviewdisplay' : true
14802 this.tickItems = [];
14804 this.selectedIndex = -1;
14805 if(this.mode == 'local'){
14806 if(config.queryDelay === undefined){
14807 this.queryDelay = 10;
14809 if(config.minChars === undefined){
14815 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14818 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14819 * rendering into an Roo.Editor, defaults to false)
14822 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14823 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14826 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14829 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14830 * the dropdown list (defaults to undefined, with no header element)
14834 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
14838 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14840 listWidth: undefined,
14842 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14843 * mode = 'remote' or 'text' if mode = 'local')
14845 displayField: undefined,
14848 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14849 * mode = 'remote' or 'value' if mode = 'local').
14850 * Note: use of a valueField requires the user make a selection
14851 * in order for a value to be mapped.
14853 valueField: undefined,
14855 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14860 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14861 * field's data value (defaults to the underlying DOM element's name)
14863 hiddenName: undefined,
14865 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14869 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14871 selectedClass: 'active',
14874 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14878 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14879 * anchor positions (defaults to 'tl-bl')
14881 listAlign: 'tl-bl?',
14883 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14887 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
14888 * query specified by the allQuery config option (defaults to 'query')
14890 triggerAction: 'query',
14892 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14893 * (defaults to 4, does not apply if editable = false)
14897 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14898 * delay (typeAheadDelay) if it matches a known value (defaults to false)
14902 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14903 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14907 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14908 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
14912 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
14913 * when editable = true (defaults to false)
14915 selectOnFocus:false,
14917 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14919 queryParam: 'query',
14921 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
14922 * when mode = 'remote' (defaults to 'Loading...')
14924 loadingText: 'Loading...',
14926 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14930 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14934 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14935 * traditional select (defaults to true)
14939 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14943 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14947 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14948 * listWidth has a higher value)
14952 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14953 * allow the user to set arbitrary text into the field (defaults to false)
14955 forceSelection:false,
14957 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14958 * if typeAhead = true (defaults to 250)
14960 typeAheadDelay : 250,
14962 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14963 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14965 valueNotFoundText : undefined,
14967 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14969 blockFocus : false,
14972 * @cfg {Boolean} disableClear Disable showing of clear button.
14974 disableClear : false,
14976 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
14978 alwaysQuery : false,
14981 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
14986 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14988 invalidClass : "has-warning",
14991 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14993 validClass : "has-success",
14996 * @cfg {Boolean} specialFilter (true|false) special filter default false
14998 specialFilter : false,
15001 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15003 mobileTouchView : true,
15006 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15008 useNativeIOS : false,
15011 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15013 mobile_restrict_height : false,
15015 ios_options : false,
15027 btnPosition : 'right',
15028 triggerList : true,
15029 showToggleBtn : true,
15031 emptyResultText: 'Empty',
15032 triggerText : 'Select',
15035 // element that contains real text value.. (when hidden is used..)
15037 getAutoCreate : function()
15042 * Render classic select for iso
15045 if(Roo.isIOS && this.useNativeIOS){
15046 cfg = this.getAutoCreateNativeIOS();
15054 if(Roo.isTouch && this.mobileTouchView){
15055 cfg = this.getAutoCreateTouchView();
15062 if(!this.tickable){
15063 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15068 * ComboBox with tickable selections
15071 var align = this.labelAlign || this.parentLabelAlign();
15074 cls : 'form-group roo-combobox-tickable' //input-group
15077 var btn_text_select = '';
15078 var btn_text_done = '';
15079 var btn_text_cancel = '';
15081 if (this.btn_text_show) {
15082 btn_text_select = 'Select';
15083 btn_text_done = 'Done';
15084 btn_text_cancel = 'Cancel';
15089 cls : 'tickable-buttons',
15094 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15095 //html : this.triggerText
15096 html: btn_text_select
15102 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15104 html: btn_text_done
15110 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15112 html: btn_text_cancel
15118 buttons.cn.unshift({
15120 cls: 'roo-select2-search-field-input'
15126 Roo.each(buttons.cn, function(c){
15128 c.cls += ' btn-' + _this.size;
15131 if (_this.disabled) {
15138 style : 'display: contents',
15143 cls: 'form-hidden-field'
15147 cls: 'roo-select2-choices',
15151 cls: 'roo-select2-search-field',
15162 cls: 'roo-select2-container input-group roo-select2-container-multi',
15168 // cls: 'typeahead typeahead-long dropdown-menu',
15169 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15174 if(this.hasFeedback && !this.allowBlank){
15178 cls: 'glyphicon form-control-feedback'
15181 combobox.cn.push(feedback);
15188 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15189 tooltip : 'This field is required'
15191 if (Roo.bootstrap.version == 4) {
15194 style : 'display:none'
15197 if (align ==='left' && this.fieldLabel.length) {
15199 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15206 cls : 'control-label col-form-label',
15207 html : this.fieldLabel
15219 var labelCfg = cfg.cn[1];
15220 var contentCfg = cfg.cn[2];
15223 if(this.indicatorpos == 'right'){
15229 cls : 'control-label col-form-label',
15233 html : this.fieldLabel
15249 labelCfg = cfg.cn[0];
15250 contentCfg = cfg.cn[1];
15254 if(this.labelWidth > 12){
15255 labelCfg.style = "width: " + this.labelWidth + 'px';
15258 if(this.labelWidth < 13 && this.labelmd == 0){
15259 this.labelmd = this.labelWidth;
15262 if(this.labellg > 0){
15263 labelCfg.cls += ' col-lg-' + this.labellg;
15264 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15267 if(this.labelmd > 0){
15268 labelCfg.cls += ' col-md-' + this.labelmd;
15269 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15272 if(this.labelsm > 0){
15273 labelCfg.cls += ' col-sm-' + this.labelsm;
15274 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15277 if(this.labelxs > 0){
15278 labelCfg.cls += ' col-xs-' + this.labelxs;
15279 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15283 } else if ( this.fieldLabel.length) {
15284 // Roo.log(" label");
15289 //cls : 'input-group-addon',
15290 html : this.fieldLabel
15295 if(this.indicatorpos == 'right'){
15299 //cls : 'input-group-addon',
15300 html : this.fieldLabel
15310 // Roo.log(" no label && no align");
15317 ['xs','sm','md','lg'].map(function(size){
15318 if (settings[size]) {
15319 cfg.cls += ' col-' + size + '-' + settings[size];
15327 _initEventsCalled : false,
15330 initEvents: function()
15332 if (this._initEventsCalled) { // as we call render... prevent looping...
15335 this._initEventsCalled = true;
15338 throw "can not find store for combo";
15341 this.indicator = this.indicatorEl();
15343 this.store = Roo.factory(this.store, Roo.data);
15344 this.store.parent = this;
15346 // if we are building from html. then this element is so complex, that we can not really
15347 // use the rendered HTML.
15348 // so we have to trash and replace the previous code.
15349 if (Roo.XComponent.build_from_html) {
15350 // remove this element....
15351 var e = this.el.dom, k=0;
15352 while (e ) { e = e.previousSibling; ++k;}
15357 this.rendered = false;
15359 this.render(this.parent().getChildContainer(true), k);
15362 if(Roo.isIOS && this.useNativeIOS){
15363 this.initIOSView();
15371 if(Roo.isTouch && this.mobileTouchView){
15372 this.initTouchView();
15377 this.initTickableEvents();
15381 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15383 if(this.hiddenName){
15385 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15387 this.hiddenField.dom.value =
15388 this.hiddenValue !== undefined ? this.hiddenValue :
15389 this.value !== undefined ? this.value : '';
15391 // prevent input submission
15392 this.el.dom.removeAttribute('name');
15393 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15398 // this.el.dom.setAttribute('autocomplete', 'off');
15401 var cls = 'x-combo-list';
15403 //this.list = new Roo.Layer({
15404 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15410 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15411 _this.list.setWidth(lw);
15414 this.list.on('mouseover', this.onViewOver, this);
15415 this.list.on('mousemove', this.onViewMove, this);
15416 this.list.on('scroll', this.onViewScroll, this);
15419 this.list.swallowEvent('mousewheel');
15420 this.assetHeight = 0;
15423 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15424 this.assetHeight += this.header.getHeight();
15427 this.innerList = this.list.createChild({cls:cls+'-inner'});
15428 this.innerList.on('mouseover', this.onViewOver, this);
15429 this.innerList.on('mousemove', this.onViewMove, this);
15430 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15432 if(this.allowBlank && !this.pageSize && !this.disableClear){
15433 this.footer = this.list.createChild({cls:cls+'-ft'});
15434 this.pageTb = new Roo.Toolbar(this.footer);
15438 this.footer = this.list.createChild({cls:cls+'-ft'});
15439 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15440 {pageSize: this.pageSize});
15444 if (this.pageTb && this.allowBlank && !this.disableClear) {
15446 this.pageTb.add(new Roo.Toolbar.Fill(), {
15447 cls: 'x-btn-icon x-btn-clear',
15449 handler: function()
15452 _this.clearValue();
15453 _this.onSelect(false, -1);
15458 this.assetHeight += this.footer.getHeight();
15463 this.tpl = Roo.bootstrap.version == 4 ?
15464 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15465 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15468 this.view = new Roo.View(this.list, this.tpl, {
15469 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15471 //this.view.wrapEl.setDisplayed(false);
15472 this.view.on('click', this.onViewClick, this);
15475 this.store.on('beforeload', this.onBeforeLoad, this);
15476 this.store.on('load', this.onLoad, this);
15477 this.store.on('loadexception', this.onLoadException, this);
15479 if(this.resizable){
15480 this.resizer = new Roo.Resizable(this.list, {
15481 pinned:true, handles:'se'
15483 this.resizer.on('resize', function(r, w, h){
15484 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15485 this.listWidth = w;
15486 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15487 this.restrictHeight();
15489 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15492 if(!this.editable){
15493 this.editable = true;
15494 this.setEditable(false);
15499 if (typeof(this.events.add.listeners) != 'undefined') {
15501 this.addicon = this.wrap.createChild(
15502 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15504 this.addicon.on('click', function(e) {
15505 this.fireEvent('add', this);
15508 if (typeof(this.events.edit.listeners) != 'undefined') {
15510 this.editicon = this.wrap.createChild(
15511 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15512 if (this.addicon) {
15513 this.editicon.setStyle('margin-left', '40px');
15515 this.editicon.on('click', function(e) {
15517 // we fire even if inothing is selected..
15518 this.fireEvent('edit', this, this.lastData );
15524 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15525 "up" : function(e){
15526 this.inKeyMode = true;
15530 "down" : function(e){
15531 if(!this.isExpanded()){
15532 this.onTriggerClick();
15534 this.inKeyMode = true;
15539 "enter" : function(e){
15540 // this.onViewClick();
15544 if(this.fireEvent("specialkey", this, e)){
15545 this.onViewClick(false);
15551 "esc" : function(e){
15555 "tab" : function(e){
15558 if(this.fireEvent("specialkey", this, e)){
15559 this.onViewClick(false);
15567 doRelay : function(foo, bar, hname){
15568 if(hname == 'down' || this.scope.isExpanded()){
15569 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15578 this.queryDelay = Math.max(this.queryDelay || 10,
15579 this.mode == 'local' ? 10 : 250);
15582 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15584 if(this.typeAhead){
15585 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15587 if(this.editable !== false){
15588 this.inputEl().on("keyup", this.onKeyUp, this);
15590 if(this.forceSelection){
15591 this.inputEl().on('blur', this.doForce, this);
15595 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15596 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15600 initTickableEvents: function()
15604 if(this.hiddenName){
15606 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15608 this.hiddenField.dom.value =
15609 this.hiddenValue !== undefined ? this.hiddenValue :
15610 this.value !== undefined ? this.value : '';
15612 // prevent input submission
15613 this.el.dom.removeAttribute('name');
15614 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15619 // this.list = this.el.select('ul.dropdown-menu',true).first();
15621 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15622 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15623 if(this.triggerList){
15624 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15627 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15628 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15630 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15631 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15633 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15634 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15636 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15637 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15638 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15641 this.cancelBtn.hide();
15646 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15647 _this.list.setWidth(lw);
15650 this.list.on('mouseover', this.onViewOver, this);
15651 this.list.on('mousemove', this.onViewMove, this);
15653 this.list.on('scroll', this.onViewScroll, this);
15656 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15657 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15660 this.view = new Roo.View(this.list, this.tpl, {
15665 selectedClass: this.selectedClass
15668 //this.view.wrapEl.setDisplayed(false);
15669 this.view.on('click', this.onViewClick, this);
15673 this.store.on('beforeload', this.onBeforeLoad, this);
15674 this.store.on('load', this.onLoad, this);
15675 this.store.on('loadexception', this.onLoadException, this);
15678 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15679 "up" : function(e){
15680 this.inKeyMode = true;
15684 "down" : function(e){
15685 this.inKeyMode = true;
15689 "enter" : function(e){
15690 if(this.fireEvent("specialkey", this, e)){
15691 this.onViewClick(false);
15697 "esc" : function(e){
15698 this.onTickableFooterButtonClick(e, false, false);
15701 "tab" : function(e){
15702 this.fireEvent("specialkey", this, e);
15704 this.onTickableFooterButtonClick(e, false, false);
15711 doRelay : function(e, fn, key){
15712 if(this.scope.isExpanded()){
15713 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15722 this.queryDelay = Math.max(this.queryDelay || 10,
15723 this.mode == 'local' ? 10 : 250);
15726 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15728 if(this.typeAhead){
15729 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15732 if(this.editable !== false){
15733 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15736 this.indicator = this.indicatorEl();
15738 if(this.indicator){
15739 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15740 this.indicator.hide();
15745 onDestroy : function(){
15747 this.view.setStore(null);
15748 this.view.el.removeAllListeners();
15749 this.view.el.remove();
15750 this.view.purgeListeners();
15753 this.list.dom.innerHTML = '';
15757 this.store.un('beforeload', this.onBeforeLoad, this);
15758 this.store.un('load', this.onLoad, this);
15759 this.store.un('loadexception', this.onLoadException, this);
15761 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15765 fireKey : function(e){
15766 if(e.isNavKeyPress() && !this.list.isVisible()){
15767 this.fireEvent("specialkey", this, e);
15772 onResize: function(w, h){
15773 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15775 // if(typeof w != 'number'){
15776 // // we do not handle it!?!?
15779 // var tw = this.trigger.getWidth();
15780 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15781 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15783 // this.inputEl().setWidth( this.adjustWidth('input', x));
15785 // //this.trigger.setStyle('left', x+'px');
15787 // if(this.list && this.listWidth === undefined){
15788 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15789 // this.list.setWidth(lw);
15790 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15798 * Allow or prevent the user from directly editing the field text. If false is passed,
15799 * the user will only be able to select from the items defined in the dropdown list. This method
15800 * is the runtime equivalent of setting the 'editable' config option at config time.
15801 * @param {Boolean} value True to allow the user to directly edit the field text
15803 setEditable : function(value){
15804 if(value == this.editable){
15807 this.editable = value;
15809 this.inputEl().dom.setAttribute('readOnly', true);
15810 this.inputEl().on('mousedown', this.onTriggerClick, this);
15811 this.inputEl().addClass('x-combo-noedit');
15813 this.inputEl().dom.setAttribute('readOnly', false);
15814 this.inputEl().un('mousedown', this.onTriggerClick, this);
15815 this.inputEl().removeClass('x-combo-noedit');
15821 onBeforeLoad : function(combo,opts){
15822 if(!this.hasFocus){
15826 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15828 this.restrictHeight();
15829 this.selectedIndex = -1;
15833 onLoad : function(){
15835 this.hasQuery = false;
15837 if(!this.hasFocus){
15841 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15842 this.loading.hide();
15845 if(this.store.getCount() > 0){
15848 this.restrictHeight();
15849 if(this.lastQuery == this.allQuery){
15850 if(this.editable && !this.tickable){
15851 this.inputEl().dom.select();
15855 !this.selectByValue(this.value, true) &&
15858 !this.store.lastOptions ||
15859 typeof(this.store.lastOptions.add) == 'undefined' ||
15860 this.store.lastOptions.add != true
15863 this.select(0, true);
15866 if(this.autoFocus){
15869 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15870 this.taTask.delay(this.typeAheadDelay);
15874 this.onEmptyResults();
15880 onLoadException : function()
15882 this.hasQuery = false;
15884 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15885 this.loading.hide();
15888 if(this.tickable && this.editable){
15893 // only causes errors at present
15894 //Roo.log(this.store.reader.jsonData);
15895 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15897 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15903 onTypeAhead : function(){
15904 if(this.store.getCount() > 0){
15905 var r = this.store.getAt(0);
15906 var newValue = r.data[this.displayField];
15907 var len = newValue.length;
15908 var selStart = this.getRawValue().length;
15910 if(selStart != len){
15911 this.setRawValue(newValue);
15912 this.selectText(selStart, newValue.length);
15918 onSelect : function(record, index){
15920 if(this.fireEvent('beforeselect', this, record, index) !== false){
15922 this.setFromData(index > -1 ? record.data : false);
15925 this.fireEvent('select', this, record, index);
15930 * Returns the currently selected field value or empty string if no value is set.
15931 * @return {String} value The selected value
15933 getValue : function()
15935 if(Roo.isIOS && this.useNativeIOS){
15936 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15940 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15943 if(this.valueField){
15944 return typeof this.value != 'undefined' ? this.value : '';
15946 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15950 getRawValue : function()
15952 if(Roo.isIOS && this.useNativeIOS){
15953 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15956 var v = this.inputEl().getValue();
15962 * Clears any text/value currently set in the field
15964 clearValue : function(){
15966 if(this.hiddenField){
15967 this.hiddenField.dom.value = '';
15970 this.setRawValue('');
15971 this.lastSelectionText = '';
15972 this.lastData = false;
15974 var close = this.closeTriggerEl();
15985 * Sets the specified value into the field. If the value finds a match, the corresponding record text
15986 * will be displayed in the field. If the value does not match the data value of an existing item,
15987 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15988 * Otherwise the field will be blank (although the value will still be set).
15989 * @param {String} value The value to match
15991 setValue : function(v)
15993 if(Roo.isIOS && this.useNativeIOS){
15994 this.setIOSValue(v);
16004 if(this.valueField){
16005 var r = this.findRecord(this.valueField, v);
16007 text = r.data[this.displayField];
16008 }else if(this.valueNotFoundText !== undefined){
16009 text = this.valueNotFoundText;
16012 this.lastSelectionText = text;
16013 if(this.hiddenField){
16014 this.hiddenField.dom.value = v;
16016 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16019 var close = this.closeTriggerEl();
16022 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16028 * @property {Object} the last set data for the element
16033 * Sets the value of the field based on a object which is related to the record format for the store.
16034 * @param {Object} value the value to set as. or false on reset?
16036 setFromData : function(o){
16043 var dv = ''; // display value
16044 var vv = ''; // value value..
16046 if (this.displayField) {
16047 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16049 // this is an error condition!!!
16050 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16053 if(this.valueField){
16054 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16057 var close = this.closeTriggerEl();
16060 if(dv.length || vv * 1 > 0){
16062 this.blockFocus=true;
16068 if(this.hiddenField){
16069 this.hiddenField.dom.value = vv;
16071 this.lastSelectionText = dv;
16072 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16076 // no hidden field.. - we store the value in 'value', but still display
16077 // display field!!!!
16078 this.lastSelectionText = dv;
16079 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16086 reset : function(){
16087 // overridden so that last data is reset..
16094 this.setValue(this.originalValue);
16095 //this.clearInvalid();
16096 this.lastData = false;
16098 this.view.clearSelections();
16104 findRecord : function(prop, value){
16106 if(this.store.getCount() > 0){
16107 this.store.each(function(r){
16108 if(r.data[prop] == value){
16118 getName: function()
16120 // returns hidden if it's set..
16121 if (!this.rendered) {return ''};
16122 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16126 onViewMove : function(e, t){
16127 this.inKeyMode = false;
16131 onViewOver : function(e, t){
16132 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16135 var item = this.view.findItemFromChild(t);
16138 var index = this.view.indexOf(item);
16139 this.select(index, false);
16144 onViewClick : function(view, doFocus, el, e)
16146 var index = this.view.getSelectedIndexes()[0];
16148 var r = this.store.getAt(index);
16152 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16159 Roo.each(this.tickItems, function(v,k){
16161 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16163 _this.tickItems.splice(k, 1);
16165 if(typeof(e) == 'undefined' && view == false){
16166 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16178 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16179 this.tickItems.push(r.data);
16182 if(typeof(e) == 'undefined' && view == false){
16183 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16190 this.onSelect(r, index);
16192 if(doFocus !== false && !this.blockFocus){
16193 this.inputEl().focus();
16198 restrictHeight : function(){
16199 //this.innerList.dom.style.height = '';
16200 //var inner = this.innerList.dom;
16201 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16202 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16203 //this.list.beginUpdate();
16204 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16205 this.list.alignTo(this.inputEl(), this.listAlign);
16206 this.list.alignTo(this.inputEl(), this.listAlign);
16207 //this.list.endUpdate();
16211 onEmptyResults : function(){
16213 if(this.tickable && this.editable){
16214 this.hasFocus = false;
16215 this.restrictHeight();
16223 * Returns true if the dropdown list is expanded, else false.
16225 isExpanded : function(){
16226 return this.list.isVisible();
16230 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16231 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16232 * @param {String} value The data value of the item to select
16233 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16234 * selected item if it is not currently in view (defaults to true)
16235 * @return {Boolean} True if the value matched an item in the list, else false
16237 selectByValue : function(v, scrollIntoView){
16238 if(v !== undefined && v !== null){
16239 var r = this.findRecord(this.valueField || this.displayField, v);
16241 this.select(this.store.indexOf(r), scrollIntoView);
16249 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16250 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16251 * @param {Number} index The zero-based index of the list item to select
16252 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16253 * selected item if it is not currently in view (defaults to true)
16255 select : function(index, scrollIntoView){
16256 this.selectedIndex = index;
16257 this.view.select(index);
16258 if(scrollIntoView !== false){
16259 var el = this.view.getNode(index);
16261 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16264 this.list.scrollChildIntoView(el, false);
16270 selectNext : function(){
16271 var ct = this.store.getCount();
16273 if(this.selectedIndex == -1){
16275 }else if(this.selectedIndex < ct-1){
16276 this.select(this.selectedIndex+1);
16282 selectPrev : function(){
16283 var ct = this.store.getCount();
16285 if(this.selectedIndex == -1){
16287 }else if(this.selectedIndex != 0){
16288 this.select(this.selectedIndex-1);
16294 onKeyUp : function(e){
16295 if(this.editable !== false && !e.isSpecialKey()){
16296 this.lastKey = e.getKey();
16297 this.dqTask.delay(this.queryDelay);
16302 validateBlur : function(){
16303 return !this.list || !this.list.isVisible();
16307 initQuery : function(){
16309 var v = this.getRawValue();
16311 if(this.tickable && this.editable){
16312 v = this.tickableInputEl().getValue();
16319 doForce : function(){
16320 if(this.inputEl().dom.value.length > 0){
16321 this.inputEl().dom.value =
16322 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16328 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16329 * query allowing the query action to be canceled if needed.
16330 * @param {String} query The SQL query to execute
16331 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16332 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16333 * saved in the current store (defaults to false)
16335 doQuery : function(q, forceAll){
16337 if(q === undefined || q === null){
16342 forceAll: forceAll,
16346 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16351 forceAll = qe.forceAll;
16352 if(forceAll === true || (q.length >= this.minChars)){
16354 this.hasQuery = true;
16356 if(this.lastQuery != q || this.alwaysQuery){
16357 this.lastQuery = q;
16358 if(this.mode == 'local'){
16359 this.selectedIndex = -1;
16361 this.store.clearFilter();
16364 if(this.specialFilter){
16365 this.fireEvent('specialfilter', this);
16370 this.store.filter(this.displayField, q);
16373 this.store.fireEvent("datachanged", this.store);
16380 this.store.baseParams[this.queryParam] = q;
16382 var options = {params : this.getParams(q)};
16385 options.add = true;
16386 options.params.start = this.page * this.pageSize;
16389 this.store.load(options);
16392 * this code will make the page width larger, at the beginning, the list not align correctly,
16393 * we should expand the list on onLoad
16394 * so command out it
16399 this.selectedIndex = -1;
16404 this.loadNext = false;
16408 getParams : function(q){
16410 //p[this.queryParam] = q;
16414 p.limit = this.pageSize;
16420 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16422 collapse : function(){
16423 if(!this.isExpanded()){
16429 this.hasFocus = false;
16433 this.cancelBtn.hide();
16434 this.trigger.show();
16437 this.tickableInputEl().dom.value = '';
16438 this.tickableInputEl().blur();
16443 Roo.get(document).un('mousedown', this.collapseIf, this);
16444 Roo.get(document).un('mousewheel', this.collapseIf, this);
16445 if (!this.editable) {
16446 Roo.get(document).un('keydown', this.listKeyPress, this);
16448 this.fireEvent('collapse', this);
16454 collapseIf : function(e){
16455 var in_combo = e.within(this.el);
16456 var in_list = e.within(this.list);
16457 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16459 if (in_combo || in_list || is_list) {
16460 //e.stopPropagation();
16465 this.onTickableFooterButtonClick(e, false, false);
16473 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16475 expand : function(){
16477 if(this.isExpanded() || !this.hasFocus){
16481 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16482 this.list.setWidth(lw);
16488 this.restrictHeight();
16492 this.tickItems = Roo.apply([], this.item);
16495 this.cancelBtn.show();
16496 this.trigger.hide();
16499 this.tickableInputEl().focus();
16504 Roo.get(document).on('mousedown', this.collapseIf, this);
16505 Roo.get(document).on('mousewheel', this.collapseIf, this);
16506 if (!this.editable) {
16507 Roo.get(document).on('keydown', this.listKeyPress, this);
16510 this.fireEvent('expand', this);
16514 // Implements the default empty TriggerField.onTriggerClick function
16515 onTriggerClick : function(e)
16517 Roo.log('trigger click');
16519 if(this.disabled || !this.triggerList){
16524 this.loadNext = false;
16526 if(this.isExpanded()){
16528 if (!this.blockFocus) {
16529 this.inputEl().focus();
16533 this.hasFocus = true;
16534 if(this.triggerAction == 'all') {
16535 this.doQuery(this.allQuery, true);
16537 this.doQuery(this.getRawValue());
16539 if (!this.blockFocus) {
16540 this.inputEl().focus();
16545 onTickableTriggerClick : function(e)
16552 this.loadNext = false;
16553 this.hasFocus = true;
16555 if(this.triggerAction == 'all') {
16556 this.doQuery(this.allQuery, true);
16558 this.doQuery(this.getRawValue());
16562 onSearchFieldClick : function(e)
16564 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16565 this.onTickableFooterButtonClick(e, false, false);
16569 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16574 this.loadNext = false;
16575 this.hasFocus = true;
16577 if(this.triggerAction == 'all') {
16578 this.doQuery(this.allQuery, true);
16580 this.doQuery(this.getRawValue());
16584 listKeyPress : function(e)
16586 //Roo.log('listkeypress');
16587 // scroll to first matching element based on key pres..
16588 if (e.isSpecialKey()) {
16591 var k = String.fromCharCode(e.getKey()).toUpperCase();
16594 var csel = this.view.getSelectedNodes();
16595 var cselitem = false;
16597 var ix = this.view.indexOf(csel[0]);
16598 cselitem = this.store.getAt(ix);
16599 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16605 this.store.each(function(v) {
16607 // start at existing selection.
16608 if (cselitem.id == v.id) {
16614 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16615 match = this.store.indexOf(v);
16621 if (match === false) {
16622 return true; // no more action?
16625 this.view.select(match);
16626 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16627 sn.scrollIntoView(sn.dom.parentNode, false);
16630 onViewScroll : function(e, t){
16632 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){
16636 this.hasQuery = true;
16638 this.loading = this.list.select('.loading', true).first();
16640 if(this.loading === null){
16641 this.list.createChild({
16643 cls: 'loading roo-select2-more-results roo-select2-active',
16644 html: 'Loading more results...'
16647 this.loading = this.list.select('.loading', true).first();
16649 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16651 this.loading.hide();
16654 this.loading.show();
16659 this.loadNext = true;
16661 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16666 addItem : function(o)
16668 var dv = ''; // display value
16670 if (this.displayField) {
16671 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16673 // this is an error condition!!!
16674 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16681 var choice = this.choices.createChild({
16683 cls: 'roo-select2-search-choice',
16692 cls: 'roo-select2-search-choice-close fa fa-times',
16697 }, this.searchField);
16699 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16701 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16709 this.inputEl().dom.value = '';
16714 onRemoveItem : function(e, _self, o)
16716 e.preventDefault();
16718 this.lastItem = Roo.apply([], this.item);
16720 var index = this.item.indexOf(o.data) * 1;
16723 Roo.log('not this item?!');
16727 this.item.splice(index, 1);
16732 this.fireEvent('remove', this, e);
16738 syncValue : function()
16740 if(!this.item.length){
16747 Roo.each(this.item, function(i){
16748 if(_this.valueField){
16749 value.push(i[_this.valueField]);
16756 this.value = value.join(',');
16758 if(this.hiddenField){
16759 this.hiddenField.dom.value = this.value;
16762 this.store.fireEvent("datachanged", this.store);
16767 clearItem : function()
16769 if(!this.multiple){
16775 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16783 if(this.tickable && !Roo.isTouch){
16784 this.view.refresh();
16788 inputEl: function ()
16790 if(Roo.isIOS && this.useNativeIOS){
16791 return this.el.select('select.roo-ios-select', true).first();
16794 if(Roo.isTouch && this.mobileTouchView){
16795 return this.el.select('input.form-control',true).first();
16799 return this.searchField;
16802 return this.el.select('input.form-control',true).first();
16805 onTickableFooterButtonClick : function(e, btn, el)
16807 e.preventDefault();
16809 this.lastItem = Roo.apply([], this.item);
16811 if(btn && btn.name == 'cancel'){
16812 this.tickItems = Roo.apply([], this.item);
16821 Roo.each(this.tickItems, function(o){
16829 validate : function()
16831 if(this.getVisibilityEl().hasClass('hidden')){
16835 var v = this.getRawValue();
16838 v = this.getValue();
16841 if(this.disabled || this.allowBlank || v.length){
16846 this.markInvalid();
16850 tickableInputEl : function()
16852 if(!this.tickable || !this.editable){
16853 return this.inputEl();
16856 return this.inputEl().select('.roo-select2-search-field-input', true).first();
16860 getAutoCreateTouchView : function()
16865 cls: 'form-group' //input-group
16871 type : this.inputType,
16872 cls : 'form-control x-combo-noedit',
16873 autocomplete: 'new-password',
16874 placeholder : this.placeholder || '',
16879 input.name = this.name;
16883 input.cls += ' input-' + this.size;
16886 if (this.disabled) {
16887 input.disabled = true;
16898 inputblock.cls += ' input-group';
16900 inputblock.cn.unshift({
16902 cls : 'input-group-addon input-group-prepend input-group-text',
16907 if(this.removable && !this.multiple){
16908 inputblock.cls += ' roo-removable';
16910 inputblock.cn.push({
16913 cls : 'roo-combo-removable-btn close'
16917 if(this.hasFeedback && !this.allowBlank){
16919 inputblock.cls += ' has-feedback';
16921 inputblock.cn.push({
16923 cls: 'glyphicon form-control-feedback'
16930 inputblock.cls += (this.before) ? '' : ' input-group';
16932 inputblock.cn.push({
16934 cls : 'input-group-addon input-group-append input-group-text',
16940 var ibwrap = inputblock;
16945 cls: 'roo-select2-choices',
16949 cls: 'roo-select2-search-field',
16962 cls: 'roo-select2-container input-group roo-touchview-combobox ',
16967 cls: 'form-hidden-field'
16973 if(!this.multiple && this.showToggleBtn){
16979 if (this.caret != false) {
16982 cls: 'fa fa-' + this.caret
16989 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16991 Roo.bootstrap.version == 3 ? caret : '',
16994 cls: 'combobox-clear',
17008 combobox.cls += ' roo-select2-container-multi';
17011 var align = this.labelAlign || this.parentLabelAlign();
17013 if (align ==='left' && this.fieldLabel.length) {
17018 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17019 tooltip : 'This field is required'
17023 cls : 'control-label col-form-label',
17024 html : this.fieldLabel
17035 var labelCfg = cfg.cn[1];
17036 var contentCfg = cfg.cn[2];
17039 if(this.indicatorpos == 'right'){
17044 cls : 'control-label col-form-label',
17048 html : this.fieldLabel
17052 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17053 tooltip : 'This field is required'
17066 labelCfg = cfg.cn[0];
17067 contentCfg = cfg.cn[1];
17072 if(this.labelWidth > 12){
17073 labelCfg.style = "width: " + this.labelWidth + 'px';
17076 if(this.labelWidth < 13 && this.labelmd == 0){
17077 this.labelmd = this.labelWidth;
17080 if(this.labellg > 0){
17081 labelCfg.cls += ' col-lg-' + this.labellg;
17082 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17085 if(this.labelmd > 0){
17086 labelCfg.cls += ' col-md-' + this.labelmd;
17087 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17090 if(this.labelsm > 0){
17091 labelCfg.cls += ' col-sm-' + this.labelsm;
17092 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17095 if(this.labelxs > 0){
17096 labelCfg.cls += ' col-xs-' + this.labelxs;
17097 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17101 } else if ( this.fieldLabel.length) {
17105 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17106 tooltip : 'This field is required'
17110 cls : 'control-label',
17111 html : this.fieldLabel
17122 if(this.indicatorpos == 'right'){
17126 cls : 'control-label',
17127 html : this.fieldLabel,
17131 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17132 tooltip : 'This field is required'
17149 var settings = this;
17151 ['xs','sm','md','lg'].map(function(size){
17152 if (settings[size]) {
17153 cfg.cls += ' col-' + size + '-' + settings[size];
17160 initTouchView : function()
17162 this.renderTouchView();
17164 this.touchViewEl.on('scroll', function(){
17165 this.el.dom.scrollTop = 0;
17168 this.originalValue = this.getValue();
17170 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17172 this.inputEl().on("click", this.showTouchView, this);
17173 if (this.triggerEl) {
17174 this.triggerEl.on("click", this.showTouchView, this);
17178 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17179 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17181 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17183 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17184 this.store.on('load', this.onTouchViewLoad, this);
17185 this.store.on('loadexception', this.onTouchViewLoadException, this);
17187 if(this.hiddenName){
17189 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17191 this.hiddenField.dom.value =
17192 this.hiddenValue !== undefined ? this.hiddenValue :
17193 this.value !== undefined ? this.value : '';
17195 this.el.dom.removeAttribute('name');
17196 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17200 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17201 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17204 if(this.removable && !this.multiple){
17205 var close = this.closeTriggerEl();
17207 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17208 close.on('click', this.removeBtnClick, this, close);
17212 * fix the bug in Safari iOS8
17214 this.inputEl().on("focus", function(e){
17215 document.activeElement.blur();
17218 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17225 renderTouchView : function()
17227 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17228 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17230 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17231 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17233 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17234 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17235 this.touchViewBodyEl.setStyle('overflow', 'auto');
17237 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17238 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17240 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17241 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17245 showTouchView : function()
17251 this.touchViewHeaderEl.hide();
17253 if(this.modalTitle.length){
17254 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17255 this.touchViewHeaderEl.show();
17258 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17259 this.touchViewEl.show();
17261 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17263 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17264 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17266 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17268 if(this.modalTitle.length){
17269 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17272 this.touchViewBodyEl.setHeight(bodyHeight);
17276 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17278 this.touchViewEl.addClass('in');
17281 if(this._touchViewMask){
17282 Roo.get(document.body).addClass("x-body-masked");
17283 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17284 this._touchViewMask.setStyle('z-index', 10000);
17285 this._touchViewMask.addClass('show');
17288 this.doTouchViewQuery();
17292 hideTouchView : function()
17294 this.touchViewEl.removeClass('in');
17298 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17300 this.touchViewEl.setStyle('display', 'none');
17303 if(this._touchViewMask){
17304 this._touchViewMask.removeClass('show');
17305 Roo.get(document.body).removeClass("x-body-masked");
17309 setTouchViewValue : function()
17316 Roo.each(this.tickItems, function(o){
17321 this.hideTouchView();
17324 doTouchViewQuery : function()
17333 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17337 if(!this.alwaysQuery || this.mode == 'local'){
17338 this.onTouchViewLoad();
17345 onTouchViewBeforeLoad : function(combo,opts)
17351 onTouchViewLoad : function()
17353 if(this.store.getCount() < 1){
17354 this.onTouchViewEmptyResults();
17358 this.clearTouchView();
17360 var rawValue = this.getRawValue();
17362 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17364 this.tickItems = [];
17366 this.store.data.each(function(d, rowIndex){
17367 var row = this.touchViewListGroup.createChild(template);
17369 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17370 row.addClass(d.data.cls);
17373 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17376 html : d.data[this.displayField]
17379 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17380 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17383 row.removeClass('selected');
17384 if(!this.multiple && this.valueField &&
17385 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17388 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17389 row.addClass('selected');
17392 if(this.multiple && this.valueField &&
17393 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17397 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17398 this.tickItems.push(d.data);
17401 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17405 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17407 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17409 if(this.modalTitle.length){
17410 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17413 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17415 if(this.mobile_restrict_height && listHeight < bodyHeight){
17416 this.touchViewBodyEl.setHeight(listHeight);
17421 if(firstChecked && listHeight > bodyHeight){
17422 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17427 onTouchViewLoadException : function()
17429 this.hideTouchView();
17432 onTouchViewEmptyResults : function()
17434 this.clearTouchView();
17436 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17438 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17442 clearTouchView : function()
17444 this.touchViewListGroup.dom.innerHTML = '';
17447 onTouchViewClick : function(e, el, o)
17449 e.preventDefault();
17452 var rowIndex = o.rowIndex;
17454 var r = this.store.getAt(rowIndex);
17456 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17458 if(!this.multiple){
17459 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17460 c.dom.removeAttribute('checked');
17463 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17465 this.setFromData(r.data);
17467 var close = this.closeTriggerEl();
17473 this.hideTouchView();
17475 this.fireEvent('select', this, r, rowIndex);
17480 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17481 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17482 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17486 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17487 this.addItem(r.data);
17488 this.tickItems.push(r.data);
17492 getAutoCreateNativeIOS : function()
17495 cls: 'form-group' //input-group,
17500 cls : 'roo-ios-select'
17504 combobox.name = this.name;
17507 if (this.disabled) {
17508 combobox.disabled = true;
17511 var settings = this;
17513 ['xs','sm','md','lg'].map(function(size){
17514 if (settings[size]) {
17515 cfg.cls += ' col-' + size + '-' + settings[size];
17525 initIOSView : function()
17527 this.store.on('load', this.onIOSViewLoad, this);
17532 onIOSViewLoad : function()
17534 if(this.store.getCount() < 1){
17538 this.clearIOSView();
17540 if(this.allowBlank) {
17542 var default_text = '-- SELECT --';
17544 if(this.placeholder.length){
17545 default_text = this.placeholder;
17548 if(this.emptyTitle.length){
17549 default_text += ' - ' + this.emptyTitle + ' -';
17552 var opt = this.inputEl().createChild({
17555 html : default_text
17559 o[this.valueField] = 0;
17560 o[this.displayField] = default_text;
17562 this.ios_options.push({
17569 this.store.data.each(function(d, rowIndex){
17573 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17574 html = d.data[this.displayField];
17579 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17580 value = d.data[this.valueField];
17589 if(this.value == d.data[this.valueField]){
17590 option['selected'] = true;
17593 var opt = this.inputEl().createChild(option);
17595 this.ios_options.push({
17602 this.inputEl().on('change', function(){
17603 this.fireEvent('select', this);
17608 clearIOSView: function()
17610 this.inputEl().dom.innerHTML = '';
17612 this.ios_options = [];
17615 setIOSValue: function(v)
17619 if(!this.ios_options){
17623 Roo.each(this.ios_options, function(opts){
17625 opts.el.dom.removeAttribute('selected');
17627 if(opts.data[this.valueField] != v){
17631 opts.el.dom.setAttribute('selected', true);
17637 * @cfg {Boolean} grow
17641 * @cfg {Number} growMin
17645 * @cfg {Number} growMax
17654 Roo.apply(Roo.bootstrap.ComboBox, {
17658 cls: 'modal-header',
17680 cls: 'list-group-item',
17684 cls: 'roo-combobox-list-group-item-value'
17688 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17702 listItemCheckbox : {
17704 cls: 'list-group-item',
17708 cls: 'roo-combobox-list-group-item-value'
17712 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17728 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17733 cls: 'modal-footer',
17741 cls: 'col-xs-6 text-left',
17744 cls: 'btn btn-danger roo-touch-view-cancel',
17750 cls: 'col-xs-6 text-right',
17753 cls: 'btn btn-success roo-touch-view-ok',
17764 Roo.apply(Roo.bootstrap.ComboBox, {
17766 touchViewTemplate : {
17768 cls: 'modal fade roo-combobox-touch-view',
17772 cls: 'modal-dialog',
17773 style : 'position:fixed', // we have to fix position....
17777 cls: 'modal-content',
17779 Roo.bootstrap.ComboBox.header,
17780 Roo.bootstrap.ComboBox.body,
17781 Roo.bootstrap.ComboBox.footer
17790 * Ext JS Library 1.1.1
17791 * Copyright(c) 2006-2007, Ext JS, LLC.
17793 * Originally Released Under LGPL - original licence link has changed is not relivant.
17796 * <script type="text/javascript">
17801 * @extends Roo.util.Observable
17802 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17803 * This class also supports single and multi selection modes. <br>
17804 * Create a data model bound view:
17806 var store = new Roo.data.Store(...);
17808 var view = new Roo.View({
17810 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
17812 singleSelect: true,
17813 selectedClass: "ydataview-selected",
17817 // listen for node click?
17818 view.on("click", function(vw, index, node, e){
17819 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17823 dataModel.load("foobar.xml");
17825 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17827 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17828 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17830 * Note: old style constructor is still suported (container, template, config)
17833 * Create a new View
17834 * @param {Object} config The config object
17837 Roo.View = function(config, depreciated_tpl, depreciated_config){
17839 this.parent = false;
17841 if (typeof(depreciated_tpl) == 'undefined') {
17842 // new way.. - universal constructor.
17843 Roo.apply(this, config);
17844 this.el = Roo.get(this.el);
17847 this.el = Roo.get(config);
17848 this.tpl = depreciated_tpl;
17849 Roo.apply(this, depreciated_config);
17851 this.wrapEl = this.el.wrap().wrap();
17852 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17855 if(typeof(this.tpl) == "string"){
17856 this.tpl = new Roo.Template(this.tpl);
17858 // support xtype ctors..
17859 this.tpl = new Roo.factory(this.tpl, Roo);
17863 this.tpl.compile();
17868 * @event beforeclick
17869 * Fires before a click is processed. Returns false to cancel the default action.
17870 * @param {Roo.View} this
17871 * @param {Number} index The index of the target node
17872 * @param {HTMLElement} node The target node
17873 * @param {Roo.EventObject} e The raw event object
17875 "beforeclick" : true,
17878 * Fires when a template node is clicked.
17879 * @param {Roo.View} this
17880 * @param {Number} index The index of the target node
17881 * @param {HTMLElement} node The target node
17882 * @param {Roo.EventObject} e The raw event object
17887 * Fires when a template node is double clicked.
17888 * @param {Roo.View} this
17889 * @param {Number} index The index of the target node
17890 * @param {HTMLElement} node The target node
17891 * @param {Roo.EventObject} e The raw event object
17895 * @event contextmenu
17896 * Fires when a template node is right clicked.
17897 * @param {Roo.View} this
17898 * @param {Number} index The index of the target node
17899 * @param {HTMLElement} node The target node
17900 * @param {Roo.EventObject} e The raw event object
17902 "contextmenu" : true,
17904 * @event selectionchange
17905 * Fires when the selected nodes change.
17906 * @param {Roo.View} this
17907 * @param {Array} selections Array of the selected nodes
17909 "selectionchange" : true,
17912 * @event beforeselect
17913 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17914 * @param {Roo.View} this
17915 * @param {HTMLElement} node The node to be selected
17916 * @param {Array} selections Array of currently selected nodes
17918 "beforeselect" : true,
17920 * @event preparedata
17921 * Fires on every row to render, to allow you to change the data.
17922 * @param {Roo.View} this
17923 * @param {Object} data to be rendered (change this)
17925 "preparedata" : true
17933 "click": this.onClick,
17934 "dblclick": this.onDblClick,
17935 "contextmenu": this.onContextMenu,
17939 this.selections = [];
17941 this.cmp = new Roo.CompositeElementLite([]);
17943 this.store = Roo.factory(this.store, Roo.data);
17944 this.setStore(this.store, true);
17947 if ( this.footer && this.footer.xtype) {
17949 var fctr = this.wrapEl.appendChild(document.createElement("div"));
17951 this.footer.dataSource = this.store;
17952 this.footer.container = fctr;
17953 this.footer = Roo.factory(this.footer, Roo);
17954 fctr.insertFirst(this.el);
17956 // this is a bit insane - as the paging toolbar seems to detach the el..
17957 // dom.parentNode.parentNode.parentNode
17958 // they get detached?
17962 Roo.View.superclass.constructor.call(this);
17967 Roo.extend(Roo.View, Roo.util.Observable, {
17970 * @cfg {Roo.data.Store} store Data store to load data from.
17975 * @cfg {String|Roo.Element} el The container element.
17980 * @cfg {String|Roo.Template} tpl The template used by this View
17984 * @cfg {String} dataName the named area of the template to use as the data area
17985 * Works with domtemplates roo-name="name"
17989 * @cfg {String} selectedClass The css class to add to selected nodes
17991 selectedClass : "x-view-selected",
17993 * @cfg {String} emptyText The empty text to show when nothing is loaded.
17998 * @cfg {String} text to display on mask (default Loading)
18002 * @cfg {Boolean} multiSelect Allow multiple selection
18004 multiSelect : false,
18006 * @cfg {Boolean} singleSelect Allow single selection
18008 singleSelect: false,
18011 * @cfg {Boolean} toggleSelect - selecting
18013 toggleSelect : false,
18016 * @cfg {Boolean} tickable - selecting
18021 * Returns the element this view is bound to.
18022 * @return {Roo.Element}
18024 getEl : function(){
18025 return this.wrapEl;
18031 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18033 refresh : function(){
18034 //Roo.log('refresh');
18037 // if we are using something like 'domtemplate', then
18038 // the what gets used is:
18039 // t.applySubtemplate(NAME, data, wrapping data..)
18040 // the outer template then get' applied with
18041 // the store 'extra data'
18042 // and the body get's added to the
18043 // roo-name="data" node?
18044 // <span class='roo-tpl-{name}'></span> ?????
18048 this.clearSelections();
18049 this.el.update("");
18051 var records = this.store.getRange();
18052 if(records.length < 1) {
18054 // is this valid?? = should it render a template??
18056 this.el.update(this.emptyText);
18060 if (this.dataName) {
18061 this.el.update(t.apply(this.store.meta)); //????
18062 el = this.el.child('.roo-tpl-' + this.dataName);
18065 for(var i = 0, len = records.length; i < len; i++){
18066 var data = this.prepareData(records[i].data, i, records[i]);
18067 this.fireEvent("preparedata", this, data, i, records[i]);
18069 var d = Roo.apply({}, data);
18072 Roo.apply(d, {'roo-id' : Roo.id()});
18076 Roo.each(this.parent.item, function(item){
18077 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18080 Roo.apply(d, {'roo-data-checked' : 'checked'});
18084 html[html.length] = Roo.util.Format.trim(
18086 t.applySubtemplate(this.dataName, d, this.store.meta) :
18093 el.update(html.join(""));
18094 this.nodes = el.dom.childNodes;
18095 this.updateIndexes(0);
18100 * Function to override to reformat the data that is sent to
18101 * the template for each node.
18102 * DEPRICATED - use the preparedata event handler.
18103 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18104 * a JSON object for an UpdateManager bound view).
18106 prepareData : function(data, index, record)
18108 this.fireEvent("preparedata", this, data, index, record);
18112 onUpdate : function(ds, record){
18113 // Roo.log('on update');
18114 this.clearSelections();
18115 var index = this.store.indexOf(record);
18116 var n = this.nodes[index];
18117 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18118 n.parentNode.removeChild(n);
18119 this.updateIndexes(index, index);
18125 onAdd : function(ds, records, index)
18127 //Roo.log(['on Add', ds, records, index] );
18128 this.clearSelections();
18129 if(this.nodes.length == 0){
18133 var n = this.nodes[index];
18134 for(var i = 0, len = records.length; i < len; i++){
18135 var d = this.prepareData(records[i].data, i, records[i]);
18137 this.tpl.insertBefore(n, d);
18140 this.tpl.append(this.el, d);
18143 this.updateIndexes(index);
18146 onRemove : function(ds, record, index){
18147 // Roo.log('onRemove');
18148 this.clearSelections();
18149 var el = this.dataName ?
18150 this.el.child('.roo-tpl-' + this.dataName) :
18153 el.dom.removeChild(this.nodes[index]);
18154 this.updateIndexes(index);
18158 * Refresh an individual node.
18159 * @param {Number} index
18161 refreshNode : function(index){
18162 this.onUpdate(this.store, this.store.getAt(index));
18165 updateIndexes : function(startIndex, endIndex){
18166 var ns = this.nodes;
18167 startIndex = startIndex || 0;
18168 endIndex = endIndex || ns.length - 1;
18169 for(var i = startIndex; i <= endIndex; i++){
18170 ns[i].nodeIndex = i;
18175 * Changes the data store this view uses and refresh the view.
18176 * @param {Store} store
18178 setStore : function(store, initial){
18179 if(!initial && this.store){
18180 this.store.un("datachanged", this.refresh);
18181 this.store.un("add", this.onAdd);
18182 this.store.un("remove", this.onRemove);
18183 this.store.un("update", this.onUpdate);
18184 this.store.un("clear", this.refresh);
18185 this.store.un("beforeload", this.onBeforeLoad);
18186 this.store.un("load", this.onLoad);
18187 this.store.un("loadexception", this.onLoad);
18191 store.on("datachanged", this.refresh, this);
18192 store.on("add", this.onAdd, this);
18193 store.on("remove", this.onRemove, this);
18194 store.on("update", this.onUpdate, this);
18195 store.on("clear", this.refresh, this);
18196 store.on("beforeload", this.onBeforeLoad, this);
18197 store.on("load", this.onLoad, this);
18198 store.on("loadexception", this.onLoad, this);
18206 * onbeforeLoad - masks the loading area.
18209 onBeforeLoad : function(store,opts)
18211 //Roo.log('onBeforeLoad');
18213 this.el.update("");
18215 this.el.mask(this.mask ? this.mask : "Loading" );
18217 onLoad : function ()
18224 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18225 * @param {HTMLElement} node
18226 * @return {HTMLElement} The template node
18228 findItemFromChild : function(node){
18229 var el = this.dataName ?
18230 this.el.child('.roo-tpl-' + this.dataName,true) :
18233 if(!node || node.parentNode == el){
18236 var p = node.parentNode;
18237 while(p && p != el){
18238 if(p.parentNode == el){
18247 onClick : function(e){
18248 var item = this.findItemFromChild(e.getTarget());
18250 var index = this.indexOf(item);
18251 if(this.onItemClick(item, index, e) !== false){
18252 this.fireEvent("click", this, index, item, e);
18255 this.clearSelections();
18260 onContextMenu : function(e){
18261 var item = this.findItemFromChild(e.getTarget());
18263 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18268 onDblClick : function(e){
18269 var item = this.findItemFromChild(e.getTarget());
18271 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18275 onItemClick : function(item, index, e)
18277 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18280 if (this.toggleSelect) {
18281 var m = this.isSelected(item) ? 'unselect' : 'select';
18284 _t[m](item, true, false);
18287 if(this.multiSelect || this.singleSelect){
18288 if(this.multiSelect && e.shiftKey && this.lastSelection){
18289 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18291 this.select(item, this.multiSelect && e.ctrlKey);
18292 this.lastSelection = item;
18295 if(!this.tickable){
18296 e.preventDefault();
18304 * Get the number of selected nodes.
18307 getSelectionCount : function(){
18308 return this.selections.length;
18312 * Get the currently selected nodes.
18313 * @return {Array} An array of HTMLElements
18315 getSelectedNodes : function(){
18316 return this.selections;
18320 * Get the indexes of the selected nodes.
18323 getSelectedIndexes : function(){
18324 var indexes = [], s = this.selections;
18325 for(var i = 0, len = s.length; i < len; i++){
18326 indexes.push(s[i].nodeIndex);
18332 * Clear all selections
18333 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18335 clearSelections : function(suppressEvent){
18336 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18337 this.cmp.elements = this.selections;
18338 this.cmp.removeClass(this.selectedClass);
18339 this.selections = [];
18340 if(!suppressEvent){
18341 this.fireEvent("selectionchange", this, this.selections);
18347 * Returns true if the passed node is selected
18348 * @param {HTMLElement/Number} node The node or node index
18349 * @return {Boolean}
18351 isSelected : function(node){
18352 var s = this.selections;
18356 node = this.getNode(node);
18357 return s.indexOf(node) !== -1;
18362 * @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
18363 * @param {Boolean} keepExisting (optional) true to keep existing selections
18364 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18366 select : function(nodeInfo, keepExisting, suppressEvent){
18367 if(nodeInfo instanceof Array){
18369 this.clearSelections(true);
18371 for(var i = 0, len = nodeInfo.length; i < len; i++){
18372 this.select(nodeInfo[i], true, true);
18376 var node = this.getNode(nodeInfo);
18377 if(!node || this.isSelected(node)){
18378 return; // already selected.
18381 this.clearSelections(true);
18384 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18385 Roo.fly(node).addClass(this.selectedClass);
18386 this.selections.push(node);
18387 if(!suppressEvent){
18388 this.fireEvent("selectionchange", this, this.selections);
18396 * @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
18397 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18398 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18400 unselect : function(nodeInfo, keepExisting, suppressEvent)
18402 if(nodeInfo instanceof Array){
18403 Roo.each(this.selections, function(s) {
18404 this.unselect(s, nodeInfo);
18408 var node = this.getNode(nodeInfo);
18409 if(!node || !this.isSelected(node)){
18410 //Roo.log("not selected");
18411 return; // not selected.
18415 Roo.each(this.selections, function(s) {
18417 Roo.fly(node).removeClass(this.selectedClass);
18424 this.selections= ns;
18425 this.fireEvent("selectionchange", this, this.selections);
18429 * Gets a template node.
18430 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18431 * @return {HTMLElement} The node or null if it wasn't found
18433 getNode : function(nodeInfo){
18434 if(typeof nodeInfo == "string"){
18435 return document.getElementById(nodeInfo);
18436 }else if(typeof nodeInfo == "number"){
18437 return this.nodes[nodeInfo];
18443 * Gets a range template nodes.
18444 * @param {Number} startIndex
18445 * @param {Number} endIndex
18446 * @return {Array} An array of nodes
18448 getNodes : function(start, end){
18449 var ns = this.nodes;
18450 start = start || 0;
18451 end = typeof end == "undefined" ? ns.length - 1 : end;
18454 for(var i = start; i <= end; i++){
18458 for(var i = start; i >= end; i--){
18466 * Finds the index of the passed node
18467 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18468 * @return {Number} The index of the node or -1
18470 indexOf : function(node){
18471 node = this.getNode(node);
18472 if(typeof node.nodeIndex == "number"){
18473 return node.nodeIndex;
18475 var ns = this.nodes;
18476 for(var i = 0, len = ns.length; i < len; i++){
18487 * based on jquery fullcalendar
18491 Roo.bootstrap = Roo.bootstrap || {};
18493 * @class Roo.bootstrap.Calendar
18494 * @extends Roo.bootstrap.Component
18495 * Bootstrap Calendar class
18496 * @cfg {Boolean} loadMask (true|false) default false
18497 * @cfg {Object} header generate the user specific header of the calendar, default false
18500 * Create a new Container
18501 * @param {Object} config The config object
18506 Roo.bootstrap.Calendar = function(config){
18507 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18511 * Fires when a date is selected
18512 * @param {DatePicker} this
18513 * @param {Date} date The selected date
18517 * @event monthchange
18518 * Fires when the displayed month changes
18519 * @param {DatePicker} this
18520 * @param {Date} date The selected month
18522 'monthchange': true,
18524 * @event evententer
18525 * Fires when mouse over an event
18526 * @param {Calendar} this
18527 * @param {event} Event
18529 'evententer': true,
18531 * @event eventleave
18532 * Fires when the mouse leaves an
18533 * @param {Calendar} this
18536 'eventleave': true,
18538 * @event eventclick
18539 * Fires when the mouse click an
18540 * @param {Calendar} this
18549 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18552 * @cfg {Number} startDay
18553 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18561 getAutoCreate : function(){
18564 var fc_button = function(name, corner, style, content ) {
18565 return Roo.apply({},{
18567 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18569 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18572 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18583 style : 'width:100%',
18590 cls : 'fc-header-left',
18592 fc_button('prev', 'left', 'arrow', '‹' ),
18593 fc_button('next', 'right', 'arrow', '›' ),
18594 { tag: 'span', cls: 'fc-header-space' },
18595 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18603 cls : 'fc-header-center',
18607 cls: 'fc-header-title',
18610 html : 'month / year'
18618 cls : 'fc-header-right',
18620 /* fc_button('month', 'left', '', 'month' ),
18621 fc_button('week', '', '', 'week' ),
18622 fc_button('day', 'right', '', 'day' )
18634 header = this.header;
18637 var cal_heads = function() {
18639 // fixme - handle this.
18641 for (var i =0; i < Date.dayNames.length; i++) {
18642 var d = Date.dayNames[i];
18645 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18646 html : d.substring(0,3)
18650 ret[0].cls += ' fc-first';
18651 ret[6].cls += ' fc-last';
18654 var cal_cell = function(n) {
18657 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18662 cls: 'fc-day-number',
18666 cls: 'fc-day-content',
18670 style: 'position: relative;' // height: 17px;
18682 var cal_rows = function() {
18685 for (var r = 0; r < 6; r++) {
18692 for (var i =0; i < Date.dayNames.length; i++) {
18693 var d = Date.dayNames[i];
18694 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18697 row.cn[0].cls+=' fc-first';
18698 row.cn[0].cn[0].style = 'min-height:90px';
18699 row.cn[6].cls+=' fc-last';
18703 ret[0].cls += ' fc-first';
18704 ret[4].cls += ' fc-prev-last';
18705 ret[5].cls += ' fc-last';
18712 cls: 'fc-border-separate',
18713 style : 'width:100%',
18721 cls : 'fc-first fc-last',
18739 cls : 'fc-content',
18740 style : "position: relative;",
18743 cls : 'fc-view fc-view-month fc-grid',
18744 style : 'position: relative',
18745 unselectable : 'on',
18748 cls : 'fc-event-container',
18749 style : 'position:absolute;z-index:8;top:0;left:0;'
18767 initEvents : function()
18770 throw "can not find store for calendar";
18776 style: "text-align:center",
18780 style: "background-color:white;width:50%;margin:250 auto",
18784 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18795 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18797 var size = this.el.select('.fc-content', true).first().getSize();
18798 this.maskEl.setSize(size.width, size.height);
18799 this.maskEl.enableDisplayMode("block");
18800 if(!this.loadMask){
18801 this.maskEl.hide();
18804 this.store = Roo.factory(this.store, Roo.data);
18805 this.store.on('load', this.onLoad, this);
18806 this.store.on('beforeload', this.onBeforeLoad, this);
18810 this.cells = this.el.select('.fc-day',true);
18811 //Roo.log(this.cells);
18812 this.textNodes = this.el.query('.fc-day-number');
18813 this.cells.addClassOnOver('fc-state-hover');
18815 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18816 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18817 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18818 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18820 this.on('monthchange', this.onMonthChange, this);
18822 this.update(new Date().clearTime());
18825 resize : function() {
18826 var sz = this.el.getSize();
18828 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18829 this.el.select('.fc-day-content div',true).setHeight(34);
18834 showPrevMonth : function(e){
18835 this.update(this.activeDate.add("mo", -1));
18837 showToday : function(e){
18838 this.update(new Date().clearTime());
18841 showNextMonth : function(e){
18842 this.update(this.activeDate.add("mo", 1));
18846 showPrevYear : function(){
18847 this.update(this.activeDate.add("y", -1));
18851 showNextYear : function(){
18852 this.update(this.activeDate.add("y", 1));
18857 update : function(date)
18859 var vd = this.activeDate;
18860 this.activeDate = date;
18861 // if(vd && this.el){
18862 // var t = date.getTime();
18863 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18864 // Roo.log('using add remove');
18866 // this.fireEvent('monthchange', this, date);
18868 // this.cells.removeClass("fc-state-highlight");
18869 // this.cells.each(function(c){
18870 // if(c.dateValue == t){
18871 // c.addClass("fc-state-highlight");
18872 // setTimeout(function(){
18873 // try{c.dom.firstChild.focus();}catch(e){}
18883 var days = date.getDaysInMonth();
18885 var firstOfMonth = date.getFirstDateOfMonth();
18886 var startingPos = firstOfMonth.getDay()-this.startDay;
18888 if(startingPos < this.startDay){
18892 var pm = date.add(Date.MONTH, -1);
18893 var prevStart = pm.getDaysInMonth()-startingPos;
18895 this.cells = this.el.select('.fc-day',true);
18896 this.textNodes = this.el.query('.fc-day-number');
18897 this.cells.addClassOnOver('fc-state-hover');
18899 var cells = this.cells.elements;
18900 var textEls = this.textNodes;
18902 Roo.each(cells, function(cell){
18903 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18906 days += startingPos;
18908 // convert everything to numbers so it's fast
18909 var day = 86400000;
18910 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18913 //Roo.log(prevStart);
18915 var today = new Date().clearTime().getTime();
18916 var sel = date.clearTime().getTime();
18917 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18918 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18919 var ddMatch = this.disabledDatesRE;
18920 var ddText = this.disabledDatesText;
18921 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18922 var ddaysText = this.disabledDaysText;
18923 var format = this.format;
18925 var setCellClass = function(cal, cell){
18929 //Roo.log('set Cell Class');
18931 var t = d.getTime();
18935 cell.dateValue = t;
18937 cell.className += " fc-today";
18938 cell.className += " fc-state-highlight";
18939 cell.title = cal.todayText;
18942 // disable highlight in other month..
18943 //cell.className += " fc-state-highlight";
18948 cell.className = " fc-state-disabled";
18949 cell.title = cal.minText;
18953 cell.className = " fc-state-disabled";
18954 cell.title = cal.maxText;
18958 if(ddays.indexOf(d.getDay()) != -1){
18959 cell.title = ddaysText;
18960 cell.className = " fc-state-disabled";
18963 if(ddMatch && format){
18964 var fvalue = d.dateFormat(format);
18965 if(ddMatch.test(fvalue)){
18966 cell.title = ddText.replace("%0", fvalue);
18967 cell.className = " fc-state-disabled";
18971 if (!cell.initialClassName) {
18972 cell.initialClassName = cell.dom.className;
18975 cell.dom.className = cell.initialClassName + ' ' + cell.className;
18980 for(; i < startingPos; i++) {
18981 textEls[i].innerHTML = (++prevStart);
18982 d.setDate(d.getDate()+1);
18984 cells[i].className = "fc-past fc-other-month";
18985 setCellClass(this, cells[i]);
18990 for(; i < days; i++){
18991 intDay = i - startingPos + 1;
18992 textEls[i].innerHTML = (intDay);
18993 d.setDate(d.getDate()+1);
18995 cells[i].className = ''; // "x-date-active";
18996 setCellClass(this, cells[i]);
19000 for(; i < 42; i++) {
19001 textEls[i].innerHTML = (++extraDays);
19002 d.setDate(d.getDate()+1);
19004 cells[i].className = "fc-future fc-other-month";
19005 setCellClass(this, cells[i]);
19008 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19010 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19012 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19013 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19015 if(totalRows != 6){
19016 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19017 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19020 this.fireEvent('monthchange', this, date);
19024 if(!this.internalRender){
19025 var main = this.el.dom.firstChild;
19026 var w = main.offsetWidth;
19027 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19028 Roo.fly(main).setWidth(w);
19029 this.internalRender = true;
19030 // opera does not respect the auto grow header center column
19031 // then, after it gets a width opera refuses to recalculate
19032 // without a second pass
19033 if(Roo.isOpera && !this.secondPass){
19034 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19035 this.secondPass = true;
19036 this.update.defer(10, this, [date]);
19043 findCell : function(dt) {
19044 dt = dt.clearTime().getTime();
19046 this.cells.each(function(c){
19047 //Roo.log("check " +c.dateValue + '?=' + dt);
19048 if(c.dateValue == dt){
19058 findCells : function(ev) {
19059 var s = ev.start.clone().clearTime().getTime();
19061 var e= ev.end.clone().clearTime().getTime();
19064 this.cells.each(function(c){
19065 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19067 if(c.dateValue > e){
19070 if(c.dateValue < s){
19079 // findBestRow: function(cells)
19083 // for (var i =0 ; i < cells.length;i++) {
19084 // ret = Math.max(cells[i].rows || 0,ret);
19091 addItem : function(ev)
19093 // look for vertical location slot in
19094 var cells = this.findCells(ev);
19096 // ev.row = this.findBestRow(cells);
19098 // work out the location.
19102 for(var i =0; i < cells.length; i++) {
19104 cells[i].row = cells[0].row;
19107 cells[i].row = cells[i].row + 1;
19117 if (crow.start.getY() == cells[i].getY()) {
19119 crow.end = cells[i];
19136 cells[0].events.push(ev);
19138 this.calevents.push(ev);
19141 clearEvents: function() {
19143 if(!this.calevents){
19147 Roo.each(this.cells.elements, function(c){
19153 Roo.each(this.calevents, function(e) {
19154 Roo.each(e.els, function(el) {
19155 el.un('mouseenter' ,this.onEventEnter, this);
19156 el.un('mouseleave' ,this.onEventLeave, this);
19161 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19167 renderEvents: function()
19171 this.cells.each(function(c) {
19180 if(c.row != c.events.length){
19181 r = 4 - (4 - (c.row - c.events.length));
19184 c.events = ev.slice(0, r);
19185 c.more = ev.slice(r);
19187 if(c.more.length && c.more.length == 1){
19188 c.events.push(c.more.pop());
19191 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19195 this.cells.each(function(c) {
19197 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19200 for (var e = 0; e < c.events.length; e++){
19201 var ev = c.events[e];
19202 var rows = ev.rows;
19204 for(var i = 0; i < rows.length; i++) {
19206 // how many rows should it span..
19209 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19210 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19212 unselectable : "on",
19215 cls: 'fc-event-inner',
19219 // cls: 'fc-event-time',
19220 // html : cells.length > 1 ? '' : ev.time
19224 cls: 'fc-event-title',
19225 html : String.format('{0}', ev.title)
19232 cls: 'ui-resizable-handle ui-resizable-e',
19233 html : '  '
19240 cfg.cls += ' fc-event-start';
19242 if ((i+1) == rows.length) {
19243 cfg.cls += ' fc-event-end';
19246 var ctr = _this.el.select('.fc-event-container',true).first();
19247 var cg = ctr.createChild(cfg);
19249 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19250 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19252 var r = (c.more.length) ? 1 : 0;
19253 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19254 cg.setWidth(ebox.right - sbox.x -2);
19256 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19257 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19258 cg.on('click', _this.onEventClick, _this, ev);
19269 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19270 style : 'position: absolute',
19271 unselectable : "on",
19274 cls: 'fc-event-inner',
19278 cls: 'fc-event-title',
19286 cls: 'ui-resizable-handle ui-resizable-e',
19287 html : '  '
19293 var ctr = _this.el.select('.fc-event-container',true).first();
19294 var cg = ctr.createChild(cfg);
19296 var sbox = c.select('.fc-day-content',true).first().getBox();
19297 var ebox = c.select('.fc-day-content',true).first().getBox();
19299 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19300 cg.setWidth(ebox.right - sbox.x -2);
19302 cg.on('click', _this.onMoreEventClick, _this, c.more);
19312 onEventEnter: function (e, el,event,d) {
19313 this.fireEvent('evententer', this, el, event);
19316 onEventLeave: function (e, el,event,d) {
19317 this.fireEvent('eventleave', this, el, event);
19320 onEventClick: function (e, el,event,d) {
19321 this.fireEvent('eventclick', this, el, event);
19324 onMonthChange: function () {
19328 onMoreEventClick: function(e, el, more)
19332 this.calpopover.placement = 'right';
19333 this.calpopover.setTitle('More');
19335 this.calpopover.setContent('');
19337 var ctr = this.calpopover.el.select('.popover-content', true).first();
19339 Roo.each(more, function(m){
19341 cls : 'fc-event-hori fc-event-draggable',
19344 var cg = ctr.createChild(cfg);
19346 cg.on('click', _this.onEventClick, _this, m);
19349 this.calpopover.show(el);
19354 onLoad: function ()
19356 this.calevents = [];
19359 if(this.store.getCount() > 0){
19360 this.store.data.each(function(d){
19363 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19364 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19365 time : d.data.start_time,
19366 title : d.data.title,
19367 description : d.data.description,
19368 venue : d.data.venue
19373 this.renderEvents();
19375 if(this.calevents.length && this.loadMask){
19376 this.maskEl.hide();
19380 onBeforeLoad: function()
19382 this.clearEvents();
19384 this.maskEl.show();
19398 * @class Roo.bootstrap.Popover
19399 * @extends Roo.bootstrap.Component
19400 * Bootstrap Popover class
19401 * @cfg {String} html contents of the popover (or false to use children..)
19402 * @cfg {String} title of popover (or false to hide)
19403 * @cfg {String} placement how it is placed
19404 * @cfg {String} trigger click || hover (or false to trigger manually)
19405 * @cfg {String} over what (parent or false to trigger manually.)
19406 * @cfg {Number} delay - delay before showing
19409 * Create a new Popover
19410 * @param {Object} config The config object
19413 Roo.bootstrap.Popover = function(config){
19414 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19420 * After the popover show
19422 * @param {Roo.bootstrap.Popover} this
19427 * After the popover hide
19429 * @param {Roo.bootstrap.Popover} this
19435 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19437 title: 'Fill in a title',
19440 placement : 'right',
19441 trigger : 'hover', // hover
19447 can_build_overlaid : false,
19449 getChildContainer : function()
19451 return this.el.select('.popover-content',true).first();
19454 getAutoCreate : function(){
19457 cls : 'popover roo-dynamic',
19458 style: 'display:block',
19464 cls : 'popover-inner',
19468 cls: 'popover-title popover-header',
19472 cls : 'popover-content popover-body',
19483 setTitle: function(str)
19486 this.el.select('.popover-title',true).first().dom.innerHTML = str;
19488 setContent: function(str)
19491 this.el.select('.popover-content',true).first().dom.innerHTML = str;
19493 // as it get's added to the bottom of the page.
19494 onRender : function(ct, position)
19496 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19498 var cfg = Roo.apply({}, this.getAutoCreate());
19502 cfg.cls += ' ' + this.cls;
19505 cfg.style = this.style;
19507 //Roo.log("adding to ");
19508 this.el = Roo.get(document.body).createChild(cfg, position);
19509 // Roo.log(this.el);
19514 initEvents : function()
19516 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19517 this.el.enableDisplayMode('block');
19519 if (this.over === false) {
19522 if (this.triggers === false) {
19525 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19526 var triggers = this.trigger ? this.trigger.split(' ') : [];
19527 Roo.each(triggers, function(trigger) {
19529 if (trigger == 'click') {
19530 on_el.on('click', this.toggle, this);
19531 } else if (trigger != 'manual') {
19532 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19533 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19535 on_el.on(eventIn ,this.enter, this);
19536 on_el.on(eventOut, this.leave, this);
19547 toggle : function () {
19548 this.hoverState == 'in' ? this.leave() : this.enter();
19551 enter : function () {
19553 clearTimeout(this.timeout);
19555 this.hoverState = 'in';
19557 if (!this.delay || !this.delay.show) {
19562 this.timeout = setTimeout(function () {
19563 if (_t.hoverState == 'in') {
19566 }, this.delay.show)
19569 leave : function() {
19570 clearTimeout(this.timeout);
19572 this.hoverState = 'out';
19574 if (!this.delay || !this.delay.hide) {
19579 this.timeout = setTimeout(function () {
19580 if (_t.hoverState == 'out') {
19583 }, this.delay.hide)
19586 show : function (on_el)
19589 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19593 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19594 if (this.html !== false) {
19595 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19597 this.el.removeClass([
19598 'fade','top','bottom', 'left', 'right','in',
19599 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19601 if (!this.title.length) {
19602 this.el.select('.popover-title',true).hide();
19605 var placement = typeof this.placement == 'function' ?
19606 this.placement.call(this, this.el, on_el) :
19609 var autoToken = /\s?auto?\s?/i;
19610 var autoPlace = autoToken.test(placement);
19612 placement = placement.replace(autoToken, '') || 'top';
19616 //this.el.setXY([0,0]);
19618 this.el.dom.style.display='block';
19619 this.el.addClass(placement);
19621 //this.el.appendTo(on_el);
19623 var p = this.getPosition();
19624 var box = this.el.getBox();
19629 var align = Roo.bootstrap.Popover.alignment[placement];
19632 this.el.alignTo(on_el, align[0],align[1]);
19633 //var arrow = this.el.select('.arrow',true).first();
19634 //arrow.set(align[2],
19636 this.el.addClass('in');
19639 if (this.el.hasClass('fade')) {
19643 this.hoverState = 'in';
19645 this.fireEvent('show', this);
19650 this.el.setXY([0,0]);
19651 this.el.removeClass('in');
19653 this.hoverState = null;
19655 this.fireEvent('hide', this);
19660 Roo.bootstrap.Popover.alignment = {
19661 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19662 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19663 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19664 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19675 * @class Roo.bootstrap.Progress
19676 * @extends Roo.bootstrap.Component
19677 * Bootstrap Progress class
19678 * @cfg {Boolean} striped striped of the progress bar
19679 * @cfg {Boolean} active animated of the progress bar
19683 * Create a new Progress
19684 * @param {Object} config The config object
19687 Roo.bootstrap.Progress = function(config){
19688 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19691 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19696 getAutoCreate : function(){
19704 cfg.cls += ' progress-striped';
19708 cfg.cls += ' active';
19727 * @class Roo.bootstrap.ProgressBar
19728 * @extends Roo.bootstrap.Component
19729 * Bootstrap ProgressBar class
19730 * @cfg {Number} aria_valuenow aria-value now
19731 * @cfg {Number} aria_valuemin aria-value min
19732 * @cfg {Number} aria_valuemax aria-value max
19733 * @cfg {String} label label for the progress bar
19734 * @cfg {String} panel (success | info | warning | danger )
19735 * @cfg {String} role role of the progress bar
19736 * @cfg {String} sr_only text
19740 * Create a new ProgressBar
19741 * @param {Object} config The config object
19744 Roo.bootstrap.ProgressBar = function(config){
19745 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19748 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19752 aria_valuemax : 100,
19758 getAutoCreate : function()
19763 cls: 'progress-bar',
19764 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19776 cfg.role = this.role;
19779 if(this.aria_valuenow){
19780 cfg['aria-valuenow'] = this.aria_valuenow;
19783 if(this.aria_valuemin){
19784 cfg['aria-valuemin'] = this.aria_valuemin;
19787 if(this.aria_valuemax){
19788 cfg['aria-valuemax'] = this.aria_valuemax;
19791 if(this.label && !this.sr_only){
19792 cfg.html = this.label;
19796 cfg.cls += ' progress-bar-' + this.panel;
19802 update : function(aria_valuenow)
19804 this.aria_valuenow = aria_valuenow;
19806 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19821 * @class Roo.bootstrap.TabGroup
19822 * @extends Roo.bootstrap.Column
19823 * Bootstrap Column class
19824 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19825 * @cfg {Boolean} carousel true to make the group behave like a carousel
19826 * @cfg {Boolean} bullets show bullets for the panels
19827 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19828 * @cfg {Number} timer auto slide timer .. default 0 millisecond
19829 * @cfg {Boolean} showarrow (true|false) show arrow default true
19832 * Create a new TabGroup
19833 * @param {Object} config The config object
19836 Roo.bootstrap.TabGroup = function(config){
19837 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19839 this.navId = Roo.id();
19842 Roo.bootstrap.TabGroup.register(this);
19846 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
19849 transition : false,
19854 slideOnTouch : false,
19857 getAutoCreate : function()
19859 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19861 cfg.cls += ' tab-content';
19863 if (this.carousel) {
19864 cfg.cls += ' carousel slide';
19867 cls : 'carousel-inner',
19871 if(this.bullets && !Roo.isTouch){
19874 cls : 'carousel-bullets',
19878 if(this.bullets_cls){
19879 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19886 cfg.cn[0].cn.push(bullets);
19889 if(this.showarrow){
19890 cfg.cn[0].cn.push({
19892 class : 'carousel-arrow',
19896 class : 'carousel-prev',
19900 class : 'fa fa-chevron-left'
19906 class : 'carousel-next',
19910 class : 'fa fa-chevron-right'
19923 initEvents: function()
19925 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19926 // this.el.on("touchstart", this.onTouchStart, this);
19929 if(this.autoslide){
19932 this.slideFn = window.setInterval(function() {
19933 _this.showPanelNext();
19937 if(this.showarrow){
19938 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19939 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19945 // onTouchStart : function(e, el, o)
19947 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19951 // this.showPanelNext();
19955 getChildContainer : function()
19957 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19961 * register a Navigation item
19962 * @param {Roo.bootstrap.NavItem} the navitem to add
19964 register : function(item)
19966 this.tabs.push( item);
19967 item.navId = this.navId; // not really needed..
19972 getActivePanel : function()
19975 Roo.each(this.tabs, function(t) {
19985 getPanelByName : function(n)
19988 Roo.each(this.tabs, function(t) {
19989 if (t.tabId == n) {
19997 indexOfPanel : function(p)
20000 Roo.each(this.tabs, function(t,i) {
20001 if (t.tabId == p.tabId) {
20010 * show a specific panel
20011 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20012 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20014 showPanel : function (pan)
20016 if(this.transition || typeof(pan) == 'undefined'){
20017 Roo.log("waiting for the transitionend");
20021 if (typeof(pan) == 'number') {
20022 pan = this.tabs[pan];
20025 if (typeof(pan) == 'string') {
20026 pan = this.getPanelByName(pan);
20029 var cur = this.getActivePanel();
20032 Roo.log('pan or acitve pan is undefined');
20036 if (pan.tabId == this.getActivePanel().tabId) {
20040 if (false === cur.fireEvent('beforedeactivate')) {
20044 if(this.bullets > 0 && !Roo.isTouch){
20045 this.setActiveBullet(this.indexOfPanel(pan));
20048 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20050 //class="carousel-item carousel-item-next carousel-item-left"
20052 this.transition = true;
20053 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20054 var lr = dir == 'next' ? 'left' : 'right';
20055 pan.el.addClass(dir); // or prev
20056 pan.el.addClass('carousel-item-' + dir); // or prev
20057 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20058 cur.el.addClass(lr); // or right
20059 pan.el.addClass(lr);
20060 cur.el.addClass('carousel-item-' +lr); // or right
20061 pan.el.addClass('carousel-item-' +lr);
20065 cur.el.on('transitionend', function() {
20066 Roo.log("trans end?");
20068 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20069 pan.setActive(true);
20071 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20072 cur.setActive(false);
20074 _this.transition = false;
20076 }, this, { single: true } );
20081 cur.setActive(false);
20082 pan.setActive(true);
20087 showPanelNext : function()
20089 var i = this.indexOfPanel(this.getActivePanel());
20091 if (i >= this.tabs.length - 1 && !this.autoslide) {
20095 if (i >= this.tabs.length - 1 && this.autoslide) {
20099 this.showPanel(this.tabs[i+1]);
20102 showPanelPrev : function()
20104 var i = this.indexOfPanel(this.getActivePanel());
20106 if (i < 1 && !this.autoslide) {
20110 if (i < 1 && this.autoslide) {
20111 i = this.tabs.length;
20114 this.showPanel(this.tabs[i-1]);
20118 addBullet: function()
20120 if(!this.bullets || Roo.isTouch){
20123 var ctr = this.el.select('.carousel-bullets',true).first();
20124 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20125 var bullet = ctr.createChild({
20126 cls : 'bullet bullet-' + i
20127 },ctr.dom.lastChild);
20132 bullet.on('click', (function(e, el, o, ii, t){
20134 e.preventDefault();
20136 this.showPanel(ii);
20138 if(this.autoslide && this.slideFn){
20139 clearInterval(this.slideFn);
20140 this.slideFn = window.setInterval(function() {
20141 _this.showPanelNext();
20145 }).createDelegate(this, [i, bullet], true));
20150 setActiveBullet : function(i)
20156 Roo.each(this.el.select('.bullet', true).elements, function(el){
20157 el.removeClass('selected');
20160 var bullet = this.el.select('.bullet-' + i, true).first();
20166 bullet.addClass('selected');
20177 Roo.apply(Roo.bootstrap.TabGroup, {
20181 * register a Navigation Group
20182 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20184 register : function(navgrp)
20186 this.groups[navgrp.navId] = navgrp;
20190 * fetch a Navigation Group based on the navigation ID
20191 * if one does not exist , it will get created.
20192 * @param {string} the navgroup to add
20193 * @returns {Roo.bootstrap.NavGroup} the navgroup
20195 get: function(navId) {
20196 if (typeof(this.groups[navId]) == 'undefined') {
20197 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20199 return this.groups[navId] ;
20214 * @class Roo.bootstrap.TabPanel
20215 * @extends Roo.bootstrap.Component
20216 * Bootstrap TabPanel class
20217 * @cfg {Boolean} active panel active
20218 * @cfg {String} html panel content
20219 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20220 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20221 * @cfg {String} href click to link..
20225 * Create a new TabPanel
20226 * @param {Object} config The config object
20229 Roo.bootstrap.TabPanel = function(config){
20230 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20234 * Fires when the active status changes
20235 * @param {Roo.bootstrap.TabPanel} this
20236 * @param {Boolean} state the new state
20241 * @event beforedeactivate
20242 * Fires before a tab is de-activated - can be used to do validation on a form.
20243 * @param {Roo.bootstrap.TabPanel} this
20244 * @return {Boolean} false if there is an error
20247 'beforedeactivate': true
20250 this.tabId = this.tabId || Roo.id();
20254 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20262 getAutoCreate : function(){
20267 // item is needed for carousel - not sure if it has any effect otherwise
20268 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20269 html: this.html || ''
20273 cfg.cls += ' active';
20277 cfg.tabId = this.tabId;
20285 initEvents: function()
20287 var p = this.parent();
20289 this.navId = this.navId || p.navId;
20291 if (typeof(this.navId) != 'undefined') {
20292 // not really needed.. but just in case.. parent should be a NavGroup.
20293 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20297 var i = tg.tabs.length - 1;
20299 if(this.active && tg.bullets > 0 && i < tg.bullets){
20300 tg.setActiveBullet(i);
20304 this.el.on('click', this.onClick, this);
20307 this.el.on("touchstart", this.onTouchStart, this);
20308 this.el.on("touchmove", this.onTouchMove, this);
20309 this.el.on("touchend", this.onTouchEnd, this);
20314 onRender : function(ct, position)
20316 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20319 setActive : function(state)
20321 Roo.log("panel - set active " + this.tabId + "=" + state);
20323 this.active = state;
20325 this.el.removeClass('active');
20327 } else if (!this.el.hasClass('active')) {
20328 this.el.addClass('active');
20331 this.fireEvent('changed', this, state);
20334 onClick : function(e)
20336 e.preventDefault();
20338 if(!this.href.length){
20342 window.location.href = this.href;
20351 onTouchStart : function(e)
20353 this.swiping = false;
20355 this.startX = e.browserEvent.touches[0].clientX;
20356 this.startY = e.browserEvent.touches[0].clientY;
20359 onTouchMove : function(e)
20361 this.swiping = true;
20363 this.endX = e.browserEvent.touches[0].clientX;
20364 this.endY = e.browserEvent.touches[0].clientY;
20367 onTouchEnd : function(e)
20374 var tabGroup = this.parent();
20376 if(this.endX > this.startX){ // swiping right
20377 tabGroup.showPanelPrev();
20381 if(this.startX > this.endX){ // swiping left
20382 tabGroup.showPanelNext();
20401 * @class Roo.bootstrap.DateField
20402 * @extends Roo.bootstrap.Input
20403 * Bootstrap DateField class
20404 * @cfg {Number} weekStart default 0
20405 * @cfg {String} viewMode default empty, (months|years)
20406 * @cfg {String} minViewMode default empty, (months|years)
20407 * @cfg {Number} startDate default -Infinity
20408 * @cfg {Number} endDate default Infinity
20409 * @cfg {Boolean} todayHighlight default false
20410 * @cfg {Boolean} todayBtn default false
20411 * @cfg {Boolean} calendarWeeks default false
20412 * @cfg {Object} daysOfWeekDisabled default empty
20413 * @cfg {Boolean} singleMode default false (true | false)
20415 * @cfg {Boolean} keyboardNavigation default true
20416 * @cfg {String} language default en
20419 * Create a new DateField
20420 * @param {Object} config The config object
20423 Roo.bootstrap.DateField = function(config){
20424 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20428 * Fires when this field show.
20429 * @param {Roo.bootstrap.DateField} this
20430 * @param {Mixed} date The date value
20435 * Fires when this field hide.
20436 * @param {Roo.bootstrap.DateField} this
20437 * @param {Mixed} date The date value
20442 * Fires when select a date.
20443 * @param {Roo.bootstrap.DateField} this
20444 * @param {Mixed} date The date value
20448 * @event beforeselect
20449 * Fires when before select a date.
20450 * @param {Roo.bootstrap.DateField} this
20451 * @param {Mixed} date The date value
20453 beforeselect : true
20457 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20460 * @cfg {String} format
20461 * The default date format string which can be overriden for localization support. The format must be
20462 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20466 * @cfg {String} altFormats
20467 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20468 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20470 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20478 todayHighlight : false,
20484 keyboardNavigation: true,
20486 calendarWeeks: false,
20488 startDate: -Infinity,
20492 daysOfWeekDisabled: [],
20496 singleMode : false,
20498 UTCDate: function()
20500 return new Date(Date.UTC.apply(Date, arguments));
20503 UTCToday: function()
20505 var today = new Date();
20506 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20509 getDate: function() {
20510 var d = this.getUTCDate();
20511 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20514 getUTCDate: function() {
20518 setDate: function(d) {
20519 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20522 setUTCDate: function(d) {
20524 this.setValue(this.formatDate(this.date));
20527 onRender: function(ct, position)
20530 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20532 this.language = this.language || 'en';
20533 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20534 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20536 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20537 this.format = this.format || 'm/d/y';
20538 this.isInline = false;
20539 this.isInput = true;
20540 this.component = this.el.select('.add-on', true).first() || false;
20541 this.component = (this.component && this.component.length === 0) ? false : this.component;
20542 this.hasInput = this.component && this.inputEl().length;
20544 if (typeof(this.minViewMode === 'string')) {
20545 switch (this.minViewMode) {
20547 this.minViewMode = 1;
20550 this.minViewMode = 2;
20553 this.minViewMode = 0;
20558 if (typeof(this.viewMode === 'string')) {
20559 switch (this.viewMode) {
20572 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20574 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20576 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20578 this.picker().on('mousedown', this.onMousedown, this);
20579 this.picker().on('click', this.onClick, this);
20581 this.picker().addClass('datepicker-dropdown');
20583 this.startViewMode = this.viewMode;
20585 if(this.singleMode){
20586 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20587 v.setVisibilityMode(Roo.Element.DISPLAY);
20591 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20592 v.setStyle('width', '189px');
20596 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20597 if(!this.calendarWeeks){
20602 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20603 v.attr('colspan', function(i, val){
20604 return parseInt(val) + 1;
20609 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20611 this.setStartDate(this.startDate);
20612 this.setEndDate(this.endDate);
20614 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20621 if(this.isInline) {
20626 picker : function()
20628 return this.pickerEl;
20629 // return this.el.select('.datepicker', true).first();
20632 fillDow: function()
20634 var dowCnt = this.weekStart;
20643 if(this.calendarWeeks){
20651 while (dowCnt < this.weekStart + 7) {
20655 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20659 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20662 fillMonths: function()
20665 var months = this.picker().select('>.datepicker-months td', true).first();
20667 months.dom.innerHTML = '';
20673 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20676 months.createChild(month);
20683 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;
20685 if (this.date < this.startDate) {
20686 this.viewDate = new Date(this.startDate);
20687 } else if (this.date > this.endDate) {
20688 this.viewDate = new Date(this.endDate);
20690 this.viewDate = new Date(this.date);
20698 var d = new Date(this.viewDate),
20699 year = d.getUTCFullYear(),
20700 month = d.getUTCMonth(),
20701 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20702 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20703 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20704 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20705 currentDate = this.date && this.date.valueOf(),
20706 today = this.UTCToday();
20708 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20710 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20712 // this.picker.select('>tfoot th.today').
20713 // .text(dates[this.language].today)
20714 // .toggle(this.todayBtn !== false);
20716 this.updateNavArrows();
20719 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20721 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20723 prevMonth.setUTCDate(day);
20725 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20727 var nextMonth = new Date(prevMonth);
20729 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20731 nextMonth = nextMonth.valueOf();
20733 var fillMonths = false;
20735 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20737 while(prevMonth.valueOf() <= nextMonth) {
20740 if (prevMonth.getUTCDay() === this.weekStart) {
20742 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20750 if(this.calendarWeeks){
20751 // ISO 8601: First week contains first thursday.
20752 // ISO also states week starts on Monday, but we can be more abstract here.
20754 // Start of current week: based on weekstart/current date
20755 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20756 // Thursday of this week
20757 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20758 // First Thursday of year, year from thursday
20759 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20760 // Calendar week: ms between thursdays, div ms per day, div 7 days
20761 calWeek = (th - yth) / 864e5 / 7 + 1;
20763 fillMonths.cn.push({
20771 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20773 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20776 if (this.todayHighlight &&
20777 prevMonth.getUTCFullYear() == today.getFullYear() &&
20778 prevMonth.getUTCMonth() == today.getMonth() &&
20779 prevMonth.getUTCDate() == today.getDate()) {
20780 clsName += ' today';
20783 if (currentDate && prevMonth.valueOf() === currentDate) {
20784 clsName += ' active';
20787 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20788 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20789 clsName += ' disabled';
20792 fillMonths.cn.push({
20794 cls: 'day ' + clsName,
20795 html: prevMonth.getDate()
20798 prevMonth.setDate(prevMonth.getDate()+1);
20801 var currentYear = this.date && this.date.getUTCFullYear();
20802 var currentMonth = this.date && this.date.getUTCMonth();
20804 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20806 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20807 v.removeClass('active');
20809 if(currentYear === year && k === currentMonth){
20810 v.addClass('active');
20813 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20814 v.addClass('disabled');
20820 year = parseInt(year/10, 10) * 10;
20822 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20824 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20827 for (var i = -1; i < 11; i++) {
20828 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20830 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20838 showMode: function(dir)
20841 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20844 Roo.each(this.picker().select('>div',true).elements, function(v){
20845 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20848 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20853 if(this.isInline) {
20857 this.picker().removeClass(['bottom', 'top']);
20859 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20861 * place to the top of element!
20865 this.picker().addClass('top');
20866 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20871 this.picker().addClass('bottom');
20873 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20876 parseDate : function(value)
20878 if(!value || value instanceof Date){
20881 var v = Date.parseDate(value, this.format);
20882 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20883 v = Date.parseDate(value, 'Y-m-d');
20885 if(!v && this.altFormats){
20886 if(!this.altFormatsArray){
20887 this.altFormatsArray = this.altFormats.split("|");
20889 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20890 v = Date.parseDate(value, this.altFormatsArray[i]);
20896 formatDate : function(date, fmt)
20898 return (!date || !(date instanceof Date)) ?
20899 date : date.dateFormat(fmt || this.format);
20902 onFocus : function()
20904 Roo.bootstrap.DateField.superclass.onFocus.call(this);
20908 onBlur : function()
20910 Roo.bootstrap.DateField.superclass.onBlur.call(this);
20912 var d = this.inputEl().getValue();
20919 showPopup : function()
20921 this.picker().show();
20925 this.fireEvent('showpopup', this, this.date);
20928 hidePopup : function()
20930 if(this.isInline) {
20933 this.picker().hide();
20934 this.viewMode = this.startViewMode;
20937 this.fireEvent('hidepopup', this, this.date);
20941 onMousedown: function(e)
20943 e.stopPropagation();
20944 e.preventDefault();
20949 Roo.bootstrap.DateField.superclass.keyup.call(this);
20953 setValue: function(v)
20955 if(this.fireEvent('beforeselect', this, v) !== false){
20956 var d = new Date(this.parseDate(v) ).clearTime();
20958 if(isNaN(d.getTime())){
20959 this.date = this.viewDate = '';
20960 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20964 v = this.formatDate(d);
20966 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20968 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20972 this.fireEvent('select', this, this.date);
20976 getValue: function()
20978 return this.formatDate(this.date);
20981 fireKey: function(e)
20983 if (!this.picker().isVisible()){
20984 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20990 var dateChanged = false,
20992 newDate, newViewDate;
20997 e.preventDefault();
21001 if (!this.keyboardNavigation) {
21004 dir = e.keyCode == 37 ? -1 : 1;
21007 newDate = this.moveYear(this.date, dir);
21008 newViewDate = this.moveYear(this.viewDate, dir);
21009 } else if (e.shiftKey){
21010 newDate = this.moveMonth(this.date, dir);
21011 newViewDate = this.moveMonth(this.viewDate, dir);
21013 newDate = new Date(this.date);
21014 newDate.setUTCDate(this.date.getUTCDate() + dir);
21015 newViewDate = new Date(this.viewDate);
21016 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21018 if (this.dateWithinRange(newDate)){
21019 this.date = newDate;
21020 this.viewDate = newViewDate;
21021 this.setValue(this.formatDate(this.date));
21023 e.preventDefault();
21024 dateChanged = true;
21029 if (!this.keyboardNavigation) {
21032 dir = e.keyCode == 38 ? -1 : 1;
21034 newDate = this.moveYear(this.date, dir);
21035 newViewDate = this.moveYear(this.viewDate, dir);
21036 } else if (e.shiftKey){
21037 newDate = this.moveMonth(this.date, dir);
21038 newViewDate = this.moveMonth(this.viewDate, dir);
21040 newDate = new Date(this.date);
21041 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21042 newViewDate = new Date(this.viewDate);
21043 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21045 if (this.dateWithinRange(newDate)){
21046 this.date = newDate;
21047 this.viewDate = newViewDate;
21048 this.setValue(this.formatDate(this.date));
21050 e.preventDefault();
21051 dateChanged = true;
21055 this.setValue(this.formatDate(this.date));
21057 e.preventDefault();
21060 this.setValue(this.formatDate(this.date));
21074 onClick: function(e)
21076 e.stopPropagation();
21077 e.preventDefault();
21079 var target = e.getTarget();
21081 if(target.nodeName.toLowerCase() === 'i'){
21082 target = Roo.get(target).dom.parentNode;
21085 var nodeName = target.nodeName;
21086 var className = target.className;
21087 var html = target.innerHTML;
21088 //Roo.log(nodeName);
21090 switch(nodeName.toLowerCase()) {
21092 switch(className) {
21098 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21099 switch(this.viewMode){
21101 this.viewDate = this.moveMonth(this.viewDate, dir);
21105 this.viewDate = this.moveYear(this.viewDate, dir);
21111 var date = new Date();
21112 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21114 this.setValue(this.formatDate(this.date));
21121 if (className.indexOf('disabled') < 0) {
21122 this.viewDate.setUTCDate(1);
21123 if (className.indexOf('month') > -1) {
21124 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21126 var year = parseInt(html, 10) || 0;
21127 this.viewDate.setUTCFullYear(year);
21131 if(this.singleMode){
21132 this.setValue(this.formatDate(this.viewDate));
21143 //Roo.log(className);
21144 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21145 var day = parseInt(html, 10) || 1;
21146 var year = this.viewDate.getUTCFullYear(),
21147 month = this.viewDate.getUTCMonth();
21149 if (className.indexOf('old') > -1) {
21156 } else if (className.indexOf('new') > -1) {
21164 //Roo.log([year,month,day]);
21165 this.date = this.UTCDate(year, month, day,0,0,0,0);
21166 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21168 //Roo.log(this.formatDate(this.date));
21169 this.setValue(this.formatDate(this.date));
21176 setStartDate: function(startDate)
21178 this.startDate = startDate || -Infinity;
21179 if (this.startDate !== -Infinity) {
21180 this.startDate = this.parseDate(this.startDate);
21183 this.updateNavArrows();
21186 setEndDate: function(endDate)
21188 this.endDate = endDate || Infinity;
21189 if (this.endDate !== Infinity) {
21190 this.endDate = this.parseDate(this.endDate);
21193 this.updateNavArrows();
21196 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21198 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21199 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21200 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21202 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21203 return parseInt(d, 10);
21206 this.updateNavArrows();
21209 updateNavArrows: function()
21211 if(this.singleMode){
21215 var d = new Date(this.viewDate),
21216 year = d.getUTCFullYear(),
21217 month = d.getUTCMonth();
21219 Roo.each(this.picker().select('.prev', true).elements, function(v){
21221 switch (this.viewMode) {
21224 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21230 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21237 Roo.each(this.picker().select('.next', true).elements, function(v){
21239 switch (this.viewMode) {
21242 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21248 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21256 moveMonth: function(date, dir)
21261 var new_date = new Date(date.valueOf()),
21262 day = new_date.getUTCDate(),
21263 month = new_date.getUTCMonth(),
21264 mag = Math.abs(dir),
21266 dir = dir > 0 ? 1 : -1;
21269 // If going back one month, make sure month is not current month
21270 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21272 return new_date.getUTCMonth() == month;
21274 // If going forward one month, make sure month is as expected
21275 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21277 return new_date.getUTCMonth() != new_month;
21279 new_month = month + dir;
21280 new_date.setUTCMonth(new_month);
21281 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21282 if (new_month < 0 || new_month > 11) {
21283 new_month = (new_month + 12) % 12;
21286 // For magnitudes >1, move one month at a time...
21287 for (var i=0; i<mag; i++) {
21288 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21289 new_date = this.moveMonth(new_date, dir);
21291 // ...then reset the day, keeping it in the new month
21292 new_month = new_date.getUTCMonth();
21293 new_date.setUTCDate(day);
21295 return new_month != new_date.getUTCMonth();
21298 // Common date-resetting loop -- if date is beyond end of month, make it
21301 new_date.setUTCDate(--day);
21302 new_date.setUTCMonth(new_month);
21307 moveYear: function(date, dir)
21309 return this.moveMonth(date, dir*12);
21312 dateWithinRange: function(date)
21314 return date >= this.startDate && date <= this.endDate;
21320 this.picker().remove();
21323 validateValue : function(value)
21325 if(this.getVisibilityEl().hasClass('hidden')){
21329 if(value.length < 1) {
21330 if(this.allowBlank){
21336 if(value.length < this.minLength){
21339 if(value.length > this.maxLength){
21343 var vt = Roo.form.VTypes;
21344 if(!vt[this.vtype](value, this)){
21348 if(typeof this.validator == "function"){
21349 var msg = this.validator(value);
21355 if(this.regex && !this.regex.test(value)){
21359 if(typeof(this.parseDate(value)) == 'undefined'){
21363 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21367 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21377 this.date = this.viewDate = '';
21379 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21384 Roo.apply(Roo.bootstrap.DateField, {
21395 html: '<i class="fa fa-arrow-left"/>'
21405 html: '<i class="fa fa-arrow-right"/>'
21447 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21448 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21449 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21450 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21451 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21464 navFnc: 'FullYear',
21469 navFnc: 'FullYear',
21474 Roo.apply(Roo.bootstrap.DateField, {
21478 cls: 'datepicker dropdown-menu roo-dynamic',
21482 cls: 'datepicker-days',
21486 cls: 'table-condensed',
21488 Roo.bootstrap.DateField.head,
21492 Roo.bootstrap.DateField.footer
21499 cls: 'datepicker-months',
21503 cls: 'table-condensed',
21505 Roo.bootstrap.DateField.head,
21506 Roo.bootstrap.DateField.content,
21507 Roo.bootstrap.DateField.footer
21514 cls: 'datepicker-years',
21518 cls: 'table-condensed',
21520 Roo.bootstrap.DateField.head,
21521 Roo.bootstrap.DateField.content,
21522 Roo.bootstrap.DateField.footer
21541 * @class Roo.bootstrap.TimeField
21542 * @extends Roo.bootstrap.Input
21543 * Bootstrap DateField class
21547 * Create a new TimeField
21548 * @param {Object} config The config object
21551 Roo.bootstrap.TimeField = function(config){
21552 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21556 * Fires when this field show.
21557 * @param {Roo.bootstrap.DateField} thisthis
21558 * @param {Mixed} date The date value
21563 * Fires when this field hide.
21564 * @param {Roo.bootstrap.DateField} this
21565 * @param {Mixed} date The date value
21570 * Fires when select a date.
21571 * @param {Roo.bootstrap.DateField} this
21572 * @param {Mixed} date The date value
21578 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21581 * @cfg {String} format
21582 * The default time format string which can be overriden for localization support. The format must be
21583 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21587 onRender: function(ct, position)
21590 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21592 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21594 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21596 this.pop = this.picker().select('>.datepicker-time',true).first();
21597 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21599 this.picker().on('mousedown', this.onMousedown, this);
21600 this.picker().on('click', this.onClick, this);
21602 this.picker().addClass('datepicker-dropdown');
21607 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21608 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21609 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21610 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21611 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21612 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21616 fireKey: function(e){
21617 if (!this.picker().isVisible()){
21618 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21624 e.preventDefault();
21632 this.onTogglePeriod();
21635 this.onIncrementMinutes();
21638 this.onDecrementMinutes();
21647 onClick: function(e) {
21648 e.stopPropagation();
21649 e.preventDefault();
21652 picker : function()
21654 return this.el.select('.datepicker', true).first();
21657 fillTime: function()
21659 var time = this.pop.select('tbody', true).first();
21661 time.dom.innerHTML = '';
21676 cls: 'hours-up glyphicon glyphicon-chevron-up'
21696 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21717 cls: 'timepicker-hour',
21732 cls: 'timepicker-minute',
21747 cls: 'btn btn-primary period',
21769 cls: 'hours-down glyphicon glyphicon-chevron-down'
21789 cls: 'minutes-down glyphicon glyphicon-chevron-down'
21807 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21814 var hours = this.time.getHours();
21815 var minutes = this.time.getMinutes();
21828 hours = hours - 12;
21832 hours = '0' + hours;
21836 minutes = '0' + minutes;
21839 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21840 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21841 this.pop.select('button', true).first().dom.innerHTML = period;
21847 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21849 var cls = ['bottom'];
21851 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21858 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21863 this.picker().addClass(cls.join('-'));
21867 Roo.each(cls, function(c){
21869 _this.picker().setTop(_this.inputEl().getHeight());
21873 _this.picker().setTop(0 - _this.picker().getHeight());
21878 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21882 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21889 onFocus : function()
21891 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21895 onBlur : function()
21897 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21903 this.picker().show();
21908 this.fireEvent('show', this, this.date);
21913 this.picker().hide();
21916 this.fireEvent('hide', this, this.date);
21919 setTime : function()
21922 this.setValue(this.time.format(this.format));
21924 this.fireEvent('select', this, this.date);
21929 onMousedown: function(e){
21930 e.stopPropagation();
21931 e.preventDefault();
21934 onIncrementHours: function()
21936 Roo.log('onIncrementHours');
21937 this.time = this.time.add(Date.HOUR, 1);
21942 onDecrementHours: function()
21944 Roo.log('onDecrementHours');
21945 this.time = this.time.add(Date.HOUR, -1);
21949 onIncrementMinutes: function()
21951 Roo.log('onIncrementMinutes');
21952 this.time = this.time.add(Date.MINUTE, 1);
21956 onDecrementMinutes: function()
21958 Roo.log('onDecrementMinutes');
21959 this.time = this.time.add(Date.MINUTE, -1);
21963 onTogglePeriod: function()
21965 Roo.log('onTogglePeriod');
21966 this.time = this.time.add(Date.HOUR, 12);
21973 Roo.apply(Roo.bootstrap.TimeField, {
22003 cls: 'btn btn-info ok',
22015 Roo.apply(Roo.bootstrap.TimeField, {
22019 cls: 'datepicker dropdown-menu',
22023 cls: 'datepicker-time',
22027 cls: 'table-condensed',
22029 Roo.bootstrap.TimeField.content,
22030 Roo.bootstrap.TimeField.footer
22049 * @class Roo.bootstrap.MonthField
22050 * @extends Roo.bootstrap.Input
22051 * Bootstrap MonthField class
22053 * @cfg {String} language default en
22056 * Create a new MonthField
22057 * @param {Object} config The config object
22060 Roo.bootstrap.MonthField = function(config){
22061 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22066 * Fires when this field show.
22067 * @param {Roo.bootstrap.MonthField} this
22068 * @param {Mixed} date The date value
22073 * Fires when this field hide.
22074 * @param {Roo.bootstrap.MonthField} this
22075 * @param {Mixed} date The date value
22080 * Fires when select a date.
22081 * @param {Roo.bootstrap.MonthField} this
22082 * @param {String} oldvalue The old value
22083 * @param {String} newvalue The new value
22089 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22091 onRender: function(ct, position)
22094 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22096 this.language = this.language || 'en';
22097 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22098 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22100 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22101 this.isInline = false;
22102 this.isInput = true;
22103 this.component = this.el.select('.add-on', true).first() || false;
22104 this.component = (this.component && this.component.length === 0) ? false : this.component;
22105 this.hasInput = this.component && this.inputEL().length;
22107 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22109 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22111 this.picker().on('mousedown', this.onMousedown, this);
22112 this.picker().on('click', this.onClick, this);
22114 this.picker().addClass('datepicker-dropdown');
22116 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22117 v.setStyle('width', '189px');
22124 if(this.isInline) {
22130 setValue: function(v, suppressEvent)
22132 var o = this.getValue();
22134 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22138 if(suppressEvent !== true){
22139 this.fireEvent('select', this, o, v);
22144 getValue: function()
22149 onClick: function(e)
22151 e.stopPropagation();
22152 e.preventDefault();
22154 var target = e.getTarget();
22156 if(target.nodeName.toLowerCase() === 'i'){
22157 target = Roo.get(target).dom.parentNode;
22160 var nodeName = target.nodeName;
22161 var className = target.className;
22162 var html = target.innerHTML;
22164 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22168 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22170 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22176 picker : function()
22178 return this.pickerEl;
22181 fillMonths: function()
22184 var months = this.picker().select('>.datepicker-months td', true).first();
22186 months.dom.innerHTML = '';
22192 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22195 months.createChild(month);
22204 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22205 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22208 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22209 e.removeClass('active');
22211 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22212 e.addClass('active');
22219 if(this.isInline) {
22223 this.picker().removeClass(['bottom', 'top']);
22225 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22227 * place to the top of element!
22231 this.picker().addClass('top');
22232 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22237 this.picker().addClass('bottom');
22239 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22242 onFocus : function()
22244 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22248 onBlur : function()
22250 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22252 var d = this.inputEl().getValue();
22261 this.picker().show();
22262 this.picker().select('>.datepicker-months', true).first().show();
22266 this.fireEvent('show', this, this.date);
22271 if(this.isInline) {
22274 this.picker().hide();
22275 this.fireEvent('hide', this, this.date);
22279 onMousedown: function(e)
22281 e.stopPropagation();
22282 e.preventDefault();
22287 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22291 fireKey: function(e)
22293 if (!this.picker().isVisible()){
22294 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22305 e.preventDefault();
22309 dir = e.keyCode == 37 ? -1 : 1;
22311 this.vIndex = this.vIndex + dir;
22313 if(this.vIndex < 0){
22317 if(this.vIndex > 11){
22321 if(isNaN(this.vIndex)){
22325 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22331 dir = e.keyCode == 38 ? -1 : 1;
22333 this.vIndex = this.vIndex + dir * 4;
22335 if(this.vIndex < 0){
22339 if(this.vIndex > 11){
22343 if(isNaN(this.vIndex)){
22347 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22352 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22353 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22357 e.preventDefault();
22360 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22361 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22377 this.picker().remove();
22382 Roo.apply(Roo.bootstrap.MonthField, {
22401 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22402 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22407 Roo.apply(Roo.bootstrap.MonthField, {
22411 cls: 'datepicker dropdown-menu roo-dynamic',
22415 cls: 'datepicker-months',
22419 cls: 'table-condensed',
22421 Roo.bootstrap.DateField.content
22441 * @class Roo.bootstrap.CheckBox
22442 * @extends Roo.bootstrap.Input
22443 * Bootstrap CheckBox class
22445 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22446 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22447 * @cfg {String} boxLabel The text that appears beside the checkbox
22448 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22449 * @cfg {Boolean} checked initnal the element
22450 * @cfg {Boolean} inline inline the element (default false)
22451 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22452 * @cfg {String} tooltip label tooltip
22455 * Create a new CheckBox
22456 * @param {Object} config The config object
22459 Roo.bootstrap.CheckBox = function(config){
22460 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22465 * Fires when the element is checked or unchecked.
22466 * @param {Roo.bootstrap.CheckBox} this This input
22467 * @param {Boolean} checked The new checked value
22472 * Fires when the element is click.
22473 * @param {Roo.bootstrap.CheckBox} this This input
22480 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22482 inputType: 'checkbox',
22491 // checkbox success does not make any sense really..
22496 getAutoCreate : function()
22498 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22504 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22507 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22513 type : this.inputType,
22514 value : this.inputValue,
22515 cls : 'roo-' + this.inputType, //'form-box',
22516 placeholder : this.placeholder || ''
22520 if(this.inputType != 'radio'){
22524 cls : 'roo-hidden-value',
22525 value : this.checked ? this.inputValue : this.valueOff
22530 if (this.weight) { // Validity check?
22531 cfg.cls += " " + this.inputType + "-" + this.weight;
22534 if (this.disabled) {
22535 input.disabled=true;
22539 input.checked = this.checked;
22544 input.name = this.name;
22546 if(this.inputType != 'radio'){
22547 hidden.name = this.name;
22548 input.name = '_hidden_' + this.name;
22553 input.cls += ' input-' + this.size;
22558 ['xs','sm','md','lg'].map(function(size){
22559 if (settings[size]) {
22560 cfg.cls += ' col-' + size + '-' + settings[size];
22564 var inputblock = input;
22566 if (this.before || this.after) {
22569 cls : 'input-group',
22574 inputblock.cn.push({
22576 cls : 'input-group-addon',
22581 inputblock.cn.push(input);
22583 if(this.inputType != 'radio'){
22584 inputblock.cn.push(hidden);
22588 inputblock.cn.push({
22590 cls : 'input-group-addon',
22596 var boxLabelCfg = false;
22602 //'for': id, // box label is handled by onclick - so no for...
22604 html: this.boxLabel
22607 boxLabelCfg.tooltip = this.tooltip;
22613 if (align ==='left' && this.fieldLabel.length) {
22614 // Roo.log("left and has label");
22619 cls : 'control-label',
22620 html : this.fieldLabel
22631 cfg.cn[1].cn.push(boxLabelCfg);
22634 if(this.labelWidth > 12){
22635 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22638 if(this.labelWidth < 13 && this.labelmd == 0){
22639 this.labelmd = this.labelWidth;
22642 if(this.labellg > 0){
22643 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22644 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22647 if(this.labelmd > 0){
22648 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22649 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22652 if(this.labelsm > 0){
22653 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22654 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22657 if(this.labelxs > 0){
22658 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22659 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22662 } else if ( this.fieldLabel.length) {
22663 // Roo.log(" label");
22667 tag: this.boxLabel ? 'span' : 'label',
22669 cls: 'control-label box-input-label',
22670 //cls : 'input-group-addon',
22671 html : this.fieldLabel
22678 cfg.cn.push(boxLabelCfg);
22683 // Roo.log(" no label && no align");
22684 cfg.cn = [ inputblock ] ;
22686 cfg.cn.push(boxLabelCfg);
22694 if(this.inputType != 'radio'){
22695 cfg.cn.push(hidden);
22703 * return the real input element.
22705 inputEl: function ()
22707 return this.el.select('input.roo-' + this.inputType,true).first();
22709 hiddenEl: function ()
22711 return this.el.select('input.roo-hidden-value',true).first();
22714 labelEl: function()
22716 return this.el.select('label.control-label',true).first();
22718 /* depricated... */
22722 return this.labelEl();
22725 boxLabelEl: function()
22727 return this.el.select('label.box-label',true).first();
22730 initEvents : function()
22732 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22734 this.inputEl().on('click', this.onClick, this);
22736 if (this.boxLabel) {
22737 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22740 this.startValue = this.getValue();
22743 Roo.bootstrap.CheckBox.register(this);
22747 onClick : function(e)
22749 if(this.fireEvent('click', this, e) !== false){
22750 this.setChecked(!this.checked);
22755 setChecked : function(state,suppressEvent)
22757 this.startValue = this.getValue();
22759 if(this.inputType == 'radio'){
22761 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22762 e.dom.checked = false;
22765 this.inputEl().dom.checked = true;
22767 this.inputEl().dom.value = this.inputValue;
22769 if(suppressEvent !== true){
22770 this.fireEvent('check', this, true);
22778 this.checked = state;
22780 this.inputEl().dom.checked = state;
22783 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22785 if(suppressEvent !== true){
22786 this.fireEvent('check', this, state);
22792 getValue : function()
22794 if(this.inputType == 'radio'){
22795 return this.getGroupValue();
22798 return this.hiddenEl().dom.value;
22802 getGroupValue : function()
22804 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22808 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22811 setValue : function(v,suppressEvent)
22813 if(this.inputType == 'radio'){
22814 this.setGroupValue(v, suppressEvent);
22818 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22823 setGroupValue : function(v, suppressEvent)
22825 this.startValue = this.getValue();
22827 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22828 e.dom.checked = false;
22830 if(e.dom.value == v){
22831 e.dom.checked = true;
22835 if(suppressEvent !== true){
22836 this.fireEvent('check', this, true);
22844 validate : function()
22846 if(this.getVisibilityEl().hasClass('hidden')){
22852 (this.inputType == 'radio' && this.validateRadio()) ||
22853 (this.inputType == 'checkbox' && this.validateCheckbox())
22859 this.markInvalid();
22863 validateRadio : function()
22865 if(this.getVisibilityEl().hasClass('hidden')){
22869 if(this.allowBlank){
22875 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22876 if(!e.dom.checked){
22888 validateCheckbox : function()
22891 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22892 //return (this.getValue() == this.inputValue) ? true : false;
22895 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22903 for(var i in group){
22904 if(group[i].el.isVisible(true)){
22912 for(var i in group){
22917 r = (group[i].getValue() == group[i].inputValue) ? true : false;
22924 * Mark this field as valid
22926 markValid : function()
22930 this.fireEvent('valid', this);
22932 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22935 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22942 if(this.inputType == 'radio'){
22943 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22944 var fg = e.findParent('.form-group', false, true);
22945 if (Roo.bootstrap.version == 3) {
22946 fg.removeClass([_this.invalidClass, _this.validClass]);
22947 fg.addClass(_this.validClass);
22949 fg.removeClass(['is-valid', 'is-invalid']);
22950 fg.addClass('is-valid');
22958 var fg = this.el.findParent('.form-group', false, true);
22959 if (Roo.bootstrap.version == 3) {
22960 fg.removeClass([this.invalidClass, this.validClass]);
22961 fg.addClass(this.validClass);
22963 fg.removeClass(['is-valid', 'is-invalid']);
22964 fg.addClass('is-valid');
22969 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22975 for(var i in group){
22976 var fg = group[i].el.findParent('.form-group', false, true);
22977 if (Roo.bootstrap.version == 3) {
22978 fg.removeClass([this.invalidClass, this.validClass]);
22979 fg.addClass(this.validClass);
22981 fg.removeClass(['is-valid', 'is-invalid']);
22982 fg.addClass('is-valid');
22988 * Mark this field as invalid
22989 * @param {String} msg The validation message
22991 markInvalid : function(msg)
22993 if(this.allowBlank){
22999 this.fireEvent('invalid', this, msg);
23001 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23004 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23008 label.markInvalid();
23011 if(this.inputType == 'radio'){
23013 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23014 var fg = e.findParent('.form-group', false, true);
23015 if (Roo.bootstrap.version == 3) {
23016 fg.removeClass([_this.invalidClass, _this.validClass]);
23017 fg.addClass(_this.invalidClass);
23019 fg.removeClass(['is-invalid', 'is-valid']);
23020 fg.addClass('is-invalid');
23028 var fg = this.el.findParent('.form-group', false, true);
23029 if (Roo.bootstrap.version == 3) {
23030 fg.removeClass([_this.invalidClass, _this.validClass]);
23031 fg.addClass(_this.invalidClass);
23033 fg.removeClass(['is-invalid', 'is-valid']);
23034 fg.addClass('is-invalid');
23039 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23045 for(var i in group){
23046 var fg = group[i].el.findParent('.form-group', false, true);
23047 if (Roo.bootstrap.version == 3) {
23048 fg.removeClass([_this.invalidClass, _this.validClass]);
23049 fg.addClass(_this.invalidClass);
23051 fg.removeClass(['is-invalid', 'is-valid']);
23052 fg.addClass('is-invalid');
23058 clearInvalid : function()
23060 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23062 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23064 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23066 if (label && label.iconEl) {
23067 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23068 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23072 disable : function()
23074 if(this.inputType != 'radio'){
23075 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23082 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23083 _this.getActionEl().addClass(this.disabledClass);
23084 e.dom.disabled = true;
23088 this.disabled = true;
23089 this.fireEvent("disable", this);
23093 enable : function()
23095 if(this.inputType != 'radio'){
23096 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23103 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23104 _this.getActionEl().removeClass(this.disabledClass);
23105 e.dom.disabled = false;
23109 this.disabled = false;
23110 this.fireEvent("enable", this);
23114 setBoxLabel : function(v)
23119 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23125 Roo.apply(Roo.bootstrap.CheckBox, {
23130 * register a CheckBox Group
23131 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23133 register : function(checkbox)
23135 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23136 this.groups[checkbox.groupId] = {};
23139 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23143 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23147 * fetch a CheckBox Group based on the group ID
23148 * @param {string} the group ID
23149 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23151 get: function(groupId) {
23152 if (typeof(this.groups[groupId]) == 'undefined') {
23156 return this.groups[groupId] ;
23169 * @class Roo.bootstrap.Radio
23170 * @extends Roo.bootstrap.Component
23171 * Bootstrap Radio class
23172 * @cfg {String} boxLabel - the label associated
23173 * @cfg {String} value - the value of radio
23176 * Create a new Radio
23177 * @param {Object} config The config object
23179 Roo.bootstrap.Radio = function(config){
23180 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23184 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23190 getAutoCreate : function()
23194 cls : 'form-group radio',
23199 html : this.boxLabel
23207 initEvents : function()
23209 this.parent().register(this);
23211 this.el.on('click', this.onClick, this);
23215 onClick : function(e)
23217 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23218 this.setChecked(true);
23222 setChecked : function(state, suppressEvent)
23224 this.parent().setValue(this.value, suppressEvent);
23228 setBoxLabel : function(v)
23233 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23248 * @class Roo.bootstrap.SecurePass
23249 * @extends Roo.bootstrap.Input
23250 * Bootstrap SecurePass class
23254 * Create a new SecurePass
23255 * @param {Object} config The config object
23258 Roo.bootstrap.SecurePass = function (config) {
23259 // these go here, so the translation tool can replace them..
23261 PwdEmpty: "Please type a password, and then retype it to confirm.",
23262 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23263 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23264 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23265 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23266 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23267 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23268 TooWeak: "Your password is Too Weak."
23270 this.meterLabel = "Password strength:";
23271 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23272 this.meterClass = [
23273 "roo-password-meter-tooweak",
23274 "roo-password-meter-weak",
23275 "roo-password-meter-medium",
23276 "roo-password-meter-strong",
23277 "roo-password-meter-grey"
23282 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23285 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23287 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23289 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23290 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23291 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23292 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23293 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23294 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23295 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23305 * @cfg {String/Object} Label for the strength meter (defaults to
23306 * 'Password strength:')
23311 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23312 * ['Weak', 'Medium', 'Strong'])
23315 pwdStrengths: false,
23328 initEvents: function ()
23330 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23332 if (this.el.is('input[type=password]') && Roo.isSafari) {
23333 this.el.on('keydown', this.SafariOnKeyDown, this);
23336 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23339 onRender: function (ct, position)
23341 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23342 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23343 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23345 this.trigger.createChild({
23350 cls: 'roo-password-meter-grey col-xs-12',
23353 //width: this.meterWidth + 'px'
23357 cls: 'roo-password-meter-text'
23363 if (this.hideTrigger) {
23364 this.trigger.setDisplayed(false);
23366 this.setSize(this.width || '', this.height || '');
23369 onDestroy: function ()
23371 if (this.trigger) {
23372 this.trigger.removeAllListeners();
23373 this.trigger.remove();
23376 this.wrap.remove();
23378 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23381 checkStrength: function ()
23383 var pwd = this.inputEl().getValue();
23384 if (pwd == this._lastPwd) {
23389 if (this.ClientSideStrongPassword(pwd)) {
23391 } else if (this.ClientSideMediumPassword(pwd)) {
23393 } else if (this.ClientSideWeakPassword(pwd)) {
23399 Roo.log('strength1: ' + strength);
23401 //var pm = this.trigger.child('div/div/div').dom;
23402 var pm = this.trigger.child('div/div');
23403 pm.removeClass(this.meterClass);
23404 pm.addClass(this.meterClass[strength]);
23407 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23409 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23411 this._lastPwd = pwd;
23415 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23417 this._lastPwd = '';
23419 var pm = this.trigger.child('div/div');
23420 pm.removeClass(this.meterClass);
23421 pm.addClass('roo-password-meter-grey');
23424 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23427 this.inputEl().dom.type='password';
23430 validateValue: function (value)
23433 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23436 if (value.length == 0) {
23437 if (this.allowBlank) {
23438 this.clearInvalid();
23442 this.markInvalid(this.errors.PwdEmpty);
23443 this.errorMsg = this.errors.PwdEmpty;
23451 if ('[\x21-\x7e]*'.match(value)) {
23452 this.markInvalid(this.errors.PwdBadChar);
23453 this.errorMsg = this.errors.PwdBadChar;
23456 if (value.length < 6) {
23457 this.markInvalid(this.errors.PwdShort);
23458 this.errorMsg = this.errors.PwdShort;
23461 if (value.length > 16) {
23462 this.markInvalid(this.errors.PwdLong);
23463 this.errorMsg = this.errors.PwdLong;
23467 if (this.ClientSideStrongPassword(value)) {
23469 } else if (this.ClientSideMediumPassword(value)) {
23471 } else if (this.ClientSideWeakPassword(value)) {
23478 if (strength < 2) {
23479 //this.markInvalid(this.errors.TooWeak);
23480 this.errorMsg = this.errors.TooWeak;
23485 console.log('strength2: ' + strength);
23487 //var pm = this.trigger.child('div/div/div').dom;
23489 var pm = this.trigger.child('div/div');
23490 pm.removeClass(this.meterClass);
23491 pm.addClass(this.meterClass[strength]);
23493 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23495 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23497 this.errorMsg = '';
23501 CharacterSetChecks: function (type)
23504 this.fResult = false;
23507 isctype: function (character, type)
23510 case this.kCapitalLetter:
23511 if (character >= 'A' && character <= 'Z') {
23516 case this.kSmallLetter:
23517 if (character >= 'a' && character <= 'z') {
23523 if (character >= '0' && character <= '9') {
23528 case this.kPunctuation:
23529 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23540 IsLongEnough: function (pwd, size)
23542 return !(pwd == null || isNaN(size) || pwd.length < size);
23545 SpansEnoughCharacterSets: function (word, nb)
23547 if (!this.IsLongEnough(word, nb))
23552 var characterSetChecks = new Array(
23553 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23554 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23557 for (var index = 0; index < word.length; ++index) {
23558 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23559 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23560 characterSetChecks[nCharSet].fResult = true;
23567 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23568 if (characterSetChecks[nCharSet].fResult) {
23573 if (nCharSets < nb) {
23579 ClientSideStrongPassword: function (pwd)
23581 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23584 ClientSideMediumPassword: function (pwd)
23586 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23589 ClientSideWeakPassword: function (pwd)
23591 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23594 })//<script type="text/javascript">
23597 * Based Ext JS Library 1.1.1
23598 * Copyright(c) 2006-2007, Ext JS, LLC.
23604 * @class Roo.HtmlEditorCore
23605 * @extends Roo.Component
23606 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23608 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23611 Roo.HtmlEditorCore = function(config){
23614 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23619 * @event initialize
23620 * Fires when the editor is fully initialized (including the iframe)
23621 * @param {Roo.HtmlEditorCore} this
23626 * Fires when the editor is first receives the focus. Any insertion must wait
23627 * until after this event.
23628 * @param {Roo.HtmlEditorCore} this
23632 * @event beforesync
23633 * Fires before the textarea is updated with content from the editor iframe. Return false
23634 * to cancel the sync.
23635 * @param {Roo.HtmlEditorCore} this
23636 * @param {String} html
23640 * @event beforepush
23641 * Fires before the iframe editor is updated with content from the textarea. Return false
23642 * to cancel the push.
23643 * @param {Roo.HtmlEditorCore} this
23644 * @param {String} html
23649 * Fires when the textarea is updated with content from the editor iframe.
23650 * @param {Roo.HtmlEditorCore} this
23651 * @param {String} html
23656 * Fires when the iframe editor is updated with content from the textarea.
23657 * @param {Roo.HtmlEditorCore} this
23658 * @param {String} html
23663 * @event editorevent
23664 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23665 * @param {Roo.HtmlEditorCore} this
23671 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23673 // defaults : white / black...
23674 this.applyBlacklists();
23681 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23685 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23691 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23696 * @cfg {Number} height (in pixels)
23700 * @cfg {Number} width (in pixels)
23705 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23708 stylesheets: false,
23713 // private properties
23714 validationEvent : false,
23716 initialized : false,
23718 sourceEditMode : false,
23719 onFocus : Roo.emptyFn,
23721 hideMode:'offsets',
23725 // blacklist + whitelisted elements..
23732 * Protected method that will not generally be called directly. It
23733 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23734 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23736 getDocMarkup : function(){
23740 // inherit styels from page...??
23741 if (this.stylesheets === false) {
23743 Roo.get(document.head).select('style').each(function(node) {
23744 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23747 Roo.get(document.head).select('link').each(function(node) {
23748 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23751 } else if (!this.stylesheets.length) {
23753 st = '<style type="text/css">' +
23754 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23757 st = '<style type="text/css">' +
23762 st += '<style type="text/css">' +
23763 'IMG { cursor: pointer } ' +
23766 var cls = 'roo-htmleditor-body';
23768 if(this.bodyCls.length){
23769 cls += ' ' + this.bodyCls;
23772 return '<html><head>' + st +
23773 //<style type="text/css">' +
23774 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23776 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23780 onRender : function(ct, position)
23783 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23784 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23787 this.el.dom.style.border = '0 none';
23788 this.el.dom.setAttribute('tabIndex', -1);
23789 this.el.addClass('x-hidden hide');
23793 if(Roo.isIE){ // fix IE 1px bogus margin
23794 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23798 this.frameId = Roo.id();
23802 var iframe = this.owner.wrap.createChild({
23804 cls: 'form-control', // bootstrap..
23806 name: this.frameId,
23807 frameBorder : 'no',
23808 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
23813 this.iframe = iframe.dom;
23815 this.assignDocWin();
23817 this.doc.designMode = 'on';
23820 this.doc.write(this.getDocMarkup());
23824 var task = { // must defer to wait for browser to be ready
23826 //console.log("run task?" + this.doc.readyState);
23827 this.assignDocWin();
23828 if(this.doc.body || this.doc.readyState == 'complete'){
23830 this.doc.designMode="on";
23834 Roo.TaskMgr.stop(task);
23835 this.initEditor.defer(10, this);
23842 Roo.TaskMgr.start(task);
23847 onResize : function(w, h)
23849 Roo.log('resize: ' +w + ',' + h );
23850 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23854 if(typeof w == 'number'){
23856 this.iframe.style.width = w + 'px';
23858 if(typeof h == 'number'){
23860 this.iframe.style.height = h + 'px';
23862 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23869 * Toggles the editor between standard and source edit mode.
23870 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23872 toggleSourceEdit : function(sourceEditMode){
23874 this.sourceEditMode = sourceEditMode === true;
23876 if(this.sourceEditMode){
23878 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
23881 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23882 //this.iframe.className = '';
23885 //this.setSize(this.owner.wrap.getSize());
23886 //this.fireEvent('editmodechange', this, this.sourceEditMode);
23893 * Protected method that will not generally be called directly. If you need/want
23894 * custom HTML cleanup, this is the method you should override.
23895 * @param {String} html The HTML to be cleaned
23896 * return {String} The cleaned HTML
23898 cleanHtml : function(html){
23899 html = String(html);
23900 if(html.length > 5){
23901 if(Roo.isSafari){ // strip safari nonsense
23902 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23905 if(html == ' '){
23912 * HTML Editor -> Textarea
23913 * Protected method that will not generally be called directly. Syncs the contents
23914 * of the editor iframe with the textarea.
23916 syncValue : function(){
23917 if(this.initialized){
23918 var bd = (this.doc.body || this.doc.documentElement);
23919 //this.cleanUpPaste(); -- this is done else where and causes havoc..
23920 var html = bd.innerHTML;
23922 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23923 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23925 html = '<div style="'+m[0]+'">' + html + '</div>';
23928 html = this.cleanHtml(html);
23929 // fix up the special chars.. normaly like back quotes in word...
23930 // however we do not want to do this with chinese..
23931 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23933 var cc = match.charCodeAt();
23935 // Get the character value, handling surrogate pairs
23936 if (match.length == 2) {
23937 // It's a surrogate pair, calculate the Unicode code point
23938 var high = match.charCodeAt(0) - 0xD800;
23939 var low = match.charCodeAt(1) - 0xDC00;
23940 cc = (high * 0x400) + low + 0x10000;
23942 (cc >= 0x4E00 && cc < 0xA000 ) ||
23943 (cc >= 0x3400 && cc < 0x4E00 ) ||
23944 (cc >= 0xf900 && cc < 0xfb00 )
23949 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23950 return "&#" + cc + ";";
23957 if(this.owner.fireEvent('beforesync', this, html) !== false){
23958 this.el.dom.value = html;
23959 this.owner.fireEvent('sync', this, html);
23965 * Protected method that will not generally be called directly. Pushes the value of the textarea
23966 * into the iframe editor.
23968 pushValue : function(){
23969 if(this.initialized){
23970 var v = this.el.dom.value.trim();
23972 // if(v.length < 1){
23976 if(this.owner.fireEvent('beforepush', this, v) !== false){
23977 var d = (this.doc.body || this.doc.documentElement);
23979 this.cleanUpPaste();
23980 this.el.dom.value = d.innerHTML;
23981 this.owner.fireEvent('push', this, v);
23987 deferFocus : function(){
23988 this.focus.defer(10, this);
23992 focus : function(){
23993 if(this.win && !this.sourceEditMode){
24000 assignDocWin: function()
24002 var iframe = this.iframe;
24005 this.doc = iframe.contentWindow.document;
24006 this.win = iframe.contentWindow;
24008 // if (!Roo.get(this.frameId)) {
24011 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24012 // this.win = Roo.get(this.frameId).dom.contentWindow;
24014 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24018 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24019 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24024 initEditor : function(){
24025 //console.log("INIT EDITOR");
24026 this.assignDocWin();
24030 this.doc.designMode="on";
24032 this.doc.write(this.getDocMarkup());
24035 var dbody = (this.doc.body || this.doc.documentElement);
24036 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24037 // this copies styles from the containing element into thsi one..
24038 // not sure why we need all of this..
24039 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24041 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24042 //ss['background-attachment'] = 'fixed'; // w3c
24043 dbody.bgProperties = 'fixed'; // ie
24044 //Roo.DomHelper.applyStyles(dbody, ss);
24045 Roo.EventManager.on(this.doc, {
24046 //'mousedown': this.onEditorEvent,
24047 'mouseup': this.onEditorEvent,
24048 'dblclick': this.onEditorEvent,
24049 'click': this.onEditorEvent,
24050 'keyup': this.onEditorEvent,
24055 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24057 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24058 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24060 this.initialized = true;
24062 this.owner.fireEvent('initialize', this);
24067 onDestroy : function(){
24073 //for (var i =0; i < this.toolbars.length;i++) {
24074 // // fixme - ask toolbars for heights?
24075 // this.toolbars[i].onDestroy();
24078 //this.wrap.dom.innerHTML = '';
24079 //this.wrap.remove();
24084 onFirstFocus : function(){
24086 this.assignDocWin();
24089 this.activated = true;
24092 if(Roo.isGecko){ // prevent silly gecko errors
24094 var s = this.win.getSelection();
24095 if(!s.focusNode || s.focusNode.nodeType != 3){
24096 var r = s.getRangeAt(0);
24097 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24102 this.execCmd('useCSS', true);
24103 this.execCmd('styleWithCSS', false);
24106 this.owner.fireEvent('activate', this);
24110 adjustFont: function(btn){
24111 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24112 //if(Roo.isSafari){ // safari
24115 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24116 if(Roo.isSafari){ // safari
24117 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24118 v = (v < 10) ? 10 : v;
24119 v = (v > 48) ? 48 : v;
24120 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24125 v = Math.max(1, v+adjust);
24127 this.execCmd('FontSize', v );
24130 onEditorEvent : function(e)
24132 this.owner.fireEvent('editorevent', this, e);
24133 // this.updateToolbar();
24134 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24137 insertTag : function(tg)
24139 // could be a bit smarter... -> wrap the current selected tRoo..
24140 if (tg.toLowerCase() == 'span' ||
24141 tg.toLowerCase() == 'code' ||
24142 tg.toLowerCase() == 'sup' ||
24143 tg.toLowerCase() == 'sub'
24146 range = this.createRange(this.getSelection());
24147 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24148 wrappingNode.appendChild(range.extractContents());
24149 range.insertNode(wrappingNode);
24156 this.execCmd("formatblock", tg);
24160 insertText : function(txt)
24164 var range = this.createRange();
24165 range.deleteContents();
24166 //alert(Sender.getAttribute('label'));
24168 range.insertNode(this.doc.createTextNode(txt));
24174 * Executes a Midas editor command on the editor document and performs necessary focus and
24175 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24176 * @param {String} cmd The Midas command
24177 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24179 relayCmd : function(cmd, value){
24181 this.execCmd(cmd, value);
24182 this.owner.fireEvent('editorevent', this);
24183 //this.updateToolbar();
24184 this.owner.deferFocus();
24188 * Executes a Midas editor command directly on the editor document.
24189 * For visual commands, you should use {@link #relayCmd} instead.
24190 * <b>This should only be called after the editor is initialized.</b>
24191 * @param {String} cmd The Midas command
24192 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24194 execCmd : function(cmd, value){
24195 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24202 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24204 * @param {String} text | dom node..
24206 insertAtCursor : function(text)
24209 if(!this.activated){
24215 var r = this.doc.selection.createRange();
24226 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24230 // from jquery ui (MIT licenced)
24232 var win = this.win;
24234 if (win.getSelection && win.getSelection().getRangeAt) {
24235 range = win.getSelection().getRangeAt(0);
24236 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24237 range.insertNode(node);
24238 } else if (win.document.selection && win.document.selection.createRange) {
24239 // no firefox support
24240 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24241 win.document.selection.createRange().pasteHTML(txt);
24243 // no firefox support
24244 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24245 this.execCmd('InsertHTML', txt);
24254 mozKeyPress : function(e){
24256 var c = e.getCharCode(), cmd;
24259 c = String.fromCharCode(c).toLowerCase();
24273 this.cleanUpPaste.defer(100, this);
24281 e.preventDefault();
24289 fixKeys : function(){ // load time branching for fastest keydown performance
24291 return function(e){
24292 var k = e.getKey(), r;
24295 r = this.doc.selection.createRange();
24298 r.pasteHTML('    ');
24305 r = this.doc.selection.createRange();
24307 var target = r.parentElement();
24308 if(!target || target.tagName.toLowerCase() != 'li'){
24310 r.pasteHTML('<br />');
24316 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24317 this.cleanUpPaste.defer(100, this);
24323 }else if(Roo.isOpera){
24324 return function(e){
24325 var k = e.getKey();
24329 this.execCmd('InsertHTML','    ');
24332 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24333 this.cleanUpPaste.defer(100, this);
24338 }else if(Roo.isSafari){
24339 return function(e){
24340 var k = e.getKey();
24344 this.execCmd('InsertText','\t');
24348 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24349 this.cleanUpPaste.defer(100, this);
24357 getAllAncestors: function()
24359 var p = this.getSelectedNode();
24362 a.push(p); // push blank onto stack..
24363 p = this.getParentElement();
24367 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24371 a.push(this.doc.body);
24375 lastSelNode : false,
24378 getSelection : function()
24380 this.assignDocWin();
24381 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24384 getSelectedNode: function()
24386 // this may only work on Gecko!!!
24388 // should we cache this!!!!
24393 var range = this.createRange(this.getSelection()).cloneRange();
24396 var parent = range.parentElement();
24398 var testRange = range.duplicate();
24399 testRange.moveToElementText(parent);
24400 if (testRange.inRange(range)) {
24403 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24406 parent = parent.parentElement;
24411 // is ancestor a text element.
24412 var ac = range.commonAncestorContainer;
24413 if (ac.nodeType == 3) {
24414 ac = ac.parentNode;
24417 var ar = ac.childNodes;
24420 var other_nodes = [];
24421 var has_other_nodes = false;
24422 for (var i=0;i<ar.length;i++) {
24423 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24426 // fullly contained node.
24428 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24433 // probably selected..
24434 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24435 other_nodes.push(ar[i]);
24439 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24444 has_other_nodes = true;
24446 if (!nodes.length && other_nodes.length) {
24447 nodes= other_nodes;
24449 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24455 createRange: function(sel)
24457 // this has strange effects when using with
24458 // top toolbar - not sure if it's a great idea.
24459 //this.editor.contentWindow.focus();
24460 if (typeof sel != "undefined") {
24462 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24464 return this.doc.createRange();
24467 return this.doc.createRange();
24470 getParentElement: function()
24473 this.assignDocWin();
24474 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24476 var range = this.createRange(sel);
24479 var p = range.commonAncestorContainer;
24480 while (p.nodeType == 3) { // text node
24491 * Range intersection.. the hard stuff...
24495 * [ -- selected range --- ]
24499 * if end is before start or hits it. fail.
24500 * if start is after end or hits it fail.
24502 * if either hits (but other is outside. - then it's not
24508 // @see http://www.thismuchiknow.co.uk/?p=64.
24509 rangeIntersectsNode : function(range, node)
24511 var nodeRange = node.ownerDocument.createRange();
24513 nodeRange.selectNode(node);
24515 nodeRange.selectNodeContents(node);
24518 var rangeStartRange = range.cloneRange();
24519 rangeStartRange.collapse(true);
24521 var rangeEndRange = range.cloneRange();
24522 rangeEndRange.collapse(false);
24524 var nodeStartRange = nodeRange.cloneRange();
24525 nodeStartRange.collapse(true);
24527 var nodeEndRange = nodeRange.cloneRange();
24528 nodeEndRange.collapse(false);
24530 return rangeStartRange.compareBoundaryPoints(
24531 Range.START_TO_START, nodeEndRange) == -1 &&
24532 rangeEndRange.compareBoundaryPoints(
24533 Range.START_TO_START, nodeStartRange) == 1;
24537 rangeCompareNode : function(range, node)
24539 var nodeRange = node.ownerDocument.createRange();
24541 nodeRange.selectNode(node);
24543 nodeRange.selectNodeContents(node);
24547 range.collapse(true);
24549 nodeRange.collapse(true);
24551 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24552 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24554 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24556 var nodeIsBefore = ss == 1;
24557 var nodeIsAfter = ee == -1;
24559 if (nodeIsBefore && nodeIsAfter) {
24562 if (!nodeIsBefore && nodeIsAfter) {
24563 return 1; //right trailed.
24566 if (nodeIsBefore && !nodeIsAfter) {
24567 return 2; // left trailed.
24573 // private? - in a new class?
24574 cleanUpPaste : function()
24576 // cleans up the whole document..
24577 Roo.log('cleanuppaste');
24579 this.cleanUpChildren(this.doc.body);
24580 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24581 if (clean != this.doc.body.innerHTML) {
24582 this.doc.body.innerHTML = clean;
24587 cleanWordChars : function(input) {// change the chars to hex code
24588 var he = Roo.HtmlEditorCore;
24590 var output = input;
24591 Roo.each(he.swapCodes, function(sw) {
24592 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24594 output = output.replace(swapper, sw[1]);
24601 cleanUpChildren : function (n)
24603 if (!n.childNodes.length) {
24606 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24607 this.cleanUpChild(n.childNodes[i]);
24614 cleanUpChild : function (node)
24617 //console.log(node);
24618 if (node.nodeName == "#text") {
24619 // clean up silly Windows -- stuff?
24622 if (node.nodeName == "#comment") {
24623 node.parentNode.removeChild(node);
24624 // clean up silly Windows -- stuff?
24627 var lcname = node.tagName.toLowerCase();
24628 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24629 // whitelist of tags..
24631 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24633 node.parentNode.removeChild(node);
24638 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24640 // spans with no attributes - just remove them..
24641 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24642 remove_keep_children = true;
24645 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24646 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24648 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24649 // remove_keep_children = true;
24652 if (remove_keep_children) {
24653 this.cleanUpChildren(node);
24654 // inserts everything just before this node...
24655 while (node.childNodes.length) {
24656 var cn = node.childNodes[0];
24657 node.removeChild(cn);
24658 node.parentNode.insertBefore(cn, node);
24660 node.parentNode.removeChild(node);
24664 if (!node.attributes || !node.attributes.length) {
24669 this.cleanUpChildren(node);
24673 function cleanAttr(n,v)
24676 if (v.match(/^\./) || v.match(/^\//)) {
24679 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24682 if (v.match(/^#/)) {
24685 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24686 node.removeAttribute(n);
24690 var cwhite = this.cwhite;
24691 var cblack = this.cblack;
24693 function cleanStyle(n,v)
24695 if (v.match(/expression/)) { //XSS?? should we even bother..
24696 node.removeAttribute(n);
24700 var parts = v.split(/;/);
24703 Roo.each(parts, function(p) {
24704 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24708 var l = p.split(':').shift().replace(/\s+/g,'');
24709 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24711 if ( cwhite.length && cblack.indexOf(l) > -1) {
24712 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24713 //node.removeAttribute(n);
24717 // only allow 'c whitelisted system attributes'
24718 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24719 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24720 //node.removeAttribute(n);
24730 if (clean.length) {
24731 node.setAttribute(n, clean.join(';'));
24733 node.removeAttribute(n);
24739 for (var i = node.attributes.length-1; i > -1 ; i--) {
24740 var a = node.attributes[i];
24743 if (a.name.toLowerCase().substr(0,2)=='on') {
24744 node.removeAttribute(a.name);
24747 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24748 node.removeAttribute(a.name);
24751 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24752 cleanAttr(a.name,a.value); // fixme..
24755 if (a.name == 'style') {
24756 cleanStyle(a.name,a.value);
24759 /// clean up MS crap..
24760 // tecnically this should be a list of valid class'es..
24763 if (a.name == 'class') {
24764 if (a.value.match(/^Mso/)) {
24765 node.removeAttribute('class');
24768 if (a.value.match(/^body$/)) {
24769 node.removeAttribute('class');
24780 this.cleanUpChildren(node);
24786 * Clean up MS wordisms...
24788 cleanWord : function(node)
24791 this.cleanWord(this.doc.body);
24796 node.nodeName == 'SPAN' &&
24797 !node.hasAttributes() &&
24798 node.childNodes.length == 1 &&
24799 node.firstChild.nodeName == "#text"
24801 var textNode = node.firstChild;
24802 node.removeChild(textNode);
24803 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24804 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24806 node.parentNode.insertBefore(textNode, node);
24807 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24808 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24810 node.parentNode.removeChild(node);
24813 if (node.nodeName == "#text") {
24814 // clean up silly Windows -- stuff?
24817 if (node.nodeName == "#comment") {
24818 node.parentNode.removeChild(node);
24819 // clean up silly Windows -- stuff?
24823 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24824 node.parentNode.removeChild(node);
24827 //Roo.log(node.tagName);
24828 // remove - but keep children..
24829 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24830 //Roo.log('-- removed');
24831 while (node.childNodes.length) {
24832 var cn = node.childNodes[0];
24833 node.removeChild(cn);
24834 node.parentNode.insertBefore(cn, node);
24835 // move node to parent - and clean it..
24836 this.cleanWord(cn);
24838 node.parentNode.removeChild(node);
24839 /// no need to iterate chidlren = it's got none..
24840 //this.iterateChildren(node, this.cleanWord);
24844 if (node.className.length) {
24846 var cn = node.className.split(/\W+/);
24848 Roo.each(cn, function(cls) {
24849 if (cls.match(/Mso[a-zA-Z]+/)) {
24854 node.className = cna.length ? cna.join(' ') : '';
24856 node.removeAttribute("class");
24860 if (node.hasAttribute("lang")) {
24861 node.removeAttribute("lang");
24864 if (node.hasAttribute("style")) {
24866 var styles = node.getAttribute("style").split(";");
24868 Roo.each(styles, function(s) {
24869 if (!s.match(/:/)) {
24872 var kv = s.split(":");
24873 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24876 // what ever is left... we allow.
24879 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24880 if (!nstyle.length) {
24881 node.removeAttribute('style');
24884 this.iterateChildren(node, this.cleanWord);
24890 * iterateChildren of a Node, calling fn each time, using this as the scole..
24891 * @param {DomNode} node node to iterate children of.
24892 * @param {Function} fn method of this class to call on each item.
24894 iterateChildren : function(node, fn)
24896 if (!node.childNodes.length) {
24899 for (var i = node.childNodes.length-1; i > -1 ; i--) {
24900 fn.call(this, node.childNodes[i])
24906 * cleanTableWidths.
24908 * Quite often pasting from word etc.. results in tables with column and widths.
24909 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24912 cleanTableWidths : function(node)
24917 this.cleanTableWidths(this.doc.body);
24922 if (node.nodeName == "#text" || node.nodeName == "#comment") {
24925 Roo.log(node.tagName);
24926 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24927 this.iterateChildren(node, this.cleanTableWidths);
24930 if (node.hasAttribute('width')) {
24931 node.removeAttribute('width');
24935 if (node.hasAttribute("style")) {
24938 var styles = node.getAttribute("style").split(";");
24940 Roo.each(styles, function(s) {
24941 if (!s.match(/:/)) {
24944 var kv = s.split(":");
24945 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24948 // what ever is left... we allow.
24951 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24952 if (!nstyle.length) {
24953 node.removeAttribute('style');
24957 this.iterateChildren(node, this.cleanTableWidths);
24965 domToHTML : function(currentElement, depth, nopadtext) {
24967 depth = depth || 0;
24968 nopadtext = nopadtext || false;
24970 if (!currentElement) {
24971 return this.domToHTML(this.doc.body);
24974 //Roo.log(currentElement);
24976 var allText = false;
24977 var nodeName = currentElement.nodeName;
24978 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24980 if (nodeName == '#text') {
24982 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24987 if (nodeName != 'BODY') {
24990 // Prints the node tagName, such as <A>, <IMG>, etc
24993 for(i = 0; i < currentElement.attributes.length;i++) {
24995 var aname = currentElement.attributes.item(i).name;
24996 if (!currentElement.attributes.item(i).value.length) {
24999 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25002 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25011 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25014 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25019 // Traverse the tree
25021 var currentElementChild = currentElement.childNodes.item(i);
25022 var allText = true;
25023 var innerHTML = '';
25025 while (currentElementChild) {
25026 // Formatting code (indent the tree so it looks nice on the screen)
25027 var nopad = nopadtext;
25028 if (lastnode == 'SPAN') {
25032 if (currentElementChild.nodeName == '#text') {
25033 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25034 toadd = nopadtext ? toadd : toadd.trim();
25035 if (!nopad && toadd.length > 80) {
25036 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25038 innerHTML += toadd;
25041 currentElementChild = currentElement.childNodes.item(i);
25047 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25049 // Recursively traverse the tree structure of the child node
25050 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25051 lastnode = currentElementChild.nodeName;
25053 currentElementChild=currentElement.childNodes.item(i);
25059 // The remaining code is mostly for formatting the tree
25060 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25065 ret+= "</"+tagName+">";
25071 applyBlacklists : function()
25073 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25074 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25078 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25079 if (b.indexOf(tag) > -1) {
25082 this.white.push(tag);
25086 Roo.each(w, function(tag) {
25087 if (b.indexOf(tag) > -1) {
25090 if (this.white.indexOf(tag) > -1) {
25093 this.white.push(tag);
25098 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25099 if (w.indexOf(tag) > -1) {
25102 this.black.push(tag);
25106 Roo.each(b, function(tag) {
25107 if (w.indexOf(tag) > -1) {
25110 if (this.black.indexOf(tag) > -1) {
25113 this.black.push(tag);
25118 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25119 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25123 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25124 if (b.indexOf(tag) > -1) {
25127 this.cwhite.push(tag);
25131 Roo.each(w, function(tag) {
25132 if (b.indexOf(tag) > -1) {
25135 if (this.cwhite.indexOf(tag) > -1) {
25138 this.cwhite.push(tag);
25143 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25144 if (w.indexOf(tag) > -1) {
25147 this.cblack.push(tag);
25151 Roo.each(b, function(tag) {
25152 if (w.indexOf(tag) > -1) {
25155 if (this.cblack.indexOf(tag) > -1) {
25158 this.cblack.push(tag);
25163 setStylesheets : function(stylesheets)
25165 if(typeof(stylesheets) == 'string'){
25166 Roo.get(this.iframe.contentDocument.head).createChild({
25168 rel : 'stylesheet',
25177 Roo.each(stylesheets, function(s) {
25182 Roo.get(_this.iframe.contentDocument.head).createChild({
25184 rel : 'stylesheet',
25193 removeStylesheets : function()
25197 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25202 setStyle : function(style)
25204 Roo.get(this.iframe.contentDocument.head).createChild({
25213 // hide stuff that is not compatible
25227 * @event specialkey
25231 * @cfg {String} fieldClass @hide
25234 * @cfg {String} focusClass @hide
25237 * @cfg {String} autoCreate @hide
25240 * @cfg {String} inputType @hide
25243 * @cfg {String} invalidClass @hide
25246 * @cfg {String} invalidText @hide
25249 * @cfg {String} msgFx @hide
25252 * @cfg {String} validateOnBlur @hide
25256 Roo.HtmlEditorCore.white = [
25257 'area', 'br', 'img', 'input', 'hr', 'wbr',
25259 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25260 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25261 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25262 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25263 'table', 'ul', 'xmp',
25265 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25268 'dir', 'menu', 'ol', 'ul', 'dl',
25274 Roo.HtmlEditorCore.black = [
25275 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25277 'base', 'basefont', 'bgsound', 'blink', 'body',
25278 'frame', 'frameset', 'head', 'html', 'ilayer',
25279 'iframe', 'layer', 'link', 'meta', 'object',
25280 'script', 'style' ,'title', 'xml' // clean later..
25282 Roo.HtmlEditorCore.clean = [
25283 'script', 'style', 'title', 'xml'
25285 Roo.HtmlEditorCore.remove = [
25290 Roo.HtmlEditorCore.ablack = [
25294 Roo.HtmlEditorCore.aclean = [
25295 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25299 Roo.HtmlEditorCore.pwhite= [
25300 'http', 'https', 'mailto'
25303 // white listed style attributes.
25304 Roo.HtmlEditorCore.cwhite= [
25305 // 'text-align', /// default is to allow most things..
25311 // black listed style attributes.
25312 Roo.HtmlEditorCore.cblack= [
25313 // 'font-size' -- this can be set by the project
25317 Roo.HtmlEditorCore.swapCodes =[
25336 * @class Roo.bootstrap.HtmlEditor
25337 * @extends Roo.bootstrap.TextArea
25338 * Bootstrap HtmlEditor class
25341 * Create a new HtmlEditor
25342 * @param {Object} config The config object
25345 Roo.bootstrap.HtmlEditor = function(config){
25346 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25347 if (!this.toolbars) {
25348 this.toolbars = [];
25351 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25354 * @event initialize
25355 * Fires when the editor is fully initialized (including the iframe)
25356 * @param {HtmlEditor} this
25361 * Fires when the editor is first receives the focus. Any insertion must wait
25362 * until after this event.
25363 * @param {HtmlEditor} this
25367 * @event beforesync
25368 * Fires before the textarea is updated with content from the editor iframe. Return false
25369 * to cancel the sync.
25370 * @param {HtmlEditor} this
25371 * @param {String} html
25375 * @event beforepush
25376 * Fires before the iframe editor is updated with content from the textarea. Return false
25377 * to cancel the push.
25378 * @param {HtmlEditor} this
25379 * @param {String} html
25384 * Fires when the textarea is updated with content from the editor iframe.
25385 * @param {HtmlEditor} this
25386 * @param {String} html
25391 * Fires when the iframe editor is updated with content from the textarea.
25392 * @param {HtmlEditor} this
25393 * @param {String} html
25397 * @event editmodechange
25398 * Fires when the editor switches edit modes
25399 * @param {HtmlEditor} this
25400 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25402 editmodechange: true,
25404 * @event editorevent
25405 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25406 * @param {HtmlEditor} this
25410 * @event firstfocus
25411 * Fires when on first focus - needed by toolbars..
25412 * @param {HtmlEditor} this
25417 * Auto save the htmlEditor value as a file into Events
25418 * @param {HtmlEditor} this
25422 * @event savedpreview
25423 * preview the saved version of htmlEditor
25424 * @param {HtmlEditor} this
25431 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25435 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25440 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25445 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25450 * @cfg {Number} height (in pixels)
25454 * @cfg {Number} width (in pixels)
25459 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25462 stylesheets: false,
25467 // private properties
25468 validationEvent : false,
25470 initialized : false,
25473 onFocus : Roo.emptyFn,
25475 hideMode:'offsets',
25477 tbContainer : false,
25481 toolbarContainer :function() {
25482 return this.wrap.select('.x-html-editor-tb',true).first();
25486 * Protected method that will not generally be called directly. It
25487 * is called when the editor creates its toolbar. Override this method if you need to
25488 * add custom toolbar buttons.
25489 * @param {HtmlEditor} editor
25491 createToolbar : function(){
25492 Roo.log('renewing');
25493 Roo.log("create toolbars");
25495 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25496 this.toolbars[0].render(this.toolbarContainer());
25500 // if (!editor.toolbars || !editor.toolbars.length) {
25501 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25504 // for (var i =0 ; i < editor.toolbars.length;i++) {
25505 // editor.toolbars[i] = Roo.factory(
25506 // typeof(editor.toolbars[i]) == 'string' ?
25507 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25508 // Roo.bootstrap.HtmlEditor);
25509 // editor.toolbars[i].init(editor);
25515 onRender : function(ct, position)
25517 // Roo.log("Call onRender: " + this.xtype);
25519 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25521 this.wrap = this.inputEl().wrap({
25522 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25525 this.editorcore.onRender(ct, position);
25527 if (this.resizable) {
25528 this.resizeEl = new Roo.Resizable(this.wrap, {
25532 minHeight : this.height,
25533 height: this.height,
25534 handles : this.resizable,
25537 resize : function(r, w, h) {
25538 _t.onResize(w,h); // -something
25544 this.createToolbar(this);
25547 if(!this.width && this.resizable){
25548 this.setSize(this.wrap.getSize());
25550 if (this.resizeEl) {
25551 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25552 // should trigger onReize..
25558 onResize : function(w, h)
25560 Roo.log('resize: ' +w + ',' + h );
25561 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25565 if(this.inputEl() ){
25566 if(typeof w == 'number'){
25567 var aw = w - this.wrap.getFrameWidth('lr');
25568 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25571 if(typeof h == 'number'){
25572 var tbh = -11; // fixme it needs to tool bar size!
25573 for (var i =0; i < this.toolbars.length;i++) {
25574 // fixme - ask toolbars for heights?
25575 tbh += this.toolbars[i].el.getHeight();
25576 //if (this.toolbars[i].footer) {
25577 // tbh += this.toolbars[i].footer.el.getHeight();
25585 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25586 ah -= 5; // knock a few pixes off for look..
25587 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25591 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25592 this.editorcore.onResize(ew,eh);
25597 * Toggles the editor between standard and source edit mode.
25598 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25600 toggleSourceEdit : function(sourceEditMode)
25602 this.editorcore.toggleSourceEdit(sourceEditMode);
25604 if(this.editorcore.sourceEditMode){
25605 Roo.log('editor - showing textarea');
25608 // Roo.log(this.syncValue());
25610 this.inputEl().removeClass(['hide', 'x-hidden']);
25611 this.inputEl().dom.removeAttribute('tabIndex');
25612 this.inputEl().focus();
25614 Roo.log('editor - hiding textarea');
25616 // Roo.log(this.pushValue());
25619 this.inputEl().addClass(['hide', 'x-hidden']);
25620 this.inputEl().dom.setAttribute('tabIndex', -1);
25621 //this.deferFocus();
25624 if(this.resizable){
25625 this.setSize(this.wrap.getSize());
25628 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25631 // private (for BoxComponent)
25632 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25634 // private (for BoxComponent)
25635 getResizeEl : function(){
25639 // private (for BoxComponent)
25640 getPositionEl : function(){
25645 initEvents : function(){
25646 this.originalValue = this.getValue();
25650 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25653 // markInvalid : Roo.emptyFn,
25655 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25658 // clearInvalid : Roo.emptyFn,
25660 setValue : function(v){
25661 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25662 this.editorcore.pushValue();
25667 deferFocus : function(){
25668 this.focus.defer(10, this);
25672 focus : function(){
25673 this.editorcore.focus();
25679 onDestroy : function(){
25685 for (var i =0; i < this.toolbars.length;i++) {
25686 // fixme - ask toolbars for heights?
25687 this.toolbars[i].onDestroy();
25690 this.wrap.dom.innerHTML = '';
25691 this.wrap.remove();
25696 onFirstFocus : function(){
25697 //Roo.log("onFirstFocus");
25698 this.editorcore.onFirstFocus();
25699 for (var i =0; i < this.toolbars.length;i++) {
25700 this.toolbars[i].onFirstFocus();
25706 syncValue : function()
25708 this.editorcore.syncValue();
25711 pushValue : function()
25713 this.editorcore.pushValue();
25717 // hide stuff that is not compatible
25731 * @event specialkey
25735 * @cfg {String} fieldClass @hide
25738 * @cfg {String} focusClass @hide
25741 * @cfg {String} autoCreate @hide
25744 * @cfg {String} inputType @hide
25748 * @cfg {String} invalidText @hide
25751 * @cfg {String} msgFx @hide
25754 * @cfg {String} validateOnBlur @hide
25763 Roo.namespace('Roo.bootstrap.htmleditor');
25765 * @class Roo.bootstrap.HtmlEditorToolbar1
25771 new Roo.bootstrap.HtmlEditor({
25774 new Roo.bootstrap.HtmlEditorToolbar1({
25775 disable : { fonts: 1 , format: 1, ..., ... , ...],
25781 * @cfg {Object} disable List of elements to disable..
25782 * @cfg {Array} btns List of additional buttons.
25786 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25789 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25792 Roo.apply(this, config);
25794 // default disabled, based on 'good practice'..
25795 this.disable = this.disable || {};
25796 Roo.applyIf(this.disable, {
25799 specialElements : true
25801 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25803 this.editor = config.editor;
25804 this.editorcore = config.editor.editorcore;
25806 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25808 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25809 // dont call parent... till later.
25811 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
25816 editorcore : false,
25821 "h1","h2","h3","h4","h5","h6",
25823 "abbr", "acronym", "address", "cite", "samp", "var",
25827 onRender : function(ct, position)
25829 // Roo.log("Call onRender: " + this.xtype);
25831 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25833 this.el.dom.style.marginBottom = '0';
25835 var editorcore = this.editorcore;
25836 var editor= this.editor;
25839 var btn = function(id,cmd , toggle, handler, html){
25841 var event = toggle ? 'toggle' : 'click';
25846 xns: Roo.bootstrap,
25850 enableToggle:toggle !== false,
25852 pressed : toggle ? false : null,
25855 a.listeners[toggle ? 'toggle' : 'click'] = function() {
25856 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
25862 // var cb_box = function...
25867 xns: Roo.bootstrap,
25872 xns: Roo.bootstrap,
25876 Roo.each(this.formats, function(f) {
25877 style.menu.items.push({
25879 xns: Roo.bootstrap,
25880 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25885 editorcore.insertTag(this.tagname);
25892 children.push(style);
25894 btn('bold',false,true);
25895 btn('italic',false,true);
25896 btn('align-left', 'justifyleft',true);
25897 btn('align-center', 'justifycenter',true);
25898 btn('align-right' , 'justifyright',true);
25899 btn('link', false, false, function(btn) {
25900 //Roo.log("create link?");
25901 var url = prompt(this.createLinkText, this.defaultLinkValue);
25902 if(url && url != 'http:/'+'/'){
25903 this.editorcore.relayCmd('createlink', url);
25906 btn('list','insertunorderedlist',true);
25907 btn('pencil', false,true, function(btn){
25909 this.toggleSourceEdit(btn.pressed);
25912 if (this.editor.btns.length > 0) {
25913 for (var i = 0; i<this.editor.btns.length; i++) {
25914 children.push(this.editor.btns[i]);
25922 xns: Roo.bootstrap,
25927 xns: Roo.bootstrap,
25932 cog.menu.items.push({
25934 xns: Roo.bootstrap,
25935 html : Clean styles,
25940 editorcore.insertTag(this.tagname);
25949 this.xtype = 'NavSimplebar';
25951 for(var i=0;i< children.length;i++) {
25953 this.buttons.add(this.addxtypeChild(children[i]));
25957 editor.on('editorevent', this.updateToolbar, this);
25959 onBtnClick : function(id)
25961 this.editorcore.relayCmd(id);
25962 this.editorcore.focus();
25966 * Protected method that will not generally be called directly. It triggers
25967 * a toolbar update by reading the markup state of the current selection in the editor.
25969 updateToolbar: function(){
25971 if(!this.editorcore.activated){
25972 this.editor.onFirstFocus(); // is this neeed?
25976 var btns = this.buttons;
25977 var doc = this.editorcore.doc;
25978 btns.get('bold').setActive(doc.queryCommandState('bold'));
25979 btns.get('italic').setActive(doc.queryCommandState('italic'));
25980 //btns.get('underline').setActive(doc.queryCommandState('underline'));
25982 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25983 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25984 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25986 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25987 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25990 var ans = this.editorcore.getAllAncestors();
25991 if (this.formatCombo) {
25994 var store = this.formatCombo.store;
25995 this.formatCombo.setValue("");
25996 for (var i =0; i < ans.length;i++) {
25997 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25999 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26007 // hides menus... - so this cant be on a menu...
26008 Roo.bootstrap.MenuMgr.hideAll();
26010 Roo.bootstrap.MenuMgr.hideAll();
26011 //this.editorsyncValue();
26013 onFirstFocus: function() {
26014 this.buttons.each(function(item){
26018 toggleSourceEdit : function(sourceEditMode){
26021 if(sourceEditMode){
26022 Roo.log("disabling buttons");
26023 this.buttons.each( function(item){
26024 if(item.cmd != 'pencil'){
26030 Roo.log("enabling buttons");
26031 if(this.editorcore.initialized){
26032 this.buttons.each( function(item){
26038 Roo.log("calling toggole on editor");
26039 // tell the editor that it's been pressed..
26040 this.editor.toggleSourceEdit(sourceEditMode);
26050 * @class Roo.bootstrap.Table.AbstractSelectionModel
26051 * @extends Roo.util.Observable
26052 * Abstract base class for grid SelectionModels. It provides the interface that should be
26053 * implemented by descendant classes. This class should not be directly instantiated.
26056 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26057 this.locked = false;
26058 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26062 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26063 /** @ignore Called by the grid automatically. Do not call directly. */
26064 init : function(grid){
26070 * Locks the selections.
26073 this.locked = true;
26077 * Unlocks the selections.
26079 unlock : function(){
26080 this.locked = false;
26084 * Returns true if the selections are locked.
26085 * @return {Boolean}
26087 isLocked : function(){
26088 return this.locked;
26092 initEvents : function ()
26098 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26099 * @class Roo.bootstrap.Table.RowSelectionModel
26100 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26101 * It supports multiple selections and keyboard selection/navigation.
26103 * @param {Object} config
26106 Roo.bootstrap.Table.RowSelectionModel = function(config){
26107 Roo.apply(this, config);
26108 this.selections = new Roo.util.MixedCollection(false, function(o){
26113 this.lastActive = false;
26117 * @event selectionchange
26118 * Fires when the selection changes
26119 * @param {SelectionModel} this
26121 "selectionchange" : true,
26123 * @event afterselectionchange
26124 * Fires after the selection changes (eg. by key press or clicking)
26125 * @param {SelectionModel} this
26127 "afterselectionchange" : true,
26129 * @event beforerowselect
26130 * Fires when a row is selected being selected, return false to cancel.
26131 * @param {SelectionModel} this
26132 * @param {Number} rowIndex The selected index
26133 * @param {Boolean} keepExisting False if other selections will be cleared
26135 "beforerowselect" : true,
26138 * Fires when a row is selected.
26139 * @param {SelectionModel} this
26140 * @param {Number} rowIndex The selected index
26141 * @param {Roo.data.Record} r The record
26143 "rowselect" : true,
26145 * @event rowdeselect
26146 * Fires when a row is deselected.
26147 * @param {SelectionModel} this
26148 * @param {Number} rowIndex The selected index
26150 "rowdeselect" : true
26152 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26153 this.locked = false;
26156 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26158 * @cfg {Boolean} singleSelect
26159 * True to allow selection of only one row at a time (defaults to false)
26161 singleSelect : false,
26164 initEvents : function()
26167 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26168 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26169 //}else{ // allow click to work like normal
26170 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26172 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26173 this.grid.on("rowclick", this.handleMouseDown, this);
26175 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26176 "up" : function(e){
26178 this.selectPrevious(e.shiftKey);
26179 }else if(this.last !== false && this.lastActive !== false){
26180 var last = this.last;
26181 this.selectRange(this.last, this.lastActive-1);
26182 this.grid.getView().focusRow(this.lastActive);
26183 if(last !== false){
26187 this.selectFirstRow();
26189 this.fireEvent("afterselectionchange", this);
26191 "down" : function(e){
26193 this.selectNext(e.shiftKey);
26194 }else if(this.last !== false && this.lastActive !== false){
26195 var last = this.last;
26196 this.selectRange(this.last, this.lastActive+1);
26197 this.grid.getView().focusRow(this.lastActive);
26198 if(last !== false){
26202 this.selectFirstRow();
26204 this.fireEvent("afterselectionchange", this);
26208 this.grid.store.on('load', function(){
26209 this.selections.clear();
26212 var view = this.grid.view;
26213 view.on("refresh", this.onRefresh, this);
26214 view.on("rowupdated", this.onRowUpdated, this);
26215 view.on("rowremoved", this.onRemove, this);
26220 onRefresh : function()
26222 var ds = this.grid.store, i, v = this.grid.view;
26223 var s = this.selections;
26224 s.each(function(r){
26225 if((i = ds.indexOfId(r.id)) != -1){
26234 onRemove : function(v, index, r){
26235 this.selections.remove(r);
26239 onRowUpdated : function(v, index, r){
26240 if(this.isSelected(r)){
26241 v.onRowSelect(index);
26247 * @param {Array} records The records to select
26248 * @param {Boolean} keepExisting (optional) True to keep existing selections
26250 selectRecords : function(records, keepExisting)
26253 this.clearSelections();
26255 var ds = this.grid.store;
26256 for(var i = 0, len = records.length; i < len; i++){
26257 this.selectRow(ds.indexOf(records[i]), true);
26262 * Gets the number of selected rows.
26265 getCount : function(){
26266 return this.selections.length;
26270 * Selects the first row in the grid.
26272 selectFirstRow : function(){
26277 * Select the last row.
26278 * @param {Boolean} keepExisting (optional) True to keep existing selections
26280 selectLastRow : function(keepExisting){
26281 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26282 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26286 * Selects the row immediately following the last selected row.
26287 * @param {Boolean} keepExisting (optional) True to keep existing selections
26289 selectNext : function(keepExisting)
26291 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26292 this.selectRow(this.last+1, keepExisting);
26293 this.grid.getView().focusRow(this.last);
26298 * Selects the row that precedes the last selected row.
26299 * @param {Boolean} keepExisting (optional) True to keep existing selections
26301 selectPrevious : function(keepExisting){
26303 this.selectRow(this.last-1, keepExisting);
26304 this.grid.getView().focusRow(this.last);
26309 * Returns the selected records
26310 * @return {Array} Array of selected records
26312 getSelections : function(){
26313 return [].concat(this.selections.items);
26317 * Returns the first selected record.
26320 getSelected : function(){
26321 return this.selections.itemAt(0);
26326 * Clears all selections.
26328 clearSelections : function(fast)
26334 var ds = this.grid.store;
26335 var s = this.selections;
26336 s.each(function(r){
26337 this.deselectRow(ds.indexOfId(r.id));
26341 this.selections.clear();
26348 * Selects all rows.
26350 selectAll : function(){
26354 this.selections.clear();
26355 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26356 this.selectRow(i, true);
26361 * Returns True if there is a selection.
26362 * @return {Boolean}
26364 hasSelection : function(){
26365 return this.selections.length > 0;
26369 * Returns True if the specified row is selected.
26370 * @param {Number/Record} record The record or index of the record to check
26371 * @return {Boolean}
26373 isSelected : function(index){
26374 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26375 return (r && this.selections.key(r.id) ? true : false);
26379 * Returns True if the specified record id is selected.
26380 * @param {String} id The id of record to check
26381 * @return {Boolean}
26383 isIdSelected : function(id){
26384 return (this.selections.key(id) ? true : false);
26389 handleMouseDBClick : function(e, t){
26393 handleMouseDown : function(e, t)
26395 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26396 if(this.isLocked() || rowIndex < 0 ){
26399 if(e.shiftKey && this.last !== false){
26400 var last = this.last;
26401 this.selectRange(last, rowIndex, e.ctrlKey);
26402 this.last = last; // reset the last
26406 var isSelected = this.isSelected(rowIndex);
26407 //Roo.log("select row:" + rowIndex);
26409 this.deselectRow(rowIndex);
26411 this.selectRow(rowIndex, true);
26415 if(e.button !== 0 && isSelected){
26416 alert('rowIndex 2: ' + rowIndex);
26417 view.focusRow(rowIndex);
26418 }else if(e.ctrlKey && isSelected){
26419 this.deselectRow(rowIndex);
26420 }else if(!isSelected){
26421 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26422 view.focusRow(rowIndex);
26426 this.fireEvent("afterselectionchange", this);
26429 handleDragableRowClick : function(grid, rowIndex, e)
26431 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26432 this.selectRow(rowIndex, false);
26433 grid.view.focusRow(rowIndex);
26434 this.fireEvent("afterselectionchange", this);
26439 * Selects multiple rows.
26440 * @param {Array} rows Array of the indexes of the row to select
26441 * @param {Boolean} keepExisting (optional) True to keep existing selections
26443 selectRows : function(rows, keepExisting){
26445 this.clearSelections();
26447 for(var i = 0, len = rows.length; i < len; i++){
26448 this.selectRow(rows[i], true);
26453 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26454 * @param {Number} startRow The index of the first row in the range
26455 * @param {Number} endRow The index of the last row in the range
26456 * @param {Boolean} keepExisting (optional) True to retain existing selections
26458 selectRange : function(startRow, endRow, keepExisting){
26463 this.clearSelections();
26465 if(startRow <= endRow){
26466 for(var i = startRow; i <= endRow; i++){
26467 this.selectRow(i, true);
26470 for(var i = startRow; i >= endRow; i--){
26471 this.selectRow(i, true);
26477 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26478 * @param {Number} startRow The index of the first row in the range
26479 * @param {Number} endRow The index of the last row in the range
26481 deselectRange : function(startRow, endRow, preventViewNotify){
26485 for(var i = startRow; i <= endRow; i++){
26486 this.deselectRow(i, preventViewNotify);
26492 * @param {Number} row The index of the row to select
26493 * @param {Boolean} keepExisting (optional) True to keep existing selections
26495 selectRow : function(index, keepExisting, preventViewNotify)
26497 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26500 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26501 if(!keepExisting || this.singleSelect){
26502 this.clearSelections();
26505 var r = this.grid.store.getAt(index);
26506 //console.log('selectRow - record id :' + r.id);
26508 this.selections.add(r);
26509 this.last = this.lastActive = index;
26510 if(!preventViewNotify){
26511 var proxy = new Roo.Element(
26512 this.grid.getRowDom(index)
26514 proxy.addClass('bg-info info');
26516 this.fireEvent("rowselect", this, index, r);
26517 this.fireEvent("selectionchange", this);
26523 * @param {Number} row The index of the row to deselect
26525 deselectRow : function(index, preventViewNotify)
26530 if(this.last == index){
26533 if(this.lastActive == index){
26534 this.lastActive = false;
26537 var r = this.grid.store.getAt(index);
26542 this.selections.remove(r);
26543 //.console.log('deselectRow - record id :' + r.id);
26544 if(!preventViewNotify){
26546 var proxy = new Roo.Element(
26547 this.grid.getRowDom(index)
26549 proxy.removeClass('bg-info info');
26551 this.fireEvent("rowdeselect", this, index);
26552 this.fireEvent("selectionchange", this);
26556 restoreLast : function(){
26558 this.last = this._last;
26563 acceptsNav : function(row, col, cm){
26564 return !cm.isHidden(col) && cm.isCellEditable(col, row);
26568 onEditorKey : function(field, e){
26569 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26574 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26576 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26578 }else if(k == e.ENTER && !e.ctrlKey){
26582 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26584 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26586 }else if(k == e.ESC){
26590 g.startEditing(newCell[0], newCell[1]);
26596 * Ext JS Library 1.1.1
26597 * Copyright(c) 2006-2007, Ext JS, LLC.
26599 * Originally Released Under LGPL - original licence link has changed is not relivant.
26602 * <script type="text/javascript">
26606 * @class Roo.bootstrap.PagingToolbar
26607 * @extends Roo.bootstrap.NavSimplebar
26608 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26610 * Create a new PagingToolbar
26611 * @param {Object} config The config object
26612 * @param {Roo.data.Store} store
26614 Roo.bootstrap.PagingToolbar = function(config)
26616 // old args format still supported... - xtype is prefered..
26617 // created from xtype...
26619 this.ds = config.dataSource;
26621 if (config.store && !this.ds) {
26622 this.store= Roo.factory(config.store, Roo.data);
26623 this.ds = this.store;
26624 this.ds.xmodule = this.xmodule || false;
26627 this.toolbarItems = [];
26628 if (config.items) {
26629 this.toolbarItems = config.items;
26632 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26637 this.bind(this.ds);
26640 if (Roo.bootstrap.version == 4) {
26641 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26643 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26648 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26650 * @cfg {Roo.data.Store} dataSource
26651 * The underlying data store providing the paged data
26654 * @cfg {String/HTMLElement/Element} container
26655 * container The id or element that will contain the toolbar
26658 * @cfg {Boolean} displayInfo
26659 * True to display the displayMsg (defaults to false)
26662 * @cfg {Number} pageSize
26663 * The number of records to display per page (defaults to 20)
26667 * @cfg {String} displayMsg
26668 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26670 displayMsg : 'Displaying {0} - {1} of {2}',
26672 * @cfg {String} emptyMsg
26673 * The message to display when no records are found (defaults to "No data to display")
26675 emptyMsg : 'No data to display',
26677 * Customizable piece of the default paging text (defaults to "Page")
26680 beforePageText : "Page",
26682 * Customizable piece of the default paging text (defaults to "of %0")
26685 afterPageText : "of {0}",
26687 * Customizable piece of the default paging text (defaults to "First Page")
26690 firstText : "First Page",
26692 * Customizable piece of the default paging text (defaults to "Previous Page")
26695 prevText : "Previous Page",
26697 * Customizable piece of the default paging text (defaults to "Next Page")
26700 nextText : "Next Page",
26702 * Customizable piece of the default paging text (defaults to "Last Page")
26705 lastText : "Last Page",
26707 * Customizable piece of the default paging text (defaults to "Refresh")
26710 refreshText : "Refresh",
26714 onRender : function(ct, position)
26716 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26717 this.navgroup.parentId = this.id;
26718 this.navgroup.onRender(this.el, null);
26719 // add the buttons to the navgroup
26721 if(this.displayInfo){
26722 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26723 this.displayEl = this.el.select('.x-paging-info', true).first();
26724 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26725 // this.displayEl = navel.el.select('span',true).first();
26731 Roo.each(_this.buttons, function(e){ // this might need to use render????
26732 Roo.factory(e).render(_this.el);
26736 Roo.each(_this.toolbarItems, function(e) {
26737 _this.navgroup.addItem(e);
26741 this.first = this.navgroup.addItem({
26742 tooltip: this.firstText,
26743 cls: "prev btn-outline-secondary",
26744 html : ' <i class="fa fa-step-backward"></i>',
26746 preventDefault: true,
26747 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26750 this.prev = this.navgroup.addItem({
26751 tooltip: this.prevText,
26752 cls: "prev btn-outline-secondary",
26753 html : ' <i class="fa fa-backward"></i>',
26755 preventDefault: true,
26756 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
26758 //this.addSeparator();
26761 var field = this.navgroup.addItem( {
26763 cls : 'x-paging-position btn-outline-secondary',
26765 html : this.beforePageText +
26766 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26767 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
26770 this.field = field.el.select('input', true).first();
26771 this.field.on("keydown", this.onPagingKeydown, this);
26772 this.field.on("focus", function(){this.dom.select();});
26775 this.afterTextEl = field.el.select('.x-paging-after',true).first();
26776 //this.field.setHeight(18);
26777 //this.addSeparator();
26778 this.next = this.navgroup.addItem({
26779 tooltip: this.nextText,
26780 cls: "next btn-outline-secondary",
26781 html : ' <i class="fa fa-forward"></i>',
26783 preventDefault: true,
26784 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
26786 this.last = this.navgroup.addItem({
26787 tooltip: this.lastText,
26788 html : ' <i class="fa fa-step-forward"></i>',
26789 cls: "next btn-outline-secondary",
26791 preventDefault: true,
26792 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
26794 //this.addSeparator();
26795 this.loading = this.navgroup.addItem({
26796 tooltip: this.refreshText,
26797 cls: "btn-outline-secondary",
26798 html : ' <i class="fa fa-refresh"></i>',
26799 preventDefault: true,
26800 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26806 updateInfo : function(){
26807 if(this.displayEl){
26808 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26809 var msg = count == 0 ?
26813 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
26815 this.displayEl.update(msg);
26820 onLoad : function(ds, r, o)
26822 this.cursor = o.params.start ? o.params.start : 0;
26824 var d = this.getPageData(),
26829 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26830 this.field.dom.value = ap;
26831 this.first.setDisabled(ap == 1);
26832 this.prev.setDisabled(ap == 1);
26833 this.next.setDisabled(ap == ps);
26834 this.last.setDisabled(ap == ps);
26835 this.loading.enable();
26840 getPageData : function(){
26841 var total = this.ds.getTotalCount();
26844 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26845 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26850 onLoadError : function(){
26851 this.loading.enable();
26855 onPagingKeydown : function(e){
26856 var k = e.getKey();
26857 var d = this.getPageData();
26859 var v = this.field.dom.value, pageNum;
26860 if(!v || isNaN(pageNum = parseInt(v, 10))){
26861 this.field.dom.value = d.activePage;
26864 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26865 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26868 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))
26870 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26871 this.field.dom.value = pageNum;
26872 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26875 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26877 var v = this.field.dom.value, pageNum;
26878 var increment = (e.shiftKey) ? 10 : 1;
26879 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26882 if(!v || isNaN(pageNum = parseInt(v, 10))) {
26883 this.field.dom.value = d.activePage;
26886 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26888 this.field.dom.value = parseInt(v, 10) + increment;
26889 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26890 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26897 beforeLoad : function(){
26899 this.loading.disable();
26904 onClick : function(which){
26913 ds.load({params:{start: 0, limit: this.pageSize}});
26916 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26919 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26922 var total = ds.getTotalCount();
26923 var extra = total % this.pageSize;
26924 var lastStart = extra ? (total - extra) : total-this.pageSize;
26925 ds.load({params:{start: lastStart, limit: this.pageSize}});
26928 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26934 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26935 * @param {Roo.data.Store} store The data store to unbind
26937 unbind : function(ds){
26938 ds.un("beforeload", this.beforeLoad, this);
26939 ds.un("load", this.onLoad, this);
26940 ds.un("loadexception", this.onLoadError, this);
26941 ds.un("remove", this.updateInfo, this);
26942 ds.un("add", this.updateInfo, this);
26943 this.ds = undefined;
26947 * Binds the paging toolbar to the specified {@link Roo.data.Store}
26948 * @param {Roo.data.Store} store The data store to bind
26950 bind : function(ds){
26951 ds.on("beforeload", this.beforeLoad, this);
26952 ds.on("load", this.onLoad, this);
26953 ds.on("loadexception", this.onLoadError, this);
26954 ds.on("remove", this.updateInfo, this);
26955 ds.on("add", this.updateInfo, this);
26966 * @class Roo.bootstrap.MessageBar
26967 * @extends Roo.bootstrap.Component
26968 * Bootstrap MessageBar class
26969 * @cfg {String} html contents of the MessageBar
26970 * @cfg {String} weight (info | success | warning | danger) default info
26971 * @cfg {String} beforeClass insert the bar before the given class
26972 * @cfg {Boolean} closable (true | false) default false
26973 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26976 * Create a new Element
26977 * @param {Object} config The config object
26980 Roo.bootstrap.MessageBar = function(config){
26981 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26984 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
26990 beforeClass: 'bootstrap-sticky-wrap',
26992 getAutoCreate : function(){
26996 cls: 'alert alert-dismissable alert-' + this.weight,
27001 html: this.html || ''
27007 cfg.cls += ' alert-messages-fixed';
27021 onRender : function(ct, position)
27023 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27026 var cfg = Roo.apply({}, this.getAutoCreate());
27030 cfg.cls += ' ' + this.cls;
27033 cfg.style = this.style;
27035 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27037 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27040 this.el.select('>button.close').on('click', this.hide, this);
27046 if (!this.rendered) {
27052 this.fireEvent('show', this);
27058 if (!this.rendered) {
27064 this.fireEvent('hide', this);
27067 update : function()
27069 // var e = this.el.dom.firstChild;
27071 // if(this.closable){
27072 // e = e.nextSibling;
27075 // e.data = this.html || '';
27077 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27093 * @class Roo.bootstrap.Graph
27094 * @extends Roo.bootstrap.Component
27095 * Bootstrap Graph class
27099 @cfg {String} graphtype bar | vbar | pie
27100 @cfg {number} g_x coodinator | centre x (pie)
27101 @cfg {number} g_y coodinator | centre y (pie)
27102 @cfg {number} g_r radius (pie)
27103 @cfg {number} g_height height of the chart (respected by all elements in the set)
27104 @cfg {number} g_width width of the chart (respected by all elements in the set)
27105 @cfg {Object} title The title of the chart
27108 -opts (object) options for the chart
27110 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27111 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27113 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.
27114 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27116 o stretch (boolean)
27118 -opts (object) options for the pie
27121 o startAngle (number)
27122 o endAngle (number)
27126 * Create a new Input
27127 * @param {Object} config The config object
27130 Roo.bootstrap.Graph = function(config){
27131 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27137 * The img click event for the img.
27138 * @param {Roo.EventObject} e
27144 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27155 //g_colors: this.colors,
27162 getAutoCreate : function(){
27173 onRender : function(ct,position){
27176 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27178 if (typeof(Raphael) == 'undefined') {
27179 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27183 this.raphael = Raphael(this.el.dom);
27185 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27186 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27187 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27188 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27190 r.text(160, 10, "Single Series Chart").attr(txtattr);
27191 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27192 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27193 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27195 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27196 r.barchart(330, 10, 300, 220, data1);
27197 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27198 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27201 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27202 // r.barchart(30, 30, 560, 250, xdata, {
27203 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27204 // axis : "0 0 1 1",
27205 // axisxlabels : xdata
27206 // //yvalues : cols,
27209 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27211 // this.load(null,xdata,{
27212 // axis : "0 0 1 1",
27213 // axisxlabels : xdata
27218 load : function(graphtype,xdata,opts)
27220 this.raphael.clear();
27222 graphtype = this.graphtype;
27227 var r = this.raphael,
27228 fin = function () {
27229 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27231 fout = function () {
27232 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27234 pfin = function() {
27235 this.sector.stop();
27236 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27239 this.label[0].stop();
27240 this.label[0].attr({ r: 7.5 });
27241 this.label[1].attr({ "font-weight": 800 });
27244 pfout = function() {
27245 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27248 this.label[0].animate({ r: 5 }, 500, "bounce");
27249 this.label[1].attr({ "font-weight": 400 });
27255 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27258 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27261 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27262 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27264 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27271 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27276 setTitle: function(o)
27281 initEvents: function() {
27284 this.el.on('click', this.onClick, this);
27288 onClick : function(e)
27290 Roo.log('img onclick');
27291 this.fireEvent('click', this, e);
27303 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27306 * @class Roo.bootstrap.dash.NumberBox
27307 * @extends Roo.bootstrap.Component
27308 * Bootstrap NumberBox class
27309 * @cfg {String} headline Box headline
27310 * @cfg {String} content Box content
27311 * @cfg {String} icon Box icon
27312 * @cfg {String} footer Footer text
27313 * @cfg {String} fhref Footer href
27316 * Create a new NumberBox
27317 * @param {Object} config The config object
27321 Roo.bootstrap.dash.NumberBox = function(config){
27322 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27326 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27335 getAutoCreate : function(){
27339 cls : 'small-box ',
27347 cls : 'roo-headline',
27348 html : this.headline
27352 cls : 'roo-content',
27353 html : this.content
27367 cls : 'ion ' + this.icon
27376 cls : 'small-box-footer',
27377 href : this.fhref || '#',
27381 cfg.cn.push(footer);
27388 onRender : function(ct,position){
27389 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27396 setHeadline: function (value)
27398 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27401 setFooter: function (value, href)
27403 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27406 this.el.select('a.small-box-footer',true).first().attr('href', href);
27411 setContent: function (value)
27413 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27416 initEvents: function()
27430 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27433 * @class Roo.bootstrap.dash.TabBox
27434 * @extends Roo.bootstrap.Component
27435 * Bootstrap TabBox class
27436 * @cfg {String} title Title of the TabBox
27437 * @cfg {String} icon Icon of the TabBox
27438 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27439 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27442 * Create a new TabBox
27443 * @param {Object} config The config object
27447 Roo.bootstrap.dash.TabBox = function(config){
27448 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27453 * When a pane is added
27454 * @param {Roo.bootstrap.dash.TabPane} pane
27458 * @event activatepane
27459 * When a pane is activated
27460 * @param {Roo.bootstrap.dash.TabPane} pane
27462 "activatepane" : true
27470 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27475 tabScrollable : false,
27477 getChildContainer : function()
27479 return this.el.select('.tab-content', true).first();
27482 getAutoCreate : function(){
27486 cls: 'pull-left header',
27494 cls: 'fa ' + this.icon
27500 cls: 'nav nav-tabs pull-right',
27506 if(this.tabScrollable){
27513 cls: 'nav nav-tabs pull-right',
27524 cls: 'nav-tabs-custom',
27529 cls: 'tab-content no-padding',
27537 initEvents : function()
27539 //Roo.log('add add pane handler');
27540 this.on('addpane', this.onAddPane, this);
27543 * Updates the box title
27544 * @param {String} html to set the title to.
27546 setTitle : function(value)
27548 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27550 onAddPane : function(pane)
27552 this.panes.push(pane);
27553 //Roo.log('addpane');
27555 // tabs are rendere left to right..
27556 if(!this.showtabs){
27560 var ctr = this.el.select('.nav-tabs', true).first();
27563 var existing = ctr.select('.nav-tab',true);
27564 var qty = existing.getCount();;
27567 var tab = ctr.createChild({
27569 cls : 'nav-tab' + (qty ? '' : ' active'),
27577 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27580 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27582 pane.el.addClass('active');
27587 onTabClick : function(ev,un,ob,pane)
27589 //Roo.log('tab - prev default');
27590 ev.preventDefault();
27593 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27594 pane.tab.addClass('active');
27595 //Roo.log(pane.title);
27596 this.getChildContainer().select('.tab-pane',true).removeClass('active');
27597 // technically we should have a deactivate event.. but maybe add later.
27598 // and it should not de-activate the selected tab...
27599 this.fireEvent('activatepane', pane);
27600 pane.el.addClass('active');
27601 pane.fireEvent('activate');
27606 getActivePane : function()
27609 Roo.each(this.panes, function(p) {
27610 if(p.el.hasClass('active')){
27631 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27633 * @class Roo.bootstrap.TabPane
27634 * @extends Roo.bootstrap.Component
27635 * Bootstrap TabPane class
27636 * @cfg {Boolean} active (false | true) Default false
27637 * @cfg {String} title title of panel
27641 * Create a new TabPane
27642 * @param {Object} config The config object
27645 Roo.bootstrap.dash.TabPane = function(config){
27646 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27652 * When a pane is activated
27653 * @param {Roo.bootstrap.dash.TabPane} pane
27660 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27665 // the tabBox that this is attached to.
27668 getAutoCreate : function()
27676 cfg.cls += ' active';
27681 initEvents : function()
27683 //Roo.log('trigger add pane handler');
27684 this.parent().fireEvent('addpane', this)
27688 * Updates the tab title
27689 * @param {String} html to set the title to.
27691 setTitle: function(str)
27697 this.tab.select('a', true).first().dom.innerHTML = str;
27714 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27717 * @class Roo.bootstrap.menu.Menu
27718 * @extends Roo.bootstrap.Component
27719 * Bootstrap Menu class - container for Menu
27720 * @cfg {String} html Text of the menu
27721 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27722 * @cfg {String} icon Font awesome icon
27723 * @cfg {String} pos Menu align to (top | bottom) default bottom
27727 * Create a new Menu
27728 * @param {Object} config The config object
27732 Roo.bootstrap.menu.Menu = function(config){
27733 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27737 * @event beforeshow
27738 * Fires before this menu is displayed
27739 * @param {Roo.bootstrap.menu.Menu} this
27743 * @event beforehide
27744 * Fires before this menu is hidden
27745 * @param {Roo.bootstrap.menu.Menu} this
27750 * Fires after this menu is displayed
27751 * @param {Roo.bootstrap.menu.Menu} this
27756 * Fires after this menu is hidden
27757 * @param {Roo.bootstrap.menu.Menu} this
27762 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27763 * @param {Roo.bootstrap.menu.Menu} this
27764 * @param {Roo.EventObject} e
27771 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
27775 weight : 'default',
27780 getChildContainer : function() {
27781 if(this.isSubMenu){
27785 return this.el.select('ul.dropdown-menu', true).first();
27788 getAutoCreate : function()
27793 cls : 'roo-menu-text',
27801 cls : 'fa ' + this.icon
27812 cls : 'dropdown-button btn btn-' + this.weight,
27817 cls : 'dropdown-toggle btn btn-' + this.weight,
27827 cls : 'dropdown-menu'
27833 if(this.pos == 'top'){
27834 cfg.cls += ' dropup';
27837 if(this.isSubMenu){
27840 cls : 'dropdown-menu'
27847 onRender : function(ct, position)
27849 this.isSubMenu = ct.hasClass('dropdown-submenu');
27851 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27854 initEvents : function()
27856 if(this.isSubMenu){
27860 this.hidden = true;
27862 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27863 this.triggerEl.on('click', this.onTriggerPress, this);
27865 this.buttonEl = this.el.select('button.dropdown-button', true).first();
27866 this.buttonEl.on('click', this.onClick, this);
27872 if(this.isSubMenu){
27876 return this.el.select('ul.dropdown-menu', true).first();
27879 onClick : function(e)
27881 this.fireEvent("click", this, e);
27884 onTriggerPress : function(e)
27886 if (this.isVisible()) {
27893 isVisible : function(){
27894 return !this.hidden;
27899 this.fireEvent("beforeshow", this);
27901 this.hidden = false;
27902 this.el.addClass('open');
27904 Roo.get(document).on("mouseup", this.onMouseUp, this);
27906 this.fireEvent("show", this);
27913 this.fireEvent("beforehide", this);
27915 this.hidden = true;
27916 this.el.removeClass('open');
27918 Roo.get(document).un("mouseup", this.onMouseUp);
27920 this.fireEvent("hide", this);
27923 onMouseUp : function()
27937 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27940 * @class Roo.bootstrap.menu.Item
27941 * @extends Roo.bootstrap.Component
27942 * Bootstrap MenuItem class
27943 * @cfg {Boolean} submenu (true | false) default false
27944 * @cfg {String} html text of the item
27945 * @cfg {String} href the link
27946 * @cfg {Boolean} disable (true | false) default false
27947 * @cfg {Boolean} preventDefault (true | false) default true
27948 * @cfg {String} icon Font awesome icon
27949 * @cfg {String} pos Submenu align to (left | right) default right
27953 * Create a new Item
27954 * @param {Object} config The config object
27958 Roo.bootstrap.menu.Item = function(config){
27959 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27963 * Fires when the mouse is hovering over this menu
27964 * @param {Roo.bootstrap.menu.Item} this
27965 * @param {Roo.EventObject} e
27970 * Fires when the mouse exits this menu
27971 * @param {Roo.bootstrap.menu.Item} this
27972 * @param {Roo.EventObject} e
27978 * The raw click event for the entire grid.
27979 * @param {Roo.EventObject} e
27985 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
27990 preventDefault: true,
27995 getAutoCreate : function()
28000 cls : 'roo-menu-item-text',
28008 cls : 'fa ' + this.icon
28017 href : this.href || '#',
28024 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28028 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28030 if(this.pos == 'left'){
28031 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28038 initEvents : function()
28040 this.el.on('mouseover', this.onMouseOver, this);
28041 this.el.on('mouseout', this.onMouseOut, this);
28043 this.el.select('a', true).first().on('click', this.onClick, this);
28047 onClick : function(e)
28049 if(this.preventDefault){
28050 e.preventDefault();
28053 this.fireEvent("click", this, e);
28056 onMouseOver : function(e)
28058 if(this.submenu && this.pos == 'left'){
28059 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28062 this.fireEvent("mouseover", this, e);
28065 onMouseOut : function(e)
28067 this.fireEvent("mouseout", this, e);
28079 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28082 * @class Roo.bootstrap.menu.Separator
28083 * @extends Roo.bootstrap.Component
28084 * Bootstrap Separator class
28087 * Create a new Separator
28088 * @param {Object} config The config object
28092 Roo.bootstrap.menu.Separator = function(config){
28093 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28096 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28098 getAutoCreate : function(){
28119 * @class Roo.bootstrap.Tooltip
28120 * Bootstrap Tooltip class
28121 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28122 * to determine which dom element triggers the tooltip.
28124 * It needs to add support for additional attributes like tooltip-position
28127 * Create a new Toolti
28128 * @param {Object} config The config object
28131 Roo.bootstrap.Tooltip = function(config){
28132 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28134 this.alignment = Roo.bootstrap.Tooltip.alignment;
28136 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28137 this.alignment = config.alignment;
28142 Roo.apply(Roo.bootstrap.Tooltip, {
28144 * @function init initialize tooltip monitoring.
28148 currentTip : false,
28149 currentRegion : false,
28155 Roo.get(document).on('mouseover', this.enter ,this);
28156 Roo.get(document).on('mouseout', this.leave, this);
28159 this.currentTip = new Roo.bootstrap.Tooltip();
28162 enter : function(ev)
28164 var dom = ev.getTarget();
28166 //Roo.log(['enter',dom]);
28167 var el = Roo.fly(dom);
28168 if (this.currentEl) {
28170 //Roo.log(this.currentEl);
28171 //Roo.log(this.currentEl.contains(dom));
28172 if (this.currentEl == el) {
28175 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28181 if (this.currentTip.el) {
28182 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28186 if(!el || el.dom == document){
28192 // you can not look for children, as if el is the body.. then everythign is the child..
28193 if (!el.attr('tooltip')) { //
28194 if (!el.select("[tooltip]").elements.length) {
28197 // is the mouse over this child...?
28198 bindEl = el.select("[tooltip]").first();
28199 var xy = ev.getXY();
28200 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28201 //Roo.log("not in region.");
28204 //Roo.log("child element over..");
28207 this.currentEl = bindEl;
28208 this.currentTip.bind(bindEl);
28209 this.currentRegion = Roo.lib.Region.getRegion(dom);
28210 this.currentTip.enter();
28213 leave : function(ev)
28215 var dom = ev.getTarget();
28216 //Roo.log(['leave',dom]);
28217 if (!this.currentEl) {
28222 if (dom != this.currentEl.dom) {
28225 var xy = ev.getXY();
28226 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28229 // only activate leave if mouse cursor is outside... bounding box..
28234 if (this.currentTip) {
28235 this.currentTip.leave();
28237 //Roo.log('clear currentEl');
28238 this.currentEl = false;
28243 'left' : ['r-l', [-2,0], 'right'],
28244 'right' : ['l-r', [2,0], 'left'],
28245 'bottom' : ['t-b', [0,2], 'top'],
28246 'top' : [ 'b-t', [0,-2], 'bottom']
28252 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28257 delay : null, // can be { show : 300 , hide: 500}
28261 hoverState : null, //???
28263 placement : 'bottom',
28267 getAutoCreate : function(){
28274 cls : 'tooltip-arrow arrow'
28277 cls : 'tooltip-inner'
28284 bind : function(el)
28289 initEvents : function()
28291 this.arrowEl = this.el.select('.arrow', true).first();
28294 enter : function () {
28296 if (this.timeout != null) {
28297 clearTimeout(this.timeout);
28300 this.hoverState = 'in';
28301 //Roo.log("enter - show");
28302 if (!this.delay || !this.delay.show) {
28307 this.timeout = setTimeout(function () {
28308 if (_t.hoverState == 'in') {
28311 }, this.delay.show);
28315 clearTimeout(this.timeout);
28317 this.hoverState = 'out';
28318 if (!this.delay || !this.delay.hide) {
28324 this.timeout = setTimeout(function () {
28325 //Roo.log("leave - timeout");
28327 if (_t.hoverState == 'out') {
28329 Roo.bootstrap.Tooltip.currentEl = false;
28334 show : function (msg)
28337 this.render(document.body);
28340 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28342 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28344 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28346 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28347 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28349 var placement = typeof this.placement == 'function' ?
28350 this.placement.call(this, this.el, on_el) :
28353 var autoToken = /\s?auto?\s?/i;
28354 var autoPlace = autoToken.test(placement);
28356 placement = placement.replace(autoToken, '') || 'top';
28360 //this.el.setXY([0,0]);
28362 //this.el.dom.style.display='block';
28364 //this.el.appendTo(on_el);
28366 var p = this.getPosition();
28367 var box = this.el.getBox();
28373 var align = this.alignment[placement];
28375 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28377 if(placement == 'top' || placement == 'bottom'){
28379 placement = 'right';
28382 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28383 placement = 'left';
28386 var scroll = Roo.select('body', true).first().getScroll();
28388 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28392 align = this.alignment[placement];
28395 this.el.alignTo(this.bindEl, align[0],align[1]);
28396 //var arrow = this.el.select('.arrow',true).first();
28397 //arrow.set(align[2],
28399 this.el.addClass(placement);
28400 this.el.addClass("bs-tooltip-"+ placement);
28402 this.el.addClass('in fade show');
28404 this.hoverState = null;
28406 if (this.el.hasClass('fade')) {
28421 //this.el.setXY([0,0]);
28422 this.el.removeClass(['show', 'in']);
28438 * @class Roo.bootstrap.LocationPicker
28439 * @extends Roo.bootstrap.Component
28440 * Bootstrap LocationPicker class
28441 * @cfg {Number} latitude Position when init default 0
28442 * @cfg {Number} longitude Position when init default 0
28443 * @cfg {Number} zoom default 15
28444 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28445 * @cfg {Boolean} mapTypeControl default false
28446 * @cfg {Boolean} disableDoubleClickZoom default false
28447 * @cfg {Boolean} scrollwheel default true
28448 * @cfg {Boolean} streetViewControl default false
28449 * @cfg {Number} radius default 0
28450 * @cfg {String} locationName
28451 * @cfg {Boolean} draggable default true
28452 * @cfg {Boolean} enableAutocomplete default false
28453 * @cfg {Boolean} enableReverseGeocode default true
28454 * @cfg {String} markerTitle
28457 * Create a new LocationPicker
28458 * @param {Object} config The config object
28462 Roo.bootstrap.LocationPicker = function(config){
28464 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28469 * Fires when the picker initialized.
28470 * @param {Roo.bootstrap.LocationPicker} this
28471 * @param {Google Location} location
28475 * @event positionchanged
28476 * Fires when the picker position changed.
28477 * @param {Roo.bootstrap.LocationPicker} this
28478 * @param {Google Location} location
28480 positionchanged : true,
28483 * Fires when the map resize.
28484 * @param {Roo.bootstrap.LocationPicker} this
28489 * Fires when the map show.
28490 * @param {Roo.bootstrap.LocationPicker} this
28495 * Fires when the map hide.
28496 * @param {Roo.bootstrap.LocationPicker} this
28501 * Fires when click the map.
28502 * @param {Roo.bootstrap.LocationPicker} this
28503 * @param {Map event} e
28507 * @event mapRightClick
28508 * Fires when right click the map.
28509 * @param {Roo.bootstrap.LocationPicker} this
28510 * @param {Map event} e
28512 mapRightClick : true,
28514 * @event markerClick
28515 * Fires when click the marker.
28516 * @param {Roo.bootstrap.LocationPicker} this
28517 * @param {Map event} e
28519 markerClick : true,
28521 * @event markerRightClick
28522 * Fires when right click the marker.
28523 * @param {Roo.bootstrap.LocationPicker} this
28524 * @param {Map event} e
28526 markerRightClick : true,
28528 * @event OverlayViewDraw
28529 * Fires when OverlayView Draw
28530 * @param {Roo.bootstrap.LocationPicker} this
28532 OverlayViewDraw : true,
28534 * @event OverlayViewOnAdd
28535 * Fires when OverlayView Draw
28536 * @param {Roo.bootstrap.LocationPicker} this
28538 OverlayViewOnAdd : true,
28540 * @event OverlayViewOnRemove
28541 * Fires when OverlayView Draw
28542 * @param {Roo.bootstrap.LocationPicker} this
28544 OverlayViewOnRemove : true,
28546 * @event OverlayViewShow
28547 * Fires when OverlayView Draw
28548 * @param {Roo.bootstrap.LocationPicker} this
28549 * @param {Pixel} cpx
28551 OverlayViewShow : true,
28553 * @event OverlayViewHide
28554 * Fires when OverlayView Draw
28555 * @param {Roo.bootstrap.LocationPicker} this
28557 OverlayViewHide : true,
28559 * @event loadexception
28560 * Fires when load google lib failed.
28561 * @param {Roo.bootstrap.LocationPicker} this
28563 loadexception : true
28568 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
28570 gMapContext: false,
28576 mapTypeControl: false,
28577 disableDoubleClickZoom: false,
28579 streetViewControl: false,
28583 enableAutocomplete: false,
28584 enableReverseGeocode: true,
28587 getAutoCreate: function()
28592 cls: 'roo-location-picker'
28598 initEvents: function(ct, position)
28600 if(!this.el.getWidth() || this.isApplied()){
28604 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28609 initial: function()
28611 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28612 this.fireEvent('loadexception', this);
28616 if(!this.mapTypeId){
28617 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28620 this.gMapContext = this.GMapContext();
28622 this.initOverlayView();
28624 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28628 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28629 _this.setPosition(_this.gMapContext.marker.position);
28632 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28633 _this.fireEvent('mapClick', this, event);
28637 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28638 _this.fireEvent('mapRightClick', this, event);
28642 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28643 _this.fireEvent('markerClick', this, event);
28647 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28648 _this.fireEvent('markerRightClick', this, event);
28652 this.setPosition(this.gMapContext.location);
28654 this.fireEvent('initial', this, this.gMapContext.location);
28657 initOverlayView: function()
28661 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28665 _this.fireEvent('OverlayViewDraw', _this);
28670 _this.fireEvent('OverlayViewOnAdd', _this);
28673 onRemove: function()
28675 _this.fireEvent('OverlayViewOnRemove', _this);
28678 show: function(cpx)
28680 _this.fireEvent('OverlayViewShow', _this, cpx);
28685 _this.fireEvent('OverlayViewHide', _this);
28691 fromLatLngToContainerPixel: function(event)
28693 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28696 isApplied: function()
28698 return this.getGmapContext() == false ? false : true;
28701 getGmapContext: function()
28703 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28706 GMapContext: function()
28708 var position = new google.maps.LatLng(this.latitude, this.longitude);
28710 var _map = new google.maps.Map(this.el.dom, {
28713 mapTypeId: this.mapTypeId,
28714 mapTypeControl: this.mapTypeControl,
28715 disableDoubleClickZoom: this.disableDoubleClickZoom,
28716 scrollwheel: this.scrollwheel,
28717 streetViewControl: this.streetViewControl,
28718 locationName: this.locationName,
28719 draggable: this.draggable,
28720 enableAutocomplete: this.enableAutocomplete,
28721 enableReverseGeocode: this.enableReverseGeocode
28724 var _marker = new google.maps.Marker({
28725 position: position,
28727 title: this.markerTitle,
28728 draggable: this.draggable
28735 location: position,
28736 radius: this.radius,
28737 locationName: this.locationName,
28738 addressComponents: {
28739 formatted_address: null,
28740 addressLine1: null,
28741 addressLine2: null,
28743 streetNumber: null,
28747 stateOrProvince: null
28750 domContainer: this.el.dom,
28751 geodecoder: new google.maps.Geocoder()
28755 drawCircle: function(center, radius, options)
28757 if (this.gMapContext.circle != null) {
28758 this.gMapContext.circle.setMap(null);
28762 options = Roo.apply({}, options, {
28763 strokeColor: "#0000FF",
28764 strokeOpacity: .35,
28766 fillColor: "#0000FF",
28770 options.map = this.gMapContext.map;
28771 options.radius = radius;
28772 options.center = center;
28773 this.gMapContext.circle = new google.maps.Circle(options);
28774 return this.gMapContext.circle;
28780 setPosition: function(location)
28782 this.gMapContext.location = location;
28783 this.gMapContext.marker.setPosition(location);
28784 this.gMapContext.map.panTo(location);
28785 this.drawCircle(location, this.gMapContext.radius, {});
28789 if (this.gMapContext.settings.enableReverseGeocode) {
28790 this.gMapContext.geodecoder.geocode({
28791 latLng: this.gMapContext.location
28792 }, function(results, status) {
28794 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28795 _this.gMapContext.locationName = results[0].formatted_address;
28796 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28798 _this.fireEvent('positionchanged', this, location);
28805 this.fireEvent('positionchanged', this, location);
28810 google.maps.event.trigger(this.gMapContext.map, "resize");
28812 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28814 this.fireEvent('resize', this);
28817 setPositionByLatLng: function(latitude, longitude)
28819 this.setPosition(new google.maps.LatLng(latitude, longitude));
28822 getCurrentPosition: function()
28825 latitude: this.gMapContext.location.lat(),
28826 longitude: this.gMapContext.location.lng()
28830 getAddressName: function()
28832 return this.gMapContext.locationName;
28835 getAddressComponents: function()
28837 return this.gMapContext.addressComponents;
28840 address_component_from_google_geocode: function(address_components)
28844 for (var i = 0; i < address_components.length; i++) {
28845 var component = address_components[i];
28846 if (component.types.indexOf("postal_code") >= 0) {
28847 result.postalCode = component.short_name;
28848 } else if (component.types.indexOf("street_number") >= 0) {
28849 result.streetNumber = component.short_name;
28850 } else if (component.types.indexOf("route") >= 0) {
28851 result.streetName = component.short_name;
28852 } else if (component.types.indexOf("neighborhood") >= 0) {
28853 result.city = component.short_name;
28854 } else if (component.types.indexOf("locality") >= 0) {
28855 result.city = component.short_name;
28856 } else if (component.types.indexOf("sublocality") >= 0) {
28857 result.district = component.short_name;
28858 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28859 result.stateOrProvince = component.short_name;
28860 } else if (component.types.indexOf("country") >= 0) {
28861 result.country = component.short_name;
28865 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28866 result.addressLine2 = "";
28870 setZoomLevel: function(zoom)
28872 this.gMapContext.map.setZoom(zoom);
28885 this.fireEvent('show', this);
28896 this.fireEvent('hide', this);
28901 Roo.apply(Roo.bootstrap.LocationPicker, {
28903 OverlayView : function(map, options)
28905 options = options || {};
28912 * @class Roo.bootstrap.Alert
28913 * @extends Roo.bootstrap.Component
28914 * Bootstrap Alert class - shows an alert area box
28916 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28917 Enter a valid email address
28920 * @cfg {String} title The title of alert
28921 * @cfg {String} html The content of alert
28922 * @cfg {String} weight ( success | info | warning | danger )
28923 * @cfg {String} faicon font-awesomeicon
28926 * Create a new alert
28927 * @param {Object} config The config object
28931 Roo.bootstrap.Alert = function(config){
28932 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28936 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
28943 getAutoCreate : function()
28952 cls : 'roo-alert-icon'
28957 cls : 'roo-alert-title',
28962 cls : 'roo-alert-text',
28969 cfg.cn[0].cls += ' fa ' + this.faicon;
28973 cfg.cls += ' alert-' + this.weight;
28979 initEvents: function()
28981 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28984 setTitle : function(str)
28986 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28989 setText : function(str)
28991 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28994 setWeight : function(weight)
28997 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29000 this.weight = weight;
29002 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29005 setIcon : function(icon)
29008 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29011 this.faicon = icon;
29013 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29034 * @class Roo.bootstrap.UploadCropbox
29035 * @extends Roo.bootstrap.Component
29036 * Bootstrap UploadCropbox class
29037 * @cfg {String} emptyText show when image has been loaded
29038 * @cfg {String} rotateNotify show when image too small to rotate
29039 * @cfg {Number} errorTimeout default 3000
29040 * @cfg {Number} minWidth default 300
29041 * @cfg {Number} minHeight default 300
29042 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29043 * @cfg {Boolean} isDocument (true|false) default false
29044 * @cfg {String} url action url
29045 * @cfg {String} paramName default 'imageUpload'
29046 * @cfg {String} method default POST
29047 * @cfg {Boolean} loadMask (true|false) default true
29048 * @cfg {Boolean} loadingText default 'Loading...'
29051 * Create a new UploadCropbox
29052 * @param {Object} config The config object
29055 Roo.bootstrap.UploadCropbox = function(config){
29056 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29060 * @event beforeselectfile
29061 * Fire before select file
29062 * @param {Roo.bootstrap.UploadCropbox} this
29064 "beforeselectfile" : true,
29067 * Fire after initEvent
29068 * @param {Roo.bootstrap.UploadCropbox} this
29073 * Fire after initEvent
29074 * @param {Roo.bootstrap.UploadCropbox} this
29075 * @param {String} data
29080 * Fire when preparing the file data
29081 * @param {Roo.bootstrap.UploadCropbox} this
29082 * @param {Object} file
29087 * Fire when get exception
29088 * @param {Roo.bootstrap.UploadCropbox} this
29089 * @param {XMLHttpRequest} xhr
29091 "exception" : true,
29093 * @event beforeloadcanvas
29094 * Fire before load the canvas
29095 * @param {Roo.bootstrap.UploadCropbox} this
29096 * @param {String} src
29098 "beforeloadcanvas" : true,
29101 * Fire when trash image
29102 * @param {Roo.bootstrap.UploadCropbox} this
29107 * Fire when download the image
29108 * @param {Roo.bootstrap.UploadCropbox} this
29112 * @event footerbuttonclick
29113 * Fire when footerbuttonclick
29114 * @param {Roo.bootstrap.UploadCropbox} this
29115 * @param {String} type
29117 "footerbuttonclick" : true,
29121 * @param {Roo.bootstrap.UploadCropbox} this
29126 * Fire when rotate the image
29127 * @param {Roo.bootstrap.UploadCropbox} this
29128 * @param {String} pos
29133 * Fire when inspect the file
29134 * @param {Roo.bootstrap.UploadCropbox} this
29135 * @param {Object} file
29140 * Fire when xhr upload the file
29141 * @param {Roo.bootstrap.UploadCropbox} this
29142 * @param {Object} data
29147 * Fire when arrange the file data
29148 * @param {Roo.bootstrap.UploadCropbox} this
29149 * @param {Object} formData
29154 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29157 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29159 emptyText : 'Click to upload image',
29160 rotateNotify : 'Image is too small to rotate',
29161 errorTimeout : 3000,
29175 cropType : 'image/jpeg',
29177 canvasLoaded : false,
29178 isDocument : false,
29180 paramName : 'imageUpload',
29182 loadingText : 'Loading...',
29185 getAutoCreate : function()
29189 cls : 'roo-upload-cropbox',
29193 cls : 'roo-upload-cropbox-selector',
29198 cls : 'roo-upload-cropbox-body',
29199 style : 'cursor:pointer',
29203 cls : 'roo-upload-cropbox-preview'
29207 cls : 'roo-upload-cropbox-thumb'
29211 cls : 'roo-upload-cropbox-empty-notify',
29212 html : this.emptyText
29216 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29217 html : this.rotateNotify
29223 cls : 'roo-upload-cropbox-footer',
29226 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29236 onRender : function(ct, position)
29238 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29240 if (this.buttons.length) {
29242 Roo.each(this.buttons, function(bb) {
29244 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29246 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29252 this.maskEl = this.el;
29256 initEvents : function()
29258 this.urlAPI = (window.createObjectURL && window) ||
29259 (window.URL && URL.revokeObjectURL && URL) ||
29260 (window.webkitURL && webkitURL);
29262 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29263 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29265 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29266 this.selectorEl.hide();
29268 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29269 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29271 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29272 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29273 this.thumbEl.hide();
29275 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29276 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29278 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29279 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29280 this.errorEl.hide();
29282 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29283 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29284 this.footerEl.hide();
29286 this.setThumbBoxSize();
29292 this.fireEvent('initial', this);
29299 window.addEventListener("resize", function() { _this.resize(); } );
29301 this.bodyEl.on('click', this.beforeSelectFile, this);
29304 this.bodyEl.on('touchstart', this.onTouchStart, this);
29305 this.bodyEl.on('touchmove', this.onTouchMove, this);
29306 this.bodyEl.on('touchend', this.onTouchEnd, this);
29310 this.bodyEl.on('mousedown', this.onMouseDown, this);
29311 this.bodyEl.on('mousemove', this.onMouseMove, this);
29312 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29313 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29314 Roo.get(document).on('mouseup', this.onMouseUp, this);
29317 this.selectorEl.on('change', this.onFileSelected, this);
29323 this.baseScale = 1;
29325 this.baseRotate = 1;
29326 this.dragable = false;
29327 this.pinching = false;
29330 this.cropData = false;
29331 this.notifyEl.dom.innerHTML = this.emptyText;
29333 this.selectorEl.dom.value = '';
29337 resize : function()
29339 if(this.fireEvent('resize', this) != false){
29340 this.setThumbBoxPosition();
29341 this.setCanvasPosition();
29345 onFooterButtonClick : function(e, el, o, type)
29348 case 'rotate-left' :
29349 this.onRotateLeft(e);
29351 case 'rotate-right' :
29352 this.onRotateRight(e);
29355 this.beforeSelectFile(e);
29370 this.fireEvent('footerbuttonclick', this, type);
29373 beforeSelectFile : function(e)
29375 e.preventDefault();
29377 if(this.fireEvent('beforeselectfile', this) != false){
29378 this.selectorEl.dom.click();
29382 onFileSelected : function(e)
29384 e.preventDefault();
29386 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29390 var file = this.selectorEl.dom.files[0];
29392 if(this.fireEvent('inspect', this, file) != false){
29393 this.prepare(file);
29398 trash : function(e)
29400 this.fireEvent('trash', this);
29403 download : function(e)
29405 this.fireEvent('download', this);
29408 loadCanvas : function(src)
29410 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29414 this.imageEl = document.createElement('img');
29418 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29420 this.imageEl.src = src;
29424 onLoadCanvas : function()
29426 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29427 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29429 this.bodyEl.un('click', this.beforeSelectFile, this);
29431 this.notifyEl.hide();
29432 this.thumbEl.show();
29433 this.footerEl.show();
29435 this.baseRotateLevel();
29437 if(this.isDocument){
29438 this.setThumbBoxSize();
29441 this.setThumbBoxPosition();
29443 this.baseScaleLevel();
29449 this.canvasLoaded = true;
29452 this.maskEl.unmask();
29457 setCanvasPosition : function()
29459 if(!this.canvasEl){
29463 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29464 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29466 this.previewEl.setLeft(pw);
29467 this.previewEl.setTop(ph);
29471 onMouseDown : function(e)
29475 this.dragable = true;
29476 this.pinching = false;
29478 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29479 this.dragable = false;
29483 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29484 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29488 onMouseMove : function(e)
29492 if(!this.canvasLoaded){
29496 if (!this.dragable){
29500 var minX = Math.ceil(this.thumbEl.getLeft(true));
29501 var minY = Math.ceil(this.thumbEl.getTop(true));
29503 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29504 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29506 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29507 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29509 x = x - this.mouseX;
29510 y = y - this.mouseY;
29512 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29513 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29515 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29516 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29518 this.previewEl.setLeft(bgX);
29519 this.previewEl.setTop(bgY);
29521 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29522 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29525 onMouseUp : function(e)
29529 this.dragable = false;
29532 onMouseWheel : function(e)
29536 this.startScale = this.scale;
29538 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29540 if(!this.zoomable()){
29541 this.scale = this.startScale;
29550 zoomable : function()
29552 var minScale = this.thumbEl.getWidth() / this.minWidth;
29554 if(this.minWidth < this.minHeight){
29555 minScale = this.thumbEl.getHeight() / this.minHeight;
29558 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29559 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29563 (this.rotate == 0 || this.rotate == 180) &&
29565 width > this.imageEl.OriginWidth ||
29566 height > this.imageEl.OriginHeight ||
29567 (width < this.minWidth && height < this.minHeight)
29575 (this.rotate == 90 || this.rotate == 270) &&
29577 width > this.imageEl.OriginWidth ||
29578 height > this.imageEl.OriginHeight ||
29579 (width < this.minHeight && height < this.minWidth)
29586 !this.isDocument &&
29587 (this.rotate == 0 || this.rotate == 180) &&
29589 width < this.minWidth ||
29590 width > this.imageEl.OriginWidth ||
29591 height < this.minHeight ||
29592 height > this.imageEl.OriginHeight
29599 !this.isDocument &&
29600 (this.rotate == 90 || this.rotate == 270) &&
29602 width < this.minHeight ||
29603 width > this.imageEl.OriginWidth ||
29604 height < this.minWidth ||
29605 height > this.imageEl.OriginHeight
29615 onRotateLeft : function(e)
29617 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29619 var minScale = this.thumbEl.getWidth() / this.minWidth;
29621 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29622 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29624 this.startScale = this.scale;
29626 while (this.getScaleLevel() < minScale){
29628 this.scale = this.scale + 1;
29630 if(!this.zoomable()){
29635 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29636 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29641 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29648 this.scale = this.startScale;
29650 this.onRotateFail();
29655 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29657 if(this.isDocument){
29658 this.setThumbBoxSize();
29659 this.setThumbBoxPosition();
29660 this.setCanvasPosition();
29665 this.fireEvent('rotate', this, 'left');
29669 onRotateRight : function(e)
29671 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29673 var minScale = this.thumbEl.getWidth() / this.minWidth;
29675 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29676 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29678 this.startScale = this.scale;
29680 while (this.getScaleLevel() < minScale){
29682 this.scale = this.scale + 1;
29684 if(!this.zoomable()){
29689 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29690 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29695 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29702 this.scale = this.startScale;
29704 this.onRotateFail();
29709 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29711 if(this.isDocument){
29712 this.setThumbBoxSize();
29713 this.setThumbBoxPosition();
29714 this.setCanvasPosition();
29719 this.fireEvent('rotate', this, 'right');
29722 onRotateFail : function()
29724 this.errorEl.show(true);
29728 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29733 this.previewEl.dom.innerHTML = '';
29735 var canvasEl = document.createElement("canvas");
29737 var contextEl = canvasEl.getContext("2d");
29739 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29740 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29741 var center = this.imageEl.OriginWidth / 2;
29743 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29744 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29745 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29746 center = this.imageEl.OriginHeight / 2;
29749 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29751 contextEl.translate(center, center);
29752 contextEl.rotate(this.rotate * Math.PI / 180);
29754 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29756 this.canvasEl = document.createElement("canvas");
29758 this.contextEl = this.canvasEl.getContext("2d");
29760 switch (this.rotate) {
29763 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29764 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29766 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29771 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29772 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29774 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29775 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);
29779 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29784 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29785 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29787 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29788 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);
29792 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);
29797 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29798 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29800 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29801 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29805 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);
29812 this.previewEl.appendChild(this.canvasEl);
29814 this.setCanvasPosition();
29819 if(!this.canvasLoaded){
29823 var imageCanvas = document.createElement("canvas");
29825 var imageContext = imageCanvas.getContext("2d");
29827 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29828 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29830 var center = imageCanvas.width / 2;
29832 imageContext.translate(center, center);
29834 imageContext.rotate(this.rotate * Math.PI / 180);
29836 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29838 var canvas = document.createElement("canvas");
29840 var context = canvas.getContext("2d");
29842 canvas.width = this.minWidth;
29843 canvas.height = this.minHeight;
29845 switch (this.rotate) {
29848 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29849 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29851 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29852 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29854 var targetWidth = this.minWidth - 2 * x;
29855 var targetHeight = this.minHeight - 2 * y;
29859 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29860 scale = targetWidth / width;
29863 if(x > 0 && y == 0){
29864 scale = targetHeight / height;
29867 if(x > 0 && y > 0){
29868 scale = targetWidth / width;
29870 if(width < height){
29871 scale = targetHeight / height;
29875 context.scale(scale, scale);
29877 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29878 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29880 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29881 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29883 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29888 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29889 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29891 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29892 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29894 var targetWidth = this.minWidth - 2 * x;
29895 var targetHeight = this.minHeight - 2 * y;
29899 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29900 scale = targetWidth / width;
29903 if(x > 0 && y == 0){
29904 scale = targetHeight / height;
29907 if(x > 0 && y > 0){
29908 scale = targetWidth / width;
29910 if(width < height){
29911 scale = targetHeight / height;
29915 context.scale(scale, scale);
29917 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29918 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29920 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29921 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29923 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29925 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29930 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29931 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29933 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29934 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29936 var targetWidth = this.minWidth - 2 * x;
29937 var targetHeight = this.minHeight - 2 * y;
29941 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29942 scale = targetWidth / width;
29945 if(x > 0 && y == 0){
29946 scale = targetHeight / height;
29949 if(x > 0 && y > 0){
29950 scale = targetWidth / width;
29952 if(width < height){
29953 scale = targetHeight / height;
29957 context.scale(scale, scale);
29959 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29960 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29962 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29963 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29965 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29966 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29968 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29973 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29974 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29976 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29977 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29979 var targetWidth = this.minWidth - 2 * x;
29980 var targetHeight = this.minHeight - 2 * y;
29984 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29985 scale = targetWidth / width;
29988 if(x > 0 && y == 0){
29989 scale = targetHeight / height;
29992 if(x > 0 && y > 0){
29993 scale = targetWidth / width;
29995 if(width < height){
29996 scale = targetHeight / height;
30000 context.scale(scale, scale);
30002 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30003 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30005 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30006 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30008 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30010 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30017 this.cropData = canvas.toDataURL(this.cropType);
30019 if(this.fireEvent('crop', this, this.cropData) !== false){
30020 this.process(this.file, this.cropData);
30027 setThumbBoxSize : function()
30031 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30032 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30033 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30035 this.minWidth = width;
30036 this.minHeight = height;
30038 if(this.rotate == 90 || this.rotate == 270){
30039 this.minWidth = height;
30040 this.minHeight = width;
30045 width = Math.ceil(this.minWidth * height / this.minHeight);
30047 if(this.minWidth > this.minHeight){
30049 height = Math.ceil(this.minHeight * width / this.minWidth);
30052 this.thumbEl.setStyle({
30053 width : width + 'px',
30054 height : height + 'px'
30061 setThumbBoxPosition : function()
30063 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30064 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30066 this.thumbEl.setLeft(x);
30067 this.thumbEl.setTop(y);
30071 baseRotateLevel : function()
30073 this.baseRotate = 1;
30076 typeof(this.exif) != 'undefined' &&
30077 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30078 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30080 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30083 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30087 baseScaleLevel : function()
30091 if(this.isDocument){
30093 if(this.baseRotate == 6 || this.baseRotate == 8){
30095 height = this.thumbEl.getHeight();
30096 this.baseScale = height / this.imageEl.OriginWidth;
30098 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30099 width = this.thumbEl.getWidth();
30100 this.baseScale = width / this.imageEl.OriginHeight;
30106 height = this.thumbEl.getHeight();
30107 this.baseScale = height / this.imageEl.OriginHeight;
30109 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30110 width = this.thumbEl.getWidth();
30111 this.baseScale = width / this.imageEl.OriginWidth;
30117 if(this.baseRotate == 6 || this.baseRotate == 8){
30119 width = this.thumbEl.getHeight();
30120 this.baseScale = width / this.imageEl.OriginHeight;
30122 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30123 height = this.thumbEl.getWidth();
30124 this.baseScale = height / this.imageEl.OriginHeight;
30127 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30128 height = this.thumbEl.getWidth();
30129 this.baseScale = height / this.imageEl.OriginHeight;
30131 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30132 width = this.thumbEl.getHeight();
30133 this.baseScale = width / this.imageEl.OriginWidth;
30140 width = this.thumbEl.getWidth();
30141 this.baseScale = width / this.imageEl.OriginWidth;
30143 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30144 height = this.thumbEl.getHeight();
30145 this.baseScale = height / this.imageEl.OriginHeight;
30148 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30150 height = this.thumbEl.getHeight();
30151 this.baseScale = height / this.imageEl.OriginHeight;
30153 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30154 width = this.thumbEl.getWidth();
30155 this.baseScale = width / this.imageEl.OriginWidth;
30163 getScaleLevel : function()
30165 return this.baseScale * Math.pow(1.1, this.scale);
30168 onTouchStart : function(e)
30170 if(!this.canvasLoaded){
30171 this.beforeSelectFile(e);
30175 var touches = e.browserEvent.touches;
30181 if(touches.length == 1){
30182 this.onMouseDown(e);
30186 if(touches.length != 2){
30192 for(var i = 0, finger; finger = touches[i]; i++){
30193 coords.push(finger.pageX, finger.pageY);
30196 var x = Math.pow(coords[0] - coords[2], 2);
30197 var y = Math.pow(coords[1] - coords[3], 2);
30199 this.startDistance = Math.sqrt(x + y);
30201 this.startScale = this.scale;
30203 this.pinching = true;
30204 this.dragable = false;
30208 onTouchMove : function(e)
30210 if(!this.pinching && !this.dragable){
30214 var touches = e.browserEvent.touches;
30221 this.onMouseMove(e);
30227 for(var i = 0, finger; finger = touches[i]; i++){
30228 coords.push(finger.pageX, finger.pageY);
30231 var x = Math.pow(coords[0] - coords[2], 2);
30232 var y = Math.pow(coords[1] - coords[3], 2);
30234 this.endDistance = Math.sqrt(x + y);
30236 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30238 if(!this.zoomable()){
30239 this.scale = this.startScale;
30247 onTouchEnd : function(e)
30249 this.pinching = false;
30250 this.dragable = false;
30254 process : function(file, crop)
30257 this.maskEl.mask(this.loadingText);
30260 this.xhr = new XMLHttpRequest();
30262 file.xhr = this.xhr;
30264 this.xhr.open(this.method, this.url, true);
30267 "Accept": "application/json",
30268 "Cache-Control": "no-cache",
30269 "X-Requested-With": "XMLHttpRequest"
30272 for (var headerName in headers) {
30273 var headerValue = headers[headerName];
30275 this.xhr.setRequestHeader(headerName, headerValue);
30281 this.xhr.onload = function()
30283 _this.xhrOnLoad(_this.xhr);
30286 this.xhr.onerror = function()
30288 _this.xhrOnError(_this.xhr);
30291 var formData = new FormData();
30293 formData.append('returnHTML', 'NO');
30296 formData.append('crop', crop);
30299 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30300 formData.append(this.paramName, file, file.name);
30303 if(typeof(file.filename) != 'undefined'){
30304 formData.append('filename', file.filename);
30307 if(typeof(file.mimetype) != 'undefined'){
30308 formData.append('mimetype', file.mimetype);
30311 if(this.fireEvent('arrange', this, formData) != false){
30312 this.xhr.send(formData);
30316 xhrOnLoad : function(xhr)
30319 this.maskEl.unmask();
30322 if (xhr.readyState !== 4) {
30323 this.fireEvent('exception', this, xhr);
30327 var response = Roo.decode(xhr.responseText);
30329 if(!response.success){
30330 this.fireEvent('exception', this, xhr);
30334 var response = Roo.decode(xhr.responseText);
30336 this.fireEvent('upload', this, response);
30340 xhrOnError : function()
30343 this.maskEl.unmask();
30346 Roo.log('xhr on error');
30348 var response = Roo.decode(xhr.responseText);
30354 prepare : function(file)
30357 this.maskEl.mask(this.loadingText);
30363 if(typeof(file) === 'string'){
30364 this.loadCanvas(file);
30368 if(!file || !this.urlAPI){
30373 this.cropType = file.type;
30377 if(this.fireEvent('prepare', this, this.file) != false){
30379 var reader = new FileReader();
30381 reader.onload = function (e) {
30382 if (e.target.error) {
30383 Roo.log(e.target.error);
30387 var buffer = e.target.result,
30388 dataView = new DataView(buffer),
30390 maxOffset = dataView.byteLength - 4,
30394 if (dataView.getUint16(0) === 0xffd8) {
30395 while (offset < maxOffset) {
30396 markerBytes = dataView.getUint16(offset);
30398 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30399 markerLength = dataView.getUint16(offset + 2) + 2;
30400 if (offset + markerLength > dataView.byteLength) {
30401 Roo.log('Invalid meta data: Invalid segment size.');
30405 if(markerBytes == 0xffe1){
30406 _this.parseExifData(
30413 offset += markerLength;
30423 var url = _this.urlAPI.createObjectURL(_this.file);
30425 _this.loadCanvas(url);
30430 reader.readAsArrayBuffer(this.file);
30436 parseExifData : function(dataView, offset, length)
30438 var tiffOffset = offset + 10,
30442 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30443 // No Exif data, might be XMP data instead
30447 // Check for the ASCII code for "Exif" (0x45786966):
30448 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30449 // No Exif data, might be XMP data instead
30452 if (tiffOffset + 8 > dataView.byteLength) {
30453 Roo.log('Invalid Exif data: Invalid segment size.');
30456 // Check for the two null bytes:
30457 if (dataView.getUint16(offset + 8) !== 0x0000) {
30458 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30461 // Check the byte alignment:
30462 switch (dataView.getUint16(tiffOffset)) {
30464 littleEndian = true;
30467 littleEndian = false;
30470 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30473 // Check for the TIFF tag marker (0x002A):
30474 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30475 Roo.log('Invalid Exif data: Missing TIFF marker.');
30478 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30479 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30481 this.parseExifTags(
30484 tiffOffset + dirOffset,
30489 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30494 if (dirOffset + 6 > dataView.byteLength) {
30495 Roo.log('Invalid Exif data: Invalid directory offset.');
30498 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30499 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30500 if (dirEndOffset + 4 > dataView.byteLength) {
30501 Roo.log('Invalid Exif data: Invalid directory size.');
30504 for (i = 0; i < tagsNumber; i += 1) {
30508 dirOffset + 2 + 12 * i, // tag offset
30512 // Return the offset to the next directory:
30513 return dataView.getUint32(dirEndOffset, littleEndian);
30516 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30518 var tag = dataView.getUint16(offset, littleEndian);
30520 this.exif[tag] = this.getExifValue(
30524 dataView.getUint16(offset + 2, littleEndian), // tag type
30525 dataView.getUint32(offset + 4, littleEndian), // tag length
30530 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30532 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30541 Roo.log('Invalid Exif data: Invalid tag type.');
30545 tagSize = tagType.size * length;
30546 // Determine if the value is contained in the dataOffset bytes,
30547 // or if the value at the dataOffset is a pointer to the actual data:
30548 dataOffset = tagSize > 4 ?
30549 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30550 if (dataOffset + tagSize > dataView.byteLength) {
30551 Roo.log('Invalid Exif data: Invalid data offset.');
30554 if (length === 1) {
30555 return tagType.getValue(dataView, dataOffset, littleEndian);
30558 for (i = 0; i < length; i += 1) {
30559 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30562 if (tagType.ascii) {
30564 // Concatenate the chars:
30565 for (i = 0; i < values.length; i += 1) {
30567 // Ignore the terminating NULL byte(s):
30568 if (c === '\u0000') {
30580 Roo.apply(Roo.bootstrap.UploadCropbox, {
30582 'Orientation': 0x0112
30586 1: 0, //'top-left',
30588 3: 180, //'bottom-right',
30589 // 4: 'bottom-left',
30591 6: 90, //'right-top',
30592 // 7: 'right-bottom',
30593 8: 270 //'left-bottom'
30597 // byte, 8-bit unsigned int:
30599 getValue: function (dataView, dataOffset) {
30600 return dataView.getUint8(dataOffset);
30604 // ascii, 8-bit byte:
30606 getValue: function (dataView, dataOffset) {
30607 return String.fromCharCode(dataView.getUint8(dataOffset));
30612 // short, 16 bit int:
30614 getValue: function (dataView, dataOffset, littleEndian) {
30615 return dataView.getUint16(dataOffset, littleEndian);
30619 // long, 32 bit int:
30621 getValue: function (dataView, dataOffset, littleEndian) {
30622 return dataView.getUint32(dataOffset, littleEndian);
30626 // rational = two long values, first is numerator, second is denominator:
30628 getValue: function (dataView, dataOffset, littleEndian) {
30629 return dataView.getUint32(dataOffset, littleEndian) /
30630 dataView.getUint32(dataOffset + 4, littleEndian);
30634 // slong, 32 bit signed int:
30636 getValue: function (dataView, dataOffset, littleEndian) {
30637 return dataView.getInt32(dataOffset, littleEndian);
30641 // srational, two slongs, first is numerator, second is denominator:
30643 getValue: function (dataView, dataOffset, littleEndian) {
30644 return dataView.getInt32(dataOffset, littleEndian) /
30645 dataView.getInt32(dataOffset + 4, littleEndian);
30655 cls : 'btn-group roo-upload-cropbox-rotate-left',
30656 action : 'rotate-left',
30660 cls : 'btn btn-default',
30661 html : '<i class="fa fa-undo"></i>'
30667 cls : 'btn-group roo-upload-cropbox-picture',
30668 action : 'picture',
30672 cls : 'btn btn-default',
30673 html : '<i class="fa fa-picture-o"></i>'
30679 cls : 'btn-group roo-upload-cropbox-rotate-right',
30680 action : 'rotate-right',
30684 cls : 'btn btn-default',
30685 html : '<i class="fa fa-repeat"></i>'
30693 cls : 'btn-group roo-upload-cropbox-rotate-left',
30694 action : 'rotate-left',
30698 cls : 'btn btn-default',
30699 html : '<i class="fa fa-undo"></i>'
30705 cls : 'btn-group roo-upload-cropbox-download',
30706 action : 'download',
30710 cls : 'btn btn-default',
30711 html : '<i class="fa fa-download"></i>'
30717 cls : 'btn-group roo-upload-cropbox-crop',
30722 cls : 'btn btn-default',
30723 html : '<i class="fa fa-crop"></i>'
30729 cls : 'btn-group roo-upload-cropbox-trash',
30734 cls : 'btn btn-default',
30735 html : '<i class="fa fa-trash"></i>'
30741 cls : 'btn-group roo-upload-cropbox-rotate-right',
30742 action : 'rotate-right',
30746 cls : 'btn btn-default',
30747 html : '<i class="fa fa-repeat"></i>'
30755 cls : 'btn-group roo-upload-cropbox-rotate-left',
30756 action : 'rotate-left',
30760 cls : 'btn btn-default',
30761 html : '<i class="fa fa-undo"></i>'
30767 cls : 'btn-group roo-upload-cropbox-rotate-right',
30768 action : 'rotate-right',
30772 cls : 'btn btn-default',
30773 html : '<i class="fa fa-repeat"></i>'
30786 * @class Roo.bootstrap.DocumentManager
30787 * @extends Roo.bootstrap.Component
30788 * Bootstrap DocumentManager class
30789 * @cfg {String} paramName default 'imageUpload'
30790 * @cfg {String} toolTipName default 'filename'
30791 * @cfg {String} method default POST
30792 * @cfg {String} url action url
30793 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30794 * @cfg {Boolean} multiple multiple upload default true
30795 * @cfg {Number} thumbSize default 300
30796 * @cfg {String} fieldLabel
30797 * @cfg {Number} labelWidth default 4
30798 * @cfg {String} labelAlign (left|top) default left
30799 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30800 * @cfg {Number} labellg set the width of label (1-12)
30801 * @cfg {Number} labelmd set the width of label (1-12)
30802 * @cfg {Number} labelsm set the width of label (1-12)
30803 * @cfg {Number} labelxs set the width of label (1-12)
30806 * Create a new DocumentManager
30807 * @param {Object} config The config object
30810 Roo.bootstrap.DocumentManager = function(config){
30811 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30814 this.delegates = [];
30819 * Fire when initial the DocumentManager
30820 * @param {Roo.bootstrap.DocumentManager} this
30825 * inspect selected file
30826 * @param {Roo.bootstrap.DocumentManager} this
30827 * @param {File} file
30832 * Fire when xhr load exception
30833 * @param {Roo.bootstrap.DocumentManager} this
30834 * @param {XMLHttpRequest} xhr
30836 "exception" : true,
30838 * @event afterupload
30839 * Fire when xhr load exception
30840 * @param {Roo.bootstrap.DocumentManager} this
30841 * @param {XMLHttpRequest} xhr
30843 "afterupload" : true,
30846 * prepare the form data
30847 * @param {Roo.bootstrap.DocumentManager} this
30848 * @param {Object} formData
30853 * Fire when remove the file
30854 * @param {Roo.bootstrap.DocumentManager} this
30855 * @param {Object} file
30860 * Fire after refresh the file
30861 * @param {Roo.bootstrap.DocumentManager} this
30866 * Fire after click the image
30867 * @param {Roo.bootstrap.DocumentManager} this
30868 * @param {Object} file
30873 * Fire when upload a image and editable set to true
30874 * @param {Roo.bootstrap.DocumentManager} this
30875 * @param {Object} file
30879 * @event beforeselectfile
30880 * Fire before select file
30881 * @param {Roo.bootstrap.DocumentManager} this
30883 "beforeselectfile" : true,
30886 * Fire before process file
30887 * @param {Roo.bootstrap.DocumentManager} this
30888 * @param {Object} file
30892 * @event previewrendered
30893 * Fire when preview rendered
30894 * @param {Roo.bootstrap.DocumentManager} this
30895 * @param {Object} file
30897 "previewrendered" : true,
30900 "previewResize" : true
30905 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
30914 paramName : 'imageUpload',
30915 toolTipName : 'filename',
30918 labelAlign : 'left',
30928 getAutoCreate : function()
30930 var managerWidget = {
30932 cls : 'roo-document-manager',
30936 cls : 'roo-document-manager-selector',
30941 cls : 'roo-document-manager-uploader',
30945 cls : 'roo-document-manager-upload-btn',
30946 html : '<i class="fa fa-plus"></i>'
30957 cls : 'column col-md-12',
30962 if(this.fieldLabel.length){
30967 cls : 'column col-md-12',
30968 html : this.fieldLabel
30972 cls : 'column col-md-12',
30977 if(this.labelAlign == 'left'){
30982 html : this.fieldLabel
30991 if(this.labelWidth > 12){
30992 content[0].style = "width: " + this.labelWidth + 'px';
30995 if(this.labelWidth < 13 && this.labelmd == 0){
30996 this.labelmd = this.labelWidth;
30999 if(this.labellg > 0){
31000 content[0].cls += ' col-lg-' + this.labellg;
31001 content[1].cls += ' col-lg-' + (12 - this.labellg);
31004 if(this.labelmd > 0){
31005 content[0].cls += ' col-md-' + this.labelmd;
31006 content[1].cls += ' col-md-' + (12 - this.labelmd);
31009 if(this.labelsm > 0){
31010 content[0].cls += ' col-sm-' + this.labelsm;
31011 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31014 if(this.labelxs > 0){
31015 content[0].cls += ' col-xs-' + this.labelxs;
31016 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31024 cls : 'row clearfix',
31032 initEvents : function()
31034 this.managerEl = this.el.select('.roo-document-manager', true).first();
31035 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31037 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31038 this.selectorEl.hide();
31041 this.selectorEl.attr('multiple', 'multiple');
31044 this.selectorEl.on('change', this.onFileSelected, this);
31046 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31047 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31049 this.uploader.on('click', this.onUploaderClick, this);
31051 this.renderProgressDialog();
31055 window.addEventListener("resize", function() { _this.refresh(); } );
31057 this.fireEvent('initial', this);
31060 renderProgressDialog : function()
31064 this.progressDialog = new Roo.bootstrap.Modal({
31065 cls : 'roo-document-manager-progress-dialog',
31066 allow_close : false,
31077 btnclick : function() {
31078 _this.uploadCancel();
31084 this.progressDialog.render(Roo.get(document.body));
31086 this.progress = new Roo.bootstrap.Progress({
31087 cls : 'roo-document-manager-progress',
31092 this.progress.render(this.progressDialog.getChildContainer());
31094 this.progressBar = new Roo.bootstrap.ProgressBar({
31095 cls : 'roo-document-manager-progress-bar',
31098 aria_valuemax : 12,
31102 this.progressBar.render(this.progress.getChildContainer());
31105 onUploaderClick : function(e)
31107 e.preventDefault();
31109 if(this.fireEvent('beforeselectfile', this) != false){
31110 this.selectorEl.dom.click();
31115 onFileSelected : function(e)
31117 e.preventDefault();
31119 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31123 Roo.each(this.selectorEl.dom.files, function(file){
31124 if(this.fireEvent('inspect', this, file) != false){
31125 this.files.push(file);
31135 this.selectorEl.dom.value = '';
31137 if(!this.files || !this.files.length){
31141 if(this.boxes > 0 && this.files.length > this.boxes){
31142 this.files = this.files.slice(0, this.boxes);
31145 this.uploader.show();
31147 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31148 this.uploader.hide();
31157 Roo.each(this.files, function(file){
31159 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31160 var f = this.renderPreview(file);
31165 if(file.type.indexOf('image') != -1){
31166 this.delegates.push(
31168 _this.process(file);
31169 }).createDelegate(this)
31177 _this.process(file);
31178 }).createDelegate(this)
31183 this.files = files;
31185 this.delegates = this.delegates.concat(docs);
31187 if(!this.delegates.length){
31192 this.progressBar.aria_valuemax = this.delegates.length;
31199 arrange : function()
31201 if(!this.delegates.length){
31202 this.progressDialog.hide();
31207 var delegate = this.delegates.shift();
31209 this.progressDialog.show();
31211 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31213 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31218 refresh : function()
31220 this.uploader.show();
31222 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31223 this.uploader.hide();
31226 Roo.isTouch ? this.closable(false) : this.closable(true);
31228 this.fireEvent('refresh', this);
31231 onRemove : function(e, el, o)
31233 e.preventDefault();
31235 this.fireEvent('remove', this, o);
31239 remove : function(o)
31243 Roo.each(this.files, function(file){
31244 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31253 this.files = files;
31260 Roo.each(this.files, function(file){
31265 file.target.remove();
31274 onClick : function(e, el, o)
31276 e.preventDefault();
31278 this.fireEvent('click', this, o);
31282 closable : function(closable)
31284 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31286 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31298 xhrOnLoad : function(xhr)
31300 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31304 if (xhr.readyState !== 4) {
31306 this.fireEvent('exception', this, xhr);
31310 var response = Roo.decode(xhr.responseText);
31312 if(!response.success){
31314 this.fireEvent('exception', this, xhr);
31318 var file = this.renderPreview(response.data);
31320 this.files.push(file);
31324 this.fireEvent('afterupload', this, xhr);
31328 xhrOnError : function(xhr)
31330 Roo.log('xhr on error');
31332 var response = Roo.decode(xhr.responseText);
31339 process : function(file)
31341 if(this.fireEvent('process', this, file) !== false){
31342 if(this.editable && file.type.indexOf('image') != -1){
31343 this.fireEvent('edit', this, file);
31347 this.uploadStart(file, false);
31354 uploadStart : function(file, crop)
31356 this.xhr = new XMLHttpRequest();
31358 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31363 file.xhr = this.xhr;
31365 this.managerEl.createChild({
31367 cls : 'roo-document-manager-loading',
31371 tooltip : file.name,
31372 cls : 'roo-document-manager-thumb',
31373 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31379 this.xhr.open(this.method, this.url, true);
31382 "Accept": "application/json",
31383 "Cache-Control": "no-cache",
31384 "X-Requested-With": "XMLHttpRequest"
31387 for (var headerName in headers) {
31388 var headerValue = headers[headerName];
31390 this.xhr.setRequestHeader(headerName, headerValue);
31396 this.xhr.onload = function()
31398 _this.xhrOnLoad(_this.xhr);
31401 this.xhr.onerror = function()
31403 _this.xhrOnError(_this.xhr);
31406 var formData = new FormData();
31408 formData.append('returnHTML', 'NO');
31411 formData.append('crop', crop);
31414 formData.append(this.paramName, file, file.name);
31421 if(this.fireEvent('prepare', this, formData, options) != false){
31423 if(options.manually){
31427 this.xhr.send(formData);
31431 this.uploadCancel();
31434 uploadCancel : function()
31440 this.delegates = [];
31442 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31449 renderPreview : function(file)
31451 if(typeof(file.target) != 'undefined' && file.target){
31455 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31457 var previewEl = this.managerEl.createChild({
31459 cls : 'roo-document-manager-preview',
31463 tooltip : file[this.toolTipName],
31464 cls : 'roo-document-manager-thumb',
31465 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31470 html : '<i class="fa fa-times-circle"></i>'
31475 var close = previewEl.select('button.close', true).first();
31477 close.on('click', this.onRemove, this, file);
31479 file.target = previewEl;
31481 var image = previewEl.select('img', true).first();
31485 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31487 image.on('click', this.onClick, this, file);
31489 this.fireEvent('previewrendered', this, file);
31495 onPreviewLoad : function(file, image)
31497 if(typeof(file.target) == 'undefined' || !file.target){
31501 var width = image.dom.naturalWidth || image.dom.width;
31502 var height = image.dom.naturalHeight || image.dom.height;
31504 if(!this.previewResize) {
31508 if(width > height){
31509 file.target.addClass('wide');
31513 file.target.addClass('tall');
31518 uploadFromSource : function(file, crop)
31520 this.xhr = new XMLHttpRequest();
31522 this.managerEl.createChild({
31524 cls : 'roo-document-manager-loading',
31528 tooltip : file.name,
31529 cls : 'roo-document-manager-thumb',
31530 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31536 this.xhr.open(this.method, this.url, true);
31539 "Accept": "application/json",
31540 "Cache-Control": "no-cache",
31541 "X-Requested-With": "XMLHttpRequest"
31544 for (var headerName in headers) {
31545 var headerValue = headers[headerName];
31547 this.xhr.setRequestHeader(headerName, headerValue);
31553 this.xhr.onload = function()
31555 _this.xhrOnLoad(_this.xhr);
31558 this.xhr.onerror = function()
31560 _this.xhrOnError(_this.xhr);
31563 var formData = new FormData();
31565 formData.append('returnHTML', 'NO');
31567 formData.append('crop', crop);
31569 if(typeof(file.filename) != 'undefined'){
31570 formData.append('filename', file.filename);
31573 if(typeof(file.mimetype) != 'undefined'){
31574 formData.append('mimetype', file.mimetype);
31579 if(this.fireEvent('prepare', this, formData) != false){
31580 this.xhr.send(formData);
31590 * @class Roo.bootstrap.DocumentViewer
31591 * @extends Roo.bootstrap.Component
31592 * Bootstrap DocumentViewer class
31593 * @cfg {Boolean} showDownload (true|false) show download button (default true)
31594 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31597 * Create a new DocumentViewer
31598 * @param {Object} config The config object
31601 Roo.bootstrap.DocumentViewer = function(config){
31602 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31607 * Fire after initEvent
31608 * @param {Roo.bootstrap.DocumentViewer} this
31614 * @param {Roo.bootstrap.DocumentViewer} this
31619 * Fire after download button
31620 * @param {Roo.bootstrap.DocumentViewer} this
31625 * Fire after trash button
31626 * @param {Roo.bootstrap.DocumentViewer} this
31633 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
31635 showDownload : true,
31639 getAutoCreate : function()
31643 cls : 'roo-document-viewer',
31647 cls : 'roo-document-viewer-body',
31651 cls : 'roo-document-viewer-thumb',
31655 cls : 'roo-document-viewer-image'
31663 cls : 'roo-document-viewer-footer',
31666 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31670 cls : 'btn-group roo-document-viewer-download',
31674 cls : 'btn btn-default',
31675 html : '<i class="fa fa-download"></i>'
31681 cls : 'btn-group roo-document-viewer-trash',
31685 cls : 'btn btn-default',
31686 html : '<i class="fa fa-trash"></i>'
31699 initEvents : function()
31701 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31702 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31704 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31705 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31707 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31708 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31710 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31711 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31713 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31714 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31716 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31717 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31719 this.bodyEl.on('click', this.onClick, this);
31720 this.downloadBtn.on('click', this.onDownload, this);
31721 this.trashBtn.on('click', this.onTrash, this);
31723 this.downloadBtn.hide();
31724 this.trashBtn.hide();
31726 if(this.showDownload){
31727 this.downloadBtn.show();
31730 if(this.showTrash){
31731 this.trashBtn.show();
31734 if(!this.showDownload && !this.showTrash) {
31735 this.footerEl.hide();
31740 initial : function()
31742 this.fireEvent('initial', this);
31746 onClick : function(e)
31748 e.preventDefault();
31750 this.fireEvent('click', this);
31753 onDownload : function(e)
31755 e.preventDefault();
31757 this.fireEvent('download', this);
31760 onTrash : function(e)
31762 e.preventDefault();
31764 this.fireEvent('trash', this);
31776 * @class Roo.bootstrap.NavProgressBar
31777 * @extends Roo.bootstrap.Component
31778 * Bootstrap NavProgressBar class
31781 * Create a new nav progress bar
31782 * @param {Object} config The config object
31785 Roo.bootstrap.NavProgressBar = function(config){
31786 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31788 this.bullets = this.bullets || [];
31790 // Roo.bootstrap.NavProgressBar.register(this);
31794 * Fires when the active item changes
31795 * @param {Roo.bootstrap.NavProgressBar} this
31796 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31797 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
31804 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
31809 getAutoCreate : function()
31811 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31815 cls : 'roo-navigation-bar-group',
31819 cls : 'roo-navigation-top-bar'
31823 cls : 'roo-navigation-bullets-bar',
31827 cls : 'roo-navigation-bar'
31834 cls : 'roo-navigation-bottom-bar'
31844 initEvents: function()
31849 onRender : function(ct, position)
31851 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31853 if(this.bullets.length){
31854 Roo.each(this.bullets, function(b){
31863 addItem : function(cfg)
31865 var item = new Roo.bootstrap.NavProgressItem(cfg);
31867 item.parentId = this.id;
31868 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31871 var top = new Roo.bootstrap.Element({
31873 cls : 'roo-navigation-bar-text'
31876 var bottom = new Roo.bootstrap.Element({
31878 cls : 'roo-navigation-bar-text'
31881 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31882 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31884 var topText = new Roo.bootstrap.Element({
31886 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31889 var bottomText = new Roo.bootstrap.Element({
31891 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31894 topText.onRender(top.el, null);
31895 bottomText.onRender(bottom.el, null);
31898 item.bottomEl = bottom;
31901 this.barItems.push(item);
31906 getActive : function()
31908 var active = false;
31910 Roo.each(this.barItems, function(v){
31912 if (!v.isActive()) {
31924 setActiveItem : function(item)
31928 Roo.each(this.barItems, function(v){
31929 if (v.rid == item.rid) {
31933 if (v.isActive()) {
31934 v.setActive(false);
31939 item.setActive(true);
31941 this.fireEvent('changed', this, item, prev);
31944 getBarItem: function(rid)
31948 Roo.each(this.barItems, function(e) {
31949 if (e.rid != rid) {
31960 indexOfItem : function(item)
31964 Roo.each(this.barItems, function(v, i){
31966 if (v.rid != item.rid) {
31977 setActiveNext : function()
31979 var i = this.indexOfItem(this.getActive());
31981 if (i > this.barItems.length) {
31985 this.setActiveItem(this.barItems[i+1]);
31988 setActivePrev : function()
31990 var i = this.indexOfItem(this.getActive());
31996 this.setActiveItem(this.barItems[i-1]);
31999 format : function()
32001 if(!this.barItems.length){
32005 var width = 100 / this.barItems.length;
32007 Roo.each(this.barItems, function(i){
32008 i.el.setStyle('width', width + '%');
32009 i.topEl.el.setStyle('width', width + '%');
32010 i.bottomEl.el.setStyle('width', width + '%');
32019 * Nav Progress Item
32024 * @class Roo.bootstrap.NavProgressItem
32025 * @extends Roo.bootstrap.Component
32026 * Bootstrap NavProgressItem class
32027 * @cfg {String} rid the reference id
32028 * @cfg {Boolean} active (true|false) Is item active default false
32029 * @cfg {Boolean} disabled (true|false) Is item active default false
32030 * @cfg {String} html
32031 * @cfg {String} position (top|bottom) text position default bottom
32032 * @cfg {String} icon show icon instead of number
32035 * Create a new NavProgressItem
32036 * @param {Object} config The config object
32038 Roo.bootstrap.NavProgressItem = function(config){
32039 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32044 * The raw click event for the entire grid.
32045 * @param {Roo.bootstrap.NavProgressItem} this
32046 * @param {Roo.EventObject} e
32053 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32059 position : 'bottom',
32062 getAutoCreate : function()
32064 var iconCls = 'roo-navigation-bar-item-icon';
32066 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32070 cls: 'roo-navigation-bar-item',
32080 cfg.cls += ' active';
32083 cfg.cls += ' disabled';
32089 disable : function()
32091 this.setDisabled(true);
32094 enable : function()
32096 this.setDisabled(false);
32099 initEvents: function()
32101 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32103 this.iconEl.on('click', this.onClick, this);
32106 onClick : function(e)
32108 e.preventDefault();
32114 if(this.fireEvent('click', this, e) === false){
32118 this.parent().setActiveItem(this);
32121 isActive: function ()
32123 return this.active;
32126 setActive : function(state)
32128 if(this.active == state){
32132 this.active = state;
32135 this.el.addClass('active');
32139 this.el.removeClass('active');
32144 setDisabled : function(state)
32146 if(this.disabled == state){
32150 this.disabled = state;
32153 this.el.addClass('disabled');
32157 this.el.removeClass('disabled');
32160 tooltipEl : function()
32162 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32175 * @class Roo.bootstrap.FieldLabel
32176 * @extends Roo.bootstrap.Component
32177 * Bootstrap FieldLabel class
32178 * @cfg {String} html contents of the element
32179 * @cfg {String} tag tag of the element default label
32180 * @cfg {String} cls class of the element
32181 * @cfg {String} target label target
32182 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32183 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32184 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32185 * @cfg {String} iconTooltip default "This field is required"
32186 * @cfg {String} indicatorpos (left|right) default left
32189 * Create a new FieldLabel
32190 * @param {Object} config The config object
32193 Roo.bootstrap.FieldLabel = function(config){
32194 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32199 * Fires after the field has been marked as invalid.
32200 * @param {Roo.form.FieldLabel} this
32201 * @param {String} msg The validation message
32206 * Fires after the field has been validated with no errors.
32207 * @param {Roo.form.FieldLabel} this
32213 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32220 invalidClass : 'has-warning',
32221 validClass : 'has-success',
32222 iconTooltip : 'This field is required',
32223 indicatorpos : 'left',
32225 getAutoCreate : function(){
32228 if (!this.allowBlank) {
32234 cls : 'roo-bootstrap-field-label ' + this.cls,
32239 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32240 tooltip : this.iconTooltip
32249 if(this.indicatorpos == 'right'){
32252 cls : 'roo-bootstrap-field-label ' + this.cls,
32261 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32262 tooltip : this.iconTooltip
32271 initEvents: function()
32273 Roo.bootstrap.Element.superclass.initEvents.call(this);
32275 this.indicator = this.indicatorEl();
32277 if(this.indicator){
32278 this.indicator.removeClass('visible');
32279 this.indicator.addClass('invisible');
32282 Roo.bootstrap.FieldLabel.register(this);
32285 indicatorEl : function()
32287 var indicator = this.el.select('i.roo-required-indicator',true).first();
32298 * Mark this field as valid
32300 markValid : function()
32302 if(this.indicator){
32303 this.indicator.removeClass('visible');
32304 this.indicator.addClass('invisible');
32306 if (Roo.bootstrap.version == 3) {
32307 this.el.removeClass(this.invalidClass);
32308 this.el.addClass(this.validClass);
32310 this.el.removeClass('is-invalid');
32311 this.el.addClass('is-valid');
32315 this.fireEvent('valid', this);
32319 * Mark this field as invalid
32320 * @param {String} msg The validation message
32322 markInvalid : function(msg)
32324 if(this.indicator){
32325 this.indicator.removeClass('invisible');
32326 this.indicator.addClass('visible');
32328 if (Roo.bootstrap.version == 3) {
32329 this.el.removeClass(this.validClass);
32330 this.el.addClass(this.invalidClass);
32332 this.el.removeClass('is-valid');
32333 this.el.addClass('is-invalid');
32337 this.fireEvent('invalid', this, msg);
32343 Roo.apply(Roo.bootstrap.FieldLabel, {
32348 * register a FieldLabel Group
32349 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32351 register : function(label)
32353 if(this.groups.hasOwnProperty(label.target)){
32357 this.groups[label.target] = label;
32361 * fetch a FieldLabel Group based on the target
32362 * @param {string} target
32363 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32365 get: function(target) {
32366 if (typeof(this.groups[target]) == 'undefined') {
32370 return this.groups[target] ;
32379 * page DateSplitField.
32385 * @class Roo.bootstrap.DateSplitField
32386 * @extends Roo.bootstrap.Component
32387 * Bootstrap DateSplitField class
32388 * @cfg {string} fieldLabel - the label associated
32389 * @cfg {Number} labelWidth set the width of label (0-12)
32390 * @cfg {String} labelAlign (top|left)
32391 * @cfg {Boolean} dayAllowBlank (true|false) default false
32392 * @cfg {Boolean} monthAllowBlank (true|false) default false
32393 * @cfg {Boolean} yearAllowBlank (true|false) default false
32394 * @cfg {string} dayPlaceholder
32395 * @cfg {string} monthPlaceholder
32396 * @cfg {string} yearPlaceholder
32397 * @cfg {string} dayFormat default 'd'
32398 * @cfg {string} monthFormat default 'm'
32399 * @cfg {string} yearFormat default 'Y'
32400 * @cfg {Number} labellg set the width of label (1-12)
32401 * @cfg {Number} labelmd set the width of label (1-12)
32402 * @cfg {Number} labelsm set the width of label (1-12)
32403 * @cfg {Number} labelxs set the width of label (1-12)
32407 * Create a new DateSplitField
32408 * @param {Object} config The config object
32411 Roo.bootstrap.DateSplitField = function(config){
32412 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32418 * getting the data of years
32419 * @param {Roo.bootstrap.DateSplitField} this
32420 * @param {Object} years
32425 * getting the data of days
32426 * @param {Roo.bootstrap.DateSplitField} this
32427 * @param {Object} days
32432 * Fires after the field has been marked as invalid.
32433 * @param {Roo.form.Field} this
32434 * @param {String} msg The validation message
32439 * Fires after the field has been validated with no errors.
32440 * @param {Roo.form.Field} this
32446 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32449 labelAlign : 'top',
32451 dayAllowBlank : false,
32452 monthAllowBlank : false,
32453 yearAllowBlank : false,
32454 dayPlaceholder : '',
32455 monthPlaceholder : '',
32456 yearPlaceholder : '',
32460 isFormField : true,
32466 getAutoCreate : function()
32470 cls : 'row roo-date-split-field-group',
32475 cls : 'form-hidden-field roo-date-split-field-group-value',
32481 var labelCls = 'col-md-12';
32482 var contentCls = 'col-md-4';
32484 if(this.fieldLabel){
32488 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32492 html : this.fieldLabel
32497 if(this.labelAlign == 'left'){
32499 if(this.labelWidth > 12){
32500 label.style = "width: " + this.labelWidth + 'px';
32503 if(this.labelWidth < 13 && this.labelmd == 0){
32504 this.labelmd = this.labelWidth;
32507 if(this.labellg > 0){
32508 labelCls = ' col-lg-' + this.labellg;
32509 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32512 if(this.labelmd > 0){
32513 labelCls = ' col-md-' + this.labelmd;
32514 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32517 if(this.labelsm > 0){
32518 labelCls = ' col-sm-' + this.labelsm;
32519 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32522 if(this.labelxs > 0){
32523 labelCls = ' col-xs-' + this.labelxs;
32524 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32528 label.cls += ' ' + labelCls;
32530 cfg.cn.push(label);
32533 Roo.each(['day', 'month', 'year'], function(t){
32536 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32543 inputEl: function ()
32545 return this.el.select('.roo-date-split-field-group-value', true).first();
32548 onRender : function(ct, position)
32552 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32554 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32556 this.dayField = new Roo.bootstrap.ComboBox({
32557 allowBlank : this.dayAllowBlank,
32558 alwaysQuery : true,
32559 displayField : 'value',
32562 forceSelection : true,
32564 placeholder : this.dayPlaceholder,
32565 selectOnFocus : true,
32566 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32567 triggerAction : 'all',
32569 valueField : 'value',
32570 store : new Roo.data.SimpleStore({
32571 data : (function() {
32573 _this.fireEvent('days', _this, days);
32576 fields : [ 'value' ]
32579 select : function (_self, record, index)
32581 _this.setValue(_this.getValue());
32586 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32588 this.monthField = new Roo.bootstrap.MonthField({
32589 after : '<i class=\"fa fa-calendar\"></i>',
32590 allowBlank : this.monthAllowBlank,
32591 placeholder : this.monthPlaceholder,
32594 render : function (_self)
32596 this.el.select('span.input-group-addon', true).first().on('click', function(e){
32597 e.preventDefault();
32601 select : function (_self, oldvalue, newvalue)
32603 _this.setValue(_this.getValue());
32608 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32610 this.yearField = new Roo.bootstrap.ComboBox({
32611 allowBlank : this.yearAllowBlank,
32612 alwaysQuery : true,
32613 displayField : 'value',
32616 forceSelection : true,
32618 placeholder : this.yearPlaceholder,
32619 selectOnFocus : true,
32620 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32621 triggerAction : 'all',
32623 valueField : 'value',
32624 store : new Roo.data.SimpleStore({
32625 data : (function() {
32627 _this.fireEvent('years', _this, years);
32630 fields : [ 'value' ]
32633 select : function (_self, record, index)
32635 _this.setValue(_this.getValue());
32640 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32643 setValue : function(v, format)
32645 this.inputEl.dom.value = v;
32647 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32649 var d = Date.parseDate(v, f);
32656 this.setDay(d.format(this.dayFormat));
32657 this.setMonth(d.format(this.monthFormat));
32658 this.setYear(d.format(this.yearFormat));
32665 setDay : function(v)
32667 this.dayField.setValue(v);
32668 this.inputEl.dom.value = this.getValue();
32673 setMonth : function(v)
32675 this.monthField.setValue(v, true);
32676 this.inputEl.dom.value = this.getValue();
32681 setYear : function(v)
32683 this.yearField.setValue(v);
32684 this.inputEl.dom.value = this.getValue();
32689 getDay : function()
32691 return this.dayField.getValue();
32694 getMonth : function()
32696 return this.monthField.getValue();
32699 getYear : function()
32701 return this.yearField.getValue();
32704 getValue : function()
32706 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32708 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32718 this.inputEl.dom.value = '';
32723 validate : function()
32725 var d = this.dayField.validate();
32726 var m = this.monthField.validate();
32727 var y = this.yearField.validate();
32732 (!this.dayAllowBlank && !d) ||
32733 (!this.monthAllowBlank && !m) ||
32734 (!this.yearAllowBlank && !y)
32739 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32748 this.markInvalid();
32753 markValid : function()
32756 var label = this.el.select('label', true).first();
32757 var icon = this.el.select('i.fa-star', true).first();
32763 this.fireEvent('valid', this);
32767 * Mark this field as invalid
32768 * @param {String} msg The validation message
32770 markInvalid : function(msg)
32773 var label = this.el.select('label', true).first();
32774 var icon = this.el.select('i.fa-star', true).first();
32776 if(label && !icon){
32777 this.el.select('.roo-date-split-field-label', true).createChild({
32779 cls : 'text-danger fa fa-lg fa-star',
32780 tooltip : 'This field is required',
32781 style : 'margin-right:5px;'
32785 this.fireEvent('invalid', this, msg);
32788 clearInvalid : function()
32790 var label = this.el.select('label', true).first();
32791 var icon = this.el.select('i.fa-star', true).first();
32797 this.fireEvent('valid', this);
32800 getName: function()
32810 * http://masonry.desandro.com
32812 * The idea is to render all the bricks based on vertical width...
32814 * The original code extends 'outlayer' - we might need to use that....
32820 * @class Roo.bootstrap.LayoutMasonry
32821 * @extends Roo.bootstrap.Component
32822 * Bootstrap Layout Masonry class
32825 * Create a new Element
32826 * @param {Object} config The config object
32829 Roo.bootstrap.LayoutMasonry = function(config){
32831 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32835 Roo.bootstrap.LayoutMasonry.register(this);
32841 * Fire after layout the items
32842 * @param {Roo.bootstrap.LayoutMasonry} this
32843 * @param {Roo.EventObject} e
32850 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
32853 * @cfg {Boolean} isLayoutInstant = no animation?
32855 isLayoutInstant : false, // needed?
32858 * @cfg {Number} boxWidth width of the columns
32863 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
32868 * @cfg {Number} padWidth padding below box..
32873 * @cfg {Number} gutter gutter width..
32878 * @cfg {Number} maxCols maximum number of columns
32884 * @cfg {Boolean} isAutoInitial defalut true
32886 isAutoInitial : true,
32891 * @cfg {Boolean} isHorizontal defalut false
32893 isHorizontal : false,
32895 currentSize : null,
32901 bricks: null, //CompositeElement
32905 _isLayoutInited : false,
32907 // isAlternative : false, // only use for vertical layout...
32910 * @cfg {Number} alternativePadWidth padding below box..
32912 alternativePadWidth : 50,
32914 selectedBrick : [],
32916 getAutoCreate : function(){
32918 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32922 cls: 'blog-masonary-wrapper ' + this.cls,
32924 cls : 'mas-boxes masonary'
32931 getChildContainer: function( )
32933 if (this.boxesEl) {
32934 return this.boxesEl;
32937 this.boxesEl = this.el.select('.mas-boxes').first();
32939 return this.boxesEl;
32943 initEvents : function()
32947 if(this.isAutoInitial){
32948 Roo.log('hook children rendered');
32949 this.on('childrenrendered', function() {
32950 Roo.log('children rendered');
32956 initial : function()
32958 this.selectedBrick = [];
32960 this.currentSize = this.el.getBox(true);
32962 Roo.EventManager.onWindowResize(this.resize, this);
32964 if(!this.isAutoInitial){
32972 //this.layout.defer(500,this);
32976 resize : function()
32978 var cs = this.el.getBox(true);
32981 this.currentSize.width == cs.width &&
32982 this.currentSize.x == cs.x &&
32983 this.currentSize.height == cs.height &&
32984 this.currentSize.y == cs.y
32986 Roo.log("no change in with or X or Y");
32990 this.currentSize = cs;
32996 layout : function()
32998 this._resetLayout();
33000 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33002 this.layoutItems( isInstant );
33004 this._isLayoutInited = true;
33006 this.fireEvent('layout', this);
33010 _resetLayout : function()
33012 if(this.isHorizontal){
33013 this.horizontalMeasureColumns();
33017 this.verticalMeasureColumns();
33021 verticalMeasureColumns : function()
33023 this.getContainerWidth();
33025 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33026 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33030 var boxWidth = this.boxWidth + this.padWidth;
33032 if(this.containerWidth < this.boxWidth){
33033 boxWidth = this.containerWidth
33036 var containerWidth = this.containerWidth;
33038 var cols = Math.floor(containerWidth / boxWidth);
33040 this.cols = Math.max( cols, 1 );
33042 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33044 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33046 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33048 this.colWidth = boxWidth + avail - this.padWidth;
33050 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33051 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33054 horizontalMeasureColumns : function()
33056 this.getContainerWidth();
33058 var boxWidth = this.boxWidth;
33060 if(this.containerWidth < boxWidth){
33061 boxWidth = this.containerWidth;
33064 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33066 this.el.setHeight(boxWidth);
33070 getContainerWidth : function()
33072 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33075 layoutItems : function( isInstant )
33077 Roo.log(this.bricks);
33079 var items = Roo.apply([], this.bricks);
33081 if(this.isHorizontal){
33082 this._horizontalLayoutItems( items , isInstant );
33086 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33087 // this._verticalAlternativeLayoutItems( items , isInstant );
33091 this._verticalLayoutItems( items , isInstant );
33095 _verticalLayoutItems : function ( items , isInstant)
33097 if ( !items || !items.length ) {
33102 ['xs', 'xs', 'xs', 'tall'],
33103 ['xs', 'xs', 'tall'],
33104 ['xs', 'xs', 'sm'],
33105 ['xs', 'xs', 'xs'],
33111 ['sm', 'xs', 'xs'],
33115 ['tall', 'xs', 'xs', 'xs'],
33116 ['tall', 'xs', 'xs'],
33128 Roo.each(items, function(item, k){
33130 switch (item.size) {
33131 // these layouts take up a full box,
33142 boxes.push([item]);
33165 var filterPattern = function(box, length)
33173 var pattern = box.slice(0, length);
33177 Roo.each(pattern, function(i){
33178 format.push(i.size);
33181 Roo.each(standard, function(s){
33183 if(String(s) != String(format)){
33192 if(!match && length == 1){
33197 filterPattern(box, length - 1);
33201 queue.push(pattern);
33203 box = box.slice(length, box.length);
33205 filterPattern(box, 4);
33211 Roo.each(boxes, function(box, k){
33217 if(box.length == 1){
33222 filterPattern(box, 4);
33226 this._processVerticalLayoutQueue( queue, isInstant );
33230 // _verticalAlternativeLayoutItems : function( items , isInstant )
33232 // if ( !items || !items.length ) {
33236 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33240 _horizontalLayoutItems : function ( items , isInstant)
33242 if ( !items || !items.length || items.length < 3) {
33248 var eItems = items.slice(0, 3);
33250 items = items.slice(3, items.length);
33253 ['xs', 'xs', 'xs', 'wide'],
33254 ['xs', 'xs', 'wide'],
33255 ['xs', 'xs', 'sm'],
33256 ['xs', 'xs', 'xs'],
33262 ['sm', 'xs', 'xs'],
33266 ['wide', 'xs', 'xs', 'xs'],
33267 ['wide', 'xs', 'xs'],
33280 Roo.each(items, function(item, k){
33282 switch (item.size) {
33293 boxes.push([item]);
33317 var filterPattern = function(box, length)
33325 var pattern = box.slice(0, length);
33329 Roo.each(pattern, function(i){
33330 format.push(i.size);
33333 Roo.each(standard, function(s){
33335 if(String(s) != String(format)){
33344 if(!match && length == 1){
33349 filterPattern(box, length - 1);
33353 queue.push(pattern);
33355 box = box.slice(length, box.length);
33357 filterPattern(box, 4);
33363 Roo.each(boxes, function(box, k){
33369 if(box.length == 1){
33374 filterPattern(box, 4);
33381 var pos = this.el.getBox(true);
33385 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33387 var hit_end = false;
33389 Roo.each(queue, function(box){
33393 Roo.each(box, function(b){
33395 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33405 Roo.each(box, function(b){
33407 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33410 mx = Math.max(mx, b.x);
33414 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33418 Roo.each(box, function(b){
33420 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33434 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33437 /** Sets position of item in DOM
33438 * @param {Element} item
33439 * @param {Number} x - horizontal position
33440 * @param {Number} y - vertical position
33441 * @param {Boolean} isInstant - disables transitions
33443 _processVerticalLayoutQueue : function( queue, isInstant )
33445 var pos = this.el.getBox(true);
33450 for (var i = 0; i < this.cols; i++){
33454 Roo.each(queue, function(box, k){
33456 var col = k % this.cols;
33458 Roo.each(box, function(b,kk){
33460 b.el.position('absolute');
33462 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33463 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33465 if(b.size == 'md-left' || b.size == 'md-right'){
33466 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33467 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33470 b.el.setWidth(width);
33471 b.el.setHeight(height);
33473 b.el.select('iframe',true).setSize(width,height);
33477 for (var i = 0; i < this.cols; i++){
33479 if(maxY[i] < maxY[col]){
33484 col = Math.min(col, i);
33488 x = pos.x + col * (this.colWidth + this.padWidth);
33492 var positions = [];
33494 switch (box.length){
33496 positions = this.getVerticalOneBoxColPositions(x, y, box);
33499 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33502 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33505 positions = this.getVerticalFourBoxColPositions(x, y, box);
33511 Roo.each(box, function(b,kk){
33513 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33515 var sz = b.el.getSize();
33517 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33525 for (var i = 0; i < this.cols; i++){
33526 mY = Math.max(mY, maxY[i]);
33529 this.el.setHeight(mY - pos.y);
33533 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33535 // var pos = this.el.getBox(true);
33538 // var maxX = pos.right;
33540 // var maxHeight = 0;
33542 // Roo.each(items, function(item, k){
33546 // item.el.position('absolute');
33548 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33550 // item.el.setWidth(width);
33552 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33554 // item.el.setHeight(height);
33557 // item.el.setXY([x, y], isInstant ? false : true);
33559 // item.el.setXY([maxX - width, y], isInstant ? false : true);
33562 // y = y + height + this.alternativePadWidth;
33564 // maxHeight = maxHeight + height + this.alternativePadWidth;
33568 // this.el.setHeight(maxHeight);
33572 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33574 var pos = this.el.getBox(true);
33579 var maxX = pos.right;
33581 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33583 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33585 Roo.each(queue, function(box, k){
33587 Roo.each(box, function(b, kk){
33589 b.el.position('absolute');
33591 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33592 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33594 if(b.size == 'md-left' || b.size == 'md-right'){
33595 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33596 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33599 b.el.setWidth(width);
33600 b.el.setHeight(height);
33608 var positions = [];
33610 switch (box.length){
33612 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33615 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33618 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33621 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33627 Roo.each(box, function(b,kk){
33629 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33631 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33639 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33641 Roo.each(eItems, function(b,k){
33643 b.size = (k == 0) ? 'sm' : 'xs';
33644 b.x = (k == 0) ? 2 : 1;
33645 b.y = (k == 0) ? 2 : 1;
33647 b.el.position('absolute');
33649 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33651 b.el.setWidth(width);
33653 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33655 b.el.setHeight(height);
33659 var positions = [];
33662 x : maxX - this.unitWidth * 2 - this.gutter,
33667 x : maxX - this.unitWidth,
33668 y : minY + (this.unitWidth + this.gutter) * 2
33672 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33676 Roo.each(eItems, function(b,k){
33678 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33684 getVerticalOneBoxColPositions : function(x, y, box)
33688 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33690 if(box[0].size == 'md-left'){
33694 if(box[0].size == 'md-right'){
33699 x : x + (this.unitWidth + this.gutter) * rand,
33706 getVerticalTwoBoxColPositions : function(x, y, box)
33710 if(box[0].size == 'xs'){
33714 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33718 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33732 x : x + (this.unitWidth + this.gutter) * 2,
33733 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33740 getVerticalThreeBoxColPositions : function(x, y, box)
33744 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33752 x : x + (this.unitWidth + this.gutter) * 1,
33757 x : x + (this.unitWidth + this.gutter) * 2,
33765 if(box[0].size == 'xs' && box[1].size == 'xs'){
33774 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33778 x : x + (this.unitWidth + this.gutter) * 1,
33792 x : x + (this.unitWidth + this.gutter) * 2,
33797 x : x + (this.unitWidth + this.gutter) * 2,
33798 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33805 getVerticalFourBoxColPositions : function(x, y, box)
33809 if(box[0].size == 'xs'){
33818 y : y + (this.unitHeight + this.gutter) * 1
33823 y : y + (this.unitHeight + this.gutter) * 2
33827 x : x + (this.unitWidth + this.gutter) * 1,
33841 x : x + (this.unitWidth + this.gutter) * 2,
33846 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33847 y : y + (this.unitHeight + this.gutter) * 1
33851 x : x + (this.unitWidth + this.gutter) * 2,
33852 y : y + (this.unitWidth + this.gutter) * 2
33859 getHorizontalOneBoxColPositions : function(maxX, minY, box)
33863 if(box[0].size == 'md-left'){
33865 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33872 if(box[0].size == 'md-right'){
33874 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33875 y : minY + (this.unitWidth + this.gutter) * 1
33881 var rand = Math.floor(Math.random() * (4 - box[0].y));
33884 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33885 y : minY + (this.unitWidth + this.gutter) * rand
33892 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33896 if(box[0].size == 'xs'){
33899 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33904 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33905 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33913 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33918 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33919 y : minY + (this.unitWidth + this.gutter) * 2
33926 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33930 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33933 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33938 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33939 y : minY + (this.unitWidth + this.gutter) * 1
33943 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33944 y : minY + (this.unitWidth + this.gutter) * 2
33951 if(box[0].size == 'xs' && box[1].size == 'xs'){
33954 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33959 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33964 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33965 y : minY + (this.unitWidth + this.gutter) * 1
33973 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33978 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33979 y : minY + (this.unitWidth + this.gutter) * 2
33983 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33984 y : minY + (this.unitWidth + this.gutter) * 2
33991 getHorizontalFourBoxColPositions : function(maxX, minY, box)
33995 if(box[0].size == 'xs'){
33998 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34003 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34008 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),
34013 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34014 y : minY + (this.unitWidth + this.gutter) * 1
34022 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34027 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34028 y : minY + (this.unitWidth + this.gutter) * 2
34032 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34033 y : minY + (this.unitWidth + this.gutter) * 2
34037 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),
34038 y : minY + (this.unitWidth + this.gutter) * 2
34046 * remove a Masonry Brick
34047 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34049 removeBrick : function(brick_id)
34055 for (var i = 0; i<this.bricks.length; i++) {
34056 if (this.bricks[i].id == brick_id) {
34057 this.bricks.splice(i,1);
34058 this.el.dom.removeChild(Roo.get(brick_id).dom);
34065 * adds a Masonry Brick
34066 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34068 addBrick : function(cfg)
34070 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34071 //this.register(cn);
34072 cn.parentId = this.id;
34073 cn.render(this.el);
34078 * register a Masonry Brick
34079 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34082 register : function(brick)
34084 this.bricks.push(brick);
34085 brick.masonryId = this.id;
34089 * clear all the Masonry Brick
34091 clearAll : function()
34094 //this.getChildContainer().dom.innerHTML = "";
34095 this.el.dom.innerHTML = '';
34098 getSelected : function()
34100 if (!this.selectedBrick) {
34104 return this.selectedBrick;
34108 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34112 * register a Masonry Layout
34113 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34116 register : function(layout)
34118 this.groups[layout.id] = layout;
34121 * fetch a Masonry Layout based on the masonry layout ID
34122 * @param {string} the masonry layout to add
34123 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34126 get: function(layout_id) {
34127 if (typeof(this.groups[layout_id]) == 'undefined') {
34130 return this.groups[layout_id] ;
34142 * http://masonry.desandro.com
34144 * The idea is to render all the bricks based on vertical width...
34146 * The original code extends 'outlayer' - we might need to use that....
34152 * @class Roo.bootstrap.LayoutMasonryAuto
34153 * @extends Roo.bootstrap.Component
34154 * Bootstrap Layout Masonry class
34157 * Create a new Element
34158 * @param {Object} config The config object
34161 Roo.bootstrap.LayoutMasonryAuto = function(config){
34162 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34165 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34168 * @cfg {Boolean} isFitWidth - resize the width..
34170 isFitWidth : false, // options..
34172 * @cfg {Boolean} isOriginLeft = left align?
34174 isOriginLeft : true,
34176 * @cfg {Boolean} isOriginTop = top align?
34178 isOriginTop : false,
34180 * @cfg {Boolean} isLayoutInstant = no animation?
34182 isLayoutInstant : false, // needed?
34184 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34186 isResizingContainer : true,
34188 * @cfg {Number} columnWidth width of the columns
34194 * @cfg {Number} maxCols maximum number of columns
34199 * @cfg {Number} padHeight padding below box..
34205 * @cfg {Boolean} isAutoInitial defalut true
34208 isAutoInitial : true,
34214 initialColumnWidth : 0,
34215 currentSize : null,
34217 colYs : null, // array.
34224 bricks: null, //CompositeElement
34225 cols : 0, // array?
34226 // element : null, // wrapped now this.el
34227 _isLayoutInited : null,
34230 getAutoCreate : function(){
34234 cls: 'blog-masonary-wrapper ' + this.cls,
34236 cls : 'mas-boxes masonary'
34243 getChildContainer: function( )
34245 if (this.boxesEl) {
34246 return this.boxesEl;
34249 this.boxesEl = this.el.select('.mas-boxes').first();
34251 return this.boxesEl;
34255 initEvents : function()
34259 if(this.isAutoInitial){
34260 Roo.log('hook children rendered');
34261 this.on('childrenrendered', function() {
34262 Roo.log('children rendered');
34269 initial : function()
34271 this.reloadItems();
34273 this.currentSize = this.el.getBox(true);
34275 /// was window resize... - let's see if this works..
34276 Roo.EventManager.onWindowResize(this.resize, this);
34278 if(!this.isAutoInitial){
34283 this.layout.defer(500,this);
34286 reloadItems: function()
34288 this.bricks = this.el.select('.masonry-brick', true);
34290 this.bricks.each(function(b) {
34291 //Roo.log(b.getSize());
34292 if (!b.attr('originalwidth')) {
34293 b.attr('originalwidth', b.getSize().width);
34298 Roo.log(this.bricks.elements.length);
34301 resize : function()
34304 var cs = this.el.getBox(true);
34306 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34307 Roo.log("no change in with or X");
34310 this.currentSize = cs;
34314 layout : function()
34317 this._resetLayout();
34318 //this._manageStamps();
34320 // don't animate first layout
34321 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34322 this.layoutItems( isInstant );
34324 // flag for initalized
34325 this._isLayoutInited = true;
34328 layoutItems : function( isInstant )
34330 //var items = this._getItemsForLayout( this.items );
34331 // original code supports filtering layout items.. we just ignore it..
34333 this._layoutItems( this.bricks , isInstant );
34335 this._postLayout();
34337 _layoutItems : function ( items , isInstant)
34339 //this.fireEvent( 'layout', this, items );
34342 if ( !items || !items.elements.length ) {
34343 // no items, emit event with empty array
34348 items.each(function(item) {
34349 Roo.log("layout item");
34351 // get x/y object from method
34352 var position = this._getItemLayoutPosition( item );
34354 position.item = item;
34355 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34356 queue.push( position );
34359 this._processLayoutQueue( queue );
34361 /** Sets position of item in DOM
34362 * @param {Element} item
34363 * @param {Number} x - horizontal position
34364 * @param {Number} y - vertical position
34365 * @param {Boolean} isInstant - disables transitions
34367 _processLayoutQueue : function( queue )
34369 for ( var i=0, len = queue.length; i < len; i++ ) {
34370 var obj = queue[i];
34371 obj.item.position('absolute');
34372 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34378 * Any logic you want to do after each layout,
34379 * i.e. size the container
34381 _postLayout : function()
34383 this.resizeContainer();
34386 resizeContainer : function()
34388 if ( !this.isResizingContainer ) {
34391 var size = this._getContainerSize();
34393 this.el.setSize(size.width,size.height);
34394 this.boxesEl.setSize(size.width,size.height);
34400 _resetLayout : function()
34402 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34403 this.colWidth = this.el.getWidth();
34404 //this.gutter = this.el.getWidth();
34406 this.measureColumns();
34412 this.colYs.push( 0 );
34418 measureColumns : function()
34420 this.getContainerWidth();
34421 // if columnWidth is 0, default to outerWidth of first item
34422 if ( !this.columnWidth ) {
34423 var firstItem = this.bricks.first();
34424 Roo.log(firstItem);
34425 this.columnWidth = this.containerWidth;
34426 if (firstItem && firstItem.attr('originalwidth') ) {
34427 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34429 // columnWidth fall back to item of first element
34430 Roo.log("set column width?");
34431 this.initialColumnWidth = this.columnWidth ;
34433 // if first elem has no width, default to size of container
34438 if (this.initialColumnWidth) {
34439 this.columnWidth = this.initialColumnWidth;
34444 // column width is fixed at the top - however if container width get's smaller we should
34447 // this bit calcs how man columns..
34449 var columnWidth = this.columnWidth += this.gutter;
34451 // calculate columns
34452 var containerWidth = this.containerWidth + this.gutter;
34454 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34455 // fix rounding errors, typically with gutters
34456 var excess = columnWidth - containerWidth % columnWidth;
34459 // if overshoot is less than a pixel, round up, otherwise floor it
34460 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34461 cols = Math[ mathMethod ]( cols );
34462 this.cols = Math.max( cols, 1 );
34463 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34465 // padding positioning..
34466 var totalColWidth = this.cols * this.columnWidth;
34467 var padavail = this.containerWidth - totalColWidth;
34468 // so for 2 columns - we need 3 'pads'
34470 var padNeeded = (1+this.cols) * this.padWidth;
34472 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34474 this.columnWidth += padExtra
34475 //this.padWidth = Math.floor(padavail / ( this.cols));
34477 // adjust colum width so that padding is fixed??
34479 // we have 3 columns ... total = width * 3
34480 // we have X left over... that should be used by
34482 //if (this.expandC) {
34490 getContainerWidth : function()
34492 /* // container is parent if fit width
34493 var container = this.isFitWidth ? this.element.parentNode : this.element;
34494 // check that this.size and size are there
34495 // IE8 triggers resize on body size change, so they might not be
34497 var size = getSize( container ); //FIXME
34498 this.containerWidth = size && size.innerWidth; //FIXME
34501 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34505 _getItemLayoutPosition : function( item ) // what is item?
34507 // we resize the item to our columnWidth..
34509 item.setWidth(this.columnWidth);
34510 item.autoBoxAdjust = false;
34512 var sz = item.getSize();
34514 // how many columns does this brick span
34515 var remainder = this.containerWidth % this.columnWidth;
34517 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34518 // round if off by 1 pixel, otherwise use ceil
34519 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34520 colSpan = Math.min( colSpan, this.cols );
34522 // normally this should be '1' as we dont' currently allow multi width columns..
34524 var colGroup = this._getColGroup( colSpan );
34525 // get the minimum Y value from the columns
34526 var minimumY = Math.min.apply( Math, colGroup );
34527 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34529 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
34531 // position the brick
34533 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34534 y: this.currentSize.y + minimumY + this.padHeight
34538 // apply setHeight to necessary columns
34539 var setHeight = minimumY + sz.height + this.padHeight;
34540 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34542 var setSpan = this.cols + 1 - colGroup.length;
34543 for ( var i = 0; i < setSpan; i++ ) {
34544 this.colYs[ shortColIndex + i ] = setHeight ;
34551 * @param {Number} colSpan - number of columns the element spans
34552 * @returns {Array} colGroup
34554 _getColGroup : function( colSpan )
34556 if ( colSpan < 2 ) {
34557 // if brick spans only one column, use all the column Ys
34562 // how many different places could this brick fit horizontally
34563 var groupCount = this.cols + 1 - colSpan;
34564 // for each group potential horizontal position
34565 for ( var i = 0; i < groupCount; i++ ) {
34566 // make an array of colY values for that one group
34567 var groupColYs = this.colYs.slice( i, i + colSpan );
34568 // and get the max value of the array
34569 colGroup[i] = Math.max.apply( Math, groupColYs );
34574 _manageStamp : function( stamp )
34576 var stampSize = stamp.getSize();
34577 var offset = stamp.getBox();
34578 // get the columns that this stamp affects
34579 var firstX = this.isOriginLeft ? offset.x : offset.right;
34580 var lastX = firstX + stampSize.width;
34581 var firstCol = Math.floor( firstX / this.columnWidth );
34582 firstCol = Math.max( 0, firstCol );
34584 var lastCol = Math.floor( lastX / this.columnWidth );
34585 // lastCol should not go over if multiple of columnWidth #425
34586 lastCol -= lastX % this.columnWidth ? 0 : 1;
34587 lastCol = Math.min( this.cols - 1, lastCol );
34589 // set colYs to bottom of the stamp
34590 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34593 for ( var i = firstCol; i <= lastCol; i++ ) {
34594 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34599 _getContainerSize : function()
34601 this.maxY = Math.max.apply( Math, this.colYs );
34606 if ( this.isFitWidth ) {
34607 size.width = this._getContainerFitWidth();
34613 _getContainerFitWidth : function()
34615 var unusedCols = 0;
34616 // count unused columns
34619 if ( this.colYs[i] !== 0 ) {
34624 // fit container to columns that have been used
34625 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34628 needsResizeLayout : function()
34630 var previousWidth = this.containerWidth;
34631 this.getContainerWidth();
34632 return previousWidth !== this.containerWidth;
34647 * @class Roo.bootstrap.MasonryBrick
34648 * @extends Roo.bootstrap.Component
34649 * Bootstrap MasonryBrick class
34652 * Create a new MasonryBrick
34653 * @param {Object} config The config object
34656 Roo.bootstrap.MasonryBrick = function(config){
34658 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34660 Roo.bootstrap.MasonryBrick.register(this);
34666 * When a MasonryBrick is clcik
34667 * @param {Roo.bootstrap.MasonryBrick} this
34668 * @param {Roo.EventObject} e
34674 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34677 * @cfg {String} title
34681 * @cfg {String} html
34685 * @cfg {String} bgimage
34689 * @cfg {String} videourl
34693 * @cfg {String} cls
34697 * @cfg {String} href
34701 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34706 * @cfg {String} placetitle (center|bottom)
34711 * @cfg {Boolean} isFitContainer defalut true
34713 isFitContainer : true,
34716 * @cfg {Boolean} preventDefault defalut false
34718 preventDefault : false,
34721 * @cfg {Boolean} inverse defalut false
34723 maskInverse : false,
34725 getAutoCreate : function()
34727 if(!this.isFitContainer){
34728 return this.getSplitAutoCreate();
34731 var cls = 'masonry-brick masonry-brick-full';
34733 if(this.href.length){
34734 cls += ' masonry-brick-link';
34737 if(this.bgimage.length){
34738 cls += ' masonry-brick-image';
34741 if(this.maskInverse){
34742 cls += ' mask-inverse';
34745 if(!this.html.length && !this.maskInverse && !this.videourl.length){
34746 cls += ' enable-mask';
34750 cls += ' masonry-' + this.size + '-brick';
34753 if(this.placetitle.length){
34755 switch (this.placetitle) {
34757 cls += ' masonry-center-title';
34760 cls += ' masonry-bottom-title';
34767 if(!this.html.length && !this.bgimage.length){
34768 cls += ' masonry-center-title';
34771 if(!this.html.length && this.bgimage.length){
34772 cls += ' masonry-bottom-title';
34777 cls += ' ' + this.cls;
34781 tag: (this.href.length) ? 'a' : 'div',
34786 cls: 'masonry-brick-mask'
34790 cls: 'masonry-brick-paragraph',
34796 if(this.href.length){
34797 cfg.href = this.href;
34800 var cn = cfg.cn[1].cn;
34802 if(this.title.length){
34805 cls: 'masonry-brick-title',
34810 if(this.html.length){
34813 cls: 'masonry-brick-text',
34818 if (!this.title.length && !this.html.length) {
34819 cfg.cn[1].cls += ' hide';
34822 if(this.bgimage.length){
34825 cls: 'masonry-brick-image-view',
34830 if(this.videourl.length){
34831 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34832 // youtube support only?
34835 cls: 'masonry-brick-image-view',
34838 allowfullscreen : true
34846 getSplitAutoCreate : function()
34848 var cls = 'masonry-brick masonry-brick-split';
34850 if(this.href.length){
34851 cls += ' masonry-brick-link';
34854 if(this.bgimage.length){
34855 cls += ' masonry-brick-image';
34859 cls += ' masonry-' + this.size + '-brick';
34862 switch (this.placetitle) {
34864 cls += ' masonry-center-title';
34867 cls += ' masonry-bottom-title';
34870 if(!this.bgimage.length){
34871 cls += ' masonry-center-title';
34874 if(this.bgimage.length){
34875 cls += ' masonry-bottom-title';
34881 cls += ' ' + this.cls;
34885 tag: (this.href.length) ? 'a' : 'div',
34890 cls: 'masonry-brick-split-head',
34894 cls: 'masonry-brick-paragraph',
34901 cls: 'masonry-brick-split-body',
34907 if(this.href.length){
34908 cfg.href = this.href;
34911 if(this.title.length){
34912 cfg.cn[0].cn[0].cn.push({
34914 cls: 'masonry-brick-title',
34919 if(this.html.length){
34920 cfg.cn[1].cn.push({
34922 cls: 'masonry-brick-text',
34927 if(this.bgimage.length){
34928 cfg.cn[0].cn.push({
34930 cls: 'masonry-brick-image-view',
34935 if(this.videourl.length){
34936 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34937 // youtube support only?
34938 cfg.cn[0].cn.cn.push({
34940 cls: 'masonry-brick-image-view',
34943 allowfullscreen : true
34950 initEvents: function()
34952 switch (this.size) {
34985 this.el.on('touchstart', this.onTouchStart, this);
34986 this.el.on('touchmove', this.onTouchMove, this);
34987 this.el.on('touchend', this.onTouchEnd, this);
34988 this.el.on('contextmenu', this.onContextMenu, this);
34990 this.el.on('mouseenter' ,this.enter, this);
34991 this.el.on('mouseleave', this.leave, this);
34992 this.el.on('click', this.onClick, this);
34995 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34996 this.parent().bricks.push(this);
35001 onClick: function(e, el)
35003 var time = this.endTimer - this.startTimer;
35004 // Roo.log(e.preventDefault());
35007 e.preventDefault();
35012 if(!this.preventDefault){
35016 e.preventDefault();
35018 if (this.activeClass != '') {
35019 this.selectBrick();
35022 this.fireEvent('click', this, e);
35025 enter: function(e, el)
35027 e.preventDefault();
35029 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35033 if(this.bgimage.length && this.html.length){
35034 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35038 leave: function(e, el)
35040 e.preventDefault();
35042 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35046 if(this.bgimage.length && this.html.length){
35047 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35051 onTouchStart: function(e, el)
35053 // e.preventDefault();
35055 this.touchmoved = false;
35057 if(!this.isFitContainer){
35061 if(!this.bgimage.length || !this.html.length){
35065 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35067 this.timer = new Date().getTime();
35071 onTouchMove: function(e, el)
35073 this.touchmoved = true;
35076 onContextMenu : function(e,el)
35078 e.preventDefault();
35079 e.stopPropagation();
35083 onTouchEnd: function(e, el)
35085 // e.preventDefault();
35087 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35094 if(!this.bgimage.length || !this.html.length){
35096 if(this.href.length){
35097 window.location.href = this.href;
35103 if(!this.isFitContainer){
35107 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35109 window.location.href = this.href;
35112 //selection on single brick only
35113 selectBrick : function() {
35115 if (!this.parentId) {
35119 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35120 var index = m.selectedBrick.indexOf(this.id);
35123 m.selectedBrick.splice(index,1);
35124 this.el.removeClass(this.activeClass);
35128 for(var i = 0; i < m.selectedBrick.length; i++) {
35129 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35130 b.el.removeClass(b.activeClass);
35133 m.selectedBrick = [];
35135 m.selectedBrick.push(this.id);
35136 this.el.addClass(this.activeClass);
35140 isSelected : function(){
35141 return this.el.hasClass(this.activeClass);
35146 Roo.apply(Roo.bootstrap.MasonryBrick, {
35149 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35151 * register a Masonry Brick
35152 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35155 register : function(brick)
35157 //this.groups[brick.id] = brick;
35158 this.groups.add(brick.id, brick);
35161 * fetch a masonry brick based on the masonry brick ID
35162 * @param {string} the masonry brick to add
35163 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35166 get: function(brick_id)
35168 // if (typeof(this.groups[brick_id]) == 'undefined') {
35171 // return this.groups[brick_id] ;
35173 if(this.groups.key(brick_id)) {
35174 return this.groups.key(brick_id);
35192 * @class Roo.bootstrap.Brick
35193 * @extends Roo.bootstrap.Component
35194 * Bootstrap Brick class
35197 * Create a new Brick
35198 * @param {Object} config The config object
35201 Roo.bootstrap.Brick = function(config){
35202 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35208 * When a Brick is click
35209 * @param {Roo.bootstrap.Brick} this
35210 * @param {Roo.EventObject} e
35216 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35219 * @cfg {String} title
35223 * @cfg {String} html
35227 * @cfg {String} bgimage
35231 * @cfg {String} cls
35235 * @cfg {String} href
35239 * @cfg {String} video
35243 * @cfg {Boolean} square
35247 getAutoCreate : function()
35249 var cls = 'roo-brick';
35251 if(this.href.length){
35252 cls += ' roo-brick-link';
35255 if(this.bgimage.length){
35256 cls += ' roo-brick-image';
35259 if(!this.html.length && !this.bgimage.length){
35260 cls += ' roo-brick-center-title';
35263 if(!this.html.length && this.bgimage.length){
35264 cls += ' roo-brick-bottom-title';
35268 cls += ' ' + this.cls;
35272 tag: (this.href.length) ? 'a' : 'div',
35277 cls: 'roo-brick-paragraph',
35283 if(this.href.length){
35284 cfg.href = this.href;
35287 var cn = cfg.cn[0].cn;
35289 if(this.title.length){
35292 cls: 'roo-brick-title',
35297 if(this.html.length){
35300 cls: 'roo-brick-text',
35307 if(this.bgimage.length){
35310 cls: 'roo-brick-image-view',
35318 initEvents: function()
35320 if(this.title.length || this.html.length){
35321 this.el.on('mouseenter' ,this.enter, this);
35322 this.el.on('mouseleave', this.leave, this);
35325 Roo.EventManager.onWindowResize(this.resize, this);
35327 if(this.bgimage.length){
35328 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35329 this.imageEl.on('load', this.onImageLoad, this);
35336 onImageLoad : function()
35341 resize : function()
35343 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35345 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35347 if(this.bgimage.length){
35348 var image = this.el.select('.roo-brick-image-view', true).first();
35350 image.setWidth(paragraph.getWidth());
35353 image.setHeight(paragraph.getWidth());
35356 this.el.setHeight(image.getHeight());
35357 paragraph.setHeight(image.getHeight());
35363 enter: function(e, el)
35365 e.preventDefault();
35367 if(this.bgimage.length){
35368 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35369 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35373 leave: function(e, el)
35375 e.preventDefault();
35377 if(this.bgimage.length){
35378 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35379 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35394 * @class Roo.bootstrap.NumberField
35395 * @extends Roo.bootstrap.Input
35396 * Bootstrap NumberField class
35402 * Create a new NumberField
35403 * @param {Object} config The config object
35406 Roo.bootstrap.NumberField = function(config){
35407 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35410 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35413 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35415 allowDecimals : true,
35417 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35419 decimalSeparator : ".",
35421 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35423 decimalPrecision : 2,
35425 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35427 allowNegative : true,
35430 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35434 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35436 minValue : Number.NEGATIVE_INFINITY,
35438 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35440 maxValue : Number.MAX_VALUE,
35442 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35444 minText : "The minimum value for this field is {0}",
35446 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35448 maxText : "The maximum value for this field is {0}",
35450 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35451 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35453 nanText : "{0} is not a valid number",
35455 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35457 thousandsDelimiter : false,
35459 * @cfg {String} valueAlign alignment of value
35461 valueAlign : "left",
35463 getAutoCreate : function()
35465 var hiddenInput = {
35469 cls: 'hidden-number-input'
35473 hiddenInput.name = this.name;
35478 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35480 this.name = hiddenInput.name;
35482 if(cfg.cn.length > 0) {
35483 cfg.cn.push(hiddenInput);
35490 initEvents : function()
35492 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35494 var allowed = "0123456789";
35496 if(this.allowDecimals){
35497 allowed += this.decimalSeparator;
35500 if(this.allowNegative){
35504 if(this.thousandsDelimiter) {
35508 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35510 var keyPress = function(e){
35512 var k = e.getKey();
35514 var c = e.getCharCode();
35517 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35518 allowed.indexOf(String.fromCharCode(c)) === -1
35524 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35528 if(allowed.indexOf(String.fromCharCode(c)) === -1){
35533 this.el.on("keypress", keyPress, this);
35536 validateValue : function(value)
35539 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35543 var num = this.parseValue(value);
35546 this.markInvalid(String.format(this.nanText, value));
35550 if(num < this.minValue){
35551 this.markInvalid(String.format(this.minText, this.minValue));
35555 if(num > this.maxValue){
35556 this.markInvalid(String.format(this.maxText, this.maxValue));
35563 getValue : function()
35565 var v = this.hiddenEl().getValue();
35567 return this.fixPrecision(this.parseValue(v));
35570 parseValue : function(value)
35572 if(this.thousandsDelimiter) {
35574 r = new RegExp(",", "g");
35575 value = value.replace(r, "");
35578 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35579 return isNaN(value) ? '' : value;
35582 fixPrecision : function(value)
35584 if(this.thousandsDelimiter) {
35586 r = new RegExp(",", "g");
35587 value = value.replace(r, "");
35590 var nan = isNaN(value);
35592 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35593 return nan ? '' : value;
35595 return parseFloat(value).toFixed(this.decimalPrecision);
35598 setValue : function(v)
35600 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35606 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35608 this.inputEl().dom.value = (v == '') ? '' :
35609 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35611 if(!this.allowZero && v === '0') {
35612 this.hiddenEl().dom.value = '';
35613 this.inputEl().dom.value = '';
35620 decimalPrecisionFcn : function(v)
35622 return Math.floor(v);
35625 beforeBlur : function()
35627 var v = this.parseValue(this.getRawValue());
35629 if(v || v === 0 || v === ''){
35634 hiddenEl : function()
35636 return this.el.select('input.hidden-number-input',true).first();
35648 * @class Roo.bootstrap.DocumentSlider
35649 * @extends Roo.bootstrap.Component
35650 * Bootstrap DocumentSlider class
35653 * Create a new DocumentViewer
35654 * @param {Object} config The config object
35657 Roo.bootstrap.DocumentSlider = function(config){
35658 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35665 * Fire after initEvent
35666 * @param {Roo.bootstrap.DocumentSlider} this
35671 * Fire after update
35672 * @param {Roo.bootstrap.DocumentSlider} this
35678 * @param {Roo.bootstrap.DocumentSlider} this
35684 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35690 getAutoCreate : function()
35694 cls : 'roo-document-slider',
35698 cls : 'roo-document-slider-header',
35702 cls : 'roo-document-slider-header-title'
35708 cls : 'roo-document-slider-body',
35712 cls : 'roo-document-slider-prev',
35716 cls : 'fa fa-chevron-left'
35722 cls : 'roo-document-slider-thumb',
35726 cls : 'roo-document-slider-image'
35732 cls : 'roo-document-slider-next',
35736 cls : 'fa fa-chevron-right'
35748 initEvents : function()
35750 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35751 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35753 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35754 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35756 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35757 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35759 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35760 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35762 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35763 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35765 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35766 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35768 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35769 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35771 this.thumbEl.on('click', this.onClick, this);
35773 this.prevIndicator.on('click', this.prev, this);
35775 this.nextIndicator.on('click', this.next, this);
35779 initial : function()
35781 if(this.files.length){
35782 this.indicator = 1;
35786 this.fireEvent('initial', this);
35789 update : function()
35791 this.imageEl.attr('src', this.files[this.indicator - 1]);
35793 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35795 this.prevIndicator.show();
35797 if(this.indicator == 1){
35798 this.prevIndicator.hide();
35801 this.nextIndicator.show();
35803 if(this.indicator == this.files.length){
35804 this.nextIndicator.hide();
35807 this.thumbEl.scrollTo('top');
35809 this.fireEvent('update', this);
35812 onClick : function(e)
35814 e.preventDefault();
35816 this.fireEvent('click', this);
35821 e.preventDefault();
35823 this.indicator = Math.max(1, this.indicator - 1);
35830 e.preventDefault();
35832 this.indicator = Math.min(this.files.length, this.indicator + 1);
35846 * @class Roo.bootstrap.RadioSet
35847 * @extends Roo.bootstrap.Input
35848 * Bootstrap RadioSet class
35849 * @cfg {String} indicatorpos (left|right) default left
35850 * @cfg {Boolean} inline (true|false) inline the element (default true)
35851 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35853 * Create a new RadioSet
35854 * @param {Object} config The config object
35857 Roo.bootstrap.RadioSet = function(config){
35859 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35863 Roo.bootstrap.RadioSet.register(this);
35868 * Fires when the element is checked or unchecked.
35869 * @param {Roo.bootstrap.RadioSet} this This radio
35870 * @param {Roo.bootstrap.Radio} item The checked item
35875 * Fires when the element is click.
35876 * @param {Roo.bootstrap.RadioSet} this This radio set
35877 * @param {Roo.bootstrap.Radio} item The checked item
35878 * @param {Roo.EventObject} e The event object
35885 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
35893 indicatorpos : 'left',
35895 getAutoCreate : function()
35899 cls : 'roo-radio-set-label',
35903 html : this.fieldLabel
35907 if (Roo.bootstrap.version == 3) {
35910 if(this.indicatorpos == 'left'){
35913 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35914 tooltip : 'This field is required'
35919 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35920 tooltip : 'This field is required'
35926 cls : 'roo-radio-set-items'
35929 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35931 if (align === 'left' && this.fieldLabel.length) {
35934 cls : "roo-radio-set-right",
35940 if(this.labelWidth > 12){
35941 label.style = "width: " + this.labelWidth + 'px';
35944 if(this.labelWidth < 13 && this.labelmd == 0){
35945 this.labelmd = this.labelWidth;
35948 if(this.labellg > 0){
35949 label.cls += ' col-lg-' + this.labellg;
35950 items.cls += ' col-lg-' + (12 - this.labellg);
35953 if(this.labelmd > 0){
35954 label.cls += ' col-md-' + this.labelmd;
35955 items.cls += ' col-md-' + (12 - this.labelmd);
35958 if(this.labelsm > 0){
35959 label.cls += ' col-sm-' + this.labelsm;
35960 items.cls += ' col-sm-' + (12 - this.labelsm);
35963 if(this.labelxs > 0){
35964 label.cls += ' col-xs-' + this.labelxs;
35965 items.cls += ' col-xs-' + (12 - this.labelxs);
35971 cls : 'roo-radio-set',
35975 cls : 'roo-radio-set-input',
35978 value : this.value ? this.value : ''
35985 if(this.weight.length){
35986 cfg.cls += ' roo-radio-' + this.weight;
35990 cfg.cls += ' roo-radio-set-inline';
35994 ['xs','sm','md','lg'].map(function(size){
35995 if (settings[size]) {
35996 cfg.cls += ' col-' + size + '-' + settings[size];
36004 initEvents : function()
36006 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36007 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36009 if(!this.fieldLabel.length){
36010 this.labelEl.hide();
36013 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36014 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36016 this.indicator = this.indicatorEl();
36018 if(this.indicator){
36019 this.indicator.addClass('invisible');
36022 this.originalValue = this.getValue();
36026 inputEl: function ()
36028 return this.el.select('.roo-radio-set-input', true).first();
36031 getChildContainer : function()
36033 return this.itemsEl;
36036 register : function(item)
36038 this.radioes.push(item);
36042 validate : function()
36044 if(this.getVisibilityEl().hasClass('hidden')){
36050 Roo.each(this.radioes, function(i){
36059 if(this.allowBlank) {
36063 if(this.disabled || valid){
36068 this.markInvalid();
36073 markValid : function()
36075 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36076 this.indicatorEl().removeClass('visible');
36077 this.indicatorEl().addClass('invisible');
36081 if (Roo.bootstrap.version == 3) {
36082 this.el.removeClass([this.invalidClass, this.validClass]);
36083 this.el.addClass(this.validClass);
36085 this.el.removeClass(['is-invalid','is-valid']);
36086 this.el.addClass(['is-valid']);
36088 this.fireEvent('valid', this);
36091 markInvalid : function(msg)
36093 if(this.allowBlank || this.disabled){
36097 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36098 this.indicatorEl().removeClass('invisible');
36099 this.indicatorEl().addClass('visible');
36101 if (Roo.bootstrap.version == 3) {
36102 this.el.removeClass([this.invalidClass, this.validClass]);
36103 this.el.addClass(this.invalidClass);
36105 this.el.removeClass(['is-invalid','is-valid']);
36106 this.el.addClass(['is-invalid']);
36109 this.fireEvent('invalid', this, msg);
36113 setValue : function(v, suppressEvent)
36115 if(this.value === v){
36122 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36125 Roo.each(this.radioes, function(i){
36127 i.el.removeClass('checked');
36130 Roo.each(this.radioes, function(i){
36132 if(i.value === v || i.value.toString() === v.toString()){
36134 i.el.addClass('checked');
36136 if(suppressEvent !== true){
36137 this.fireEvent('check', this, i);
36148 clearInvalid : function(){
36150 if(!this.el || this.preventMark){
36154 this.el.removeClass([this.invalidClass]);
36156 this.fireEvent('valid', this);
36161 Roo.apply(Roo.bootstrap.RadioSet, {
36165 register : function(set)
36167 this.groups[set.name] = set;
36170 get: function(name)
36172 if (typeof(this.groups[name]) == 'undefined') {
36176 return this.groups[name] ;
36182 * Ext JS Library 1.1.1
36183 * Copyright(c) 2006-2007, Ext JS, LLC.
36185 * Originally Released Under LGPL - original licence link has changed is not relivant.
36188 * <script type="text/javascript">
36193 * @class Roo.bootstrap.SplitBar
36194 * @extends Roo.util.Observable
36195 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36199 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36200 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36201 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36202 split.minSize = 100;
36203 split.maxSize = 600;
36204 split.animate = true;
36205 split.on('moved', splitterMoved);
36208 * Create a new SplitBar
36209 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36210 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36211 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36212 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36213 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36214 position of the SplitBar).
36216 Roo.bootstrap.SplitBar = function(cfg){
36221 // dragElement : elm
36222 // resizingElement: el,
36224 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36225 // placement : Roo.bootstrap.SplitBar.LEFT ,
36226 // existingProxy ???
36229 this.el = Roo.get(cfg.dragElement, true);
36230 this.el.dom.unselectable = "on";
36232 this.resizingEl = Roo.get(cfg.resizingElement, true);
36236 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36237 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36240 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36243 * The minimum size of the resizing element. (Defaults to 0)
36249 * The maximum size of the resizing element. (Defaults to 2000)
36252 this.maxSize = 2000;
36255 * Whether to animate the transition to the new size
36258 this.animate = false;
36261 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36264 this.useShim = false;
36269 if(!cfg.existingProxy){
36271 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36273 this.proxy = Roo.get(cfg.existingProxy).dom;
36276 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36279 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36282 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36285 this.dragSpecs = {};
36288 * @private The adapter to use to positon and resize elements
36290 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36291 this.adapter.init(this);
36293 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36295 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36296 this.el.addClass("roo-splitbar-h");
36299 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36300 this.el.addClass("roo-splitbar-v");
36306 * Fires when the splitter is moved (alias for {@link #event-moved})
36307 * @param {Roo.bootstrap.SplitBar} this
36308 * @param {Number} newSize the new width or height
36313 * Fires when the splitter is moved
36314 * @param {Roo.bootstrap.SplitBar} this
36315 * @param {Number} newSize the new width or height
36319 * @event beforeresize
36320 * Fires before the splitter is dragged
36321 * @param {Roo.bootstrap.SplitBar} this
36323 "beforeresize" : true,
36325 "beforeapply" : true
36328 Roo.util.Observable.call(this);
36331 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36332 onStartProxyDrag : function(x, y){
36333 this.fireEvent("beforeresize", this);
36335 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36337 o.enableDisplayMode("block");
36338 // all splitbars share the same overlay
36339 Roo.bootstrap.SplitBar.prototype.overlay = o;
36341 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36342 this.overlay.show();
36343 Roo.get(this.proxy).setDisplayed("block");
36344 var size = this.adapter.getElementSize(this);
36345 this.activeMinSize = this.getMinimumSize();;
36346 this.activeMaxSize = this.getMaximumSize();;
36347 var c1 = size - this.activeMinSize;
36348 var c2 = Math.max(this.activeMaxSize - size, 0);
36349 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36350 this.dd.resetConstraints();
36351 this.dd.setXConstraint(
36352 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36353 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36355 this.dd.setYConstraint(0, 0);
36357 this.dd.resetConstraints();
36358 this.dd.setXConstraint(0, 0);
36359 this.dd.setYConstraint(
36360 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36361 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36364 this.dragSpecs.startSize = size;
36365 this.dragSpecs.startPoint = [x, y];
36366 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36370 * @private Called after the drag operation by the DDProxy
36372 onEndProxyDrag : function(e){
36373 Roo.get(this.proxy).setDisplayed(false);
36374 var endPoint = Roo.lib.Event.getXY(e);
36376 this.overlay.hide();
36379 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36380 newSize = this.dragSpecs.startSize +
36381 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36382 endPoint[0] - this.dragSpecs.startPoint[0] :
36383 this.dragSpecs.startPoint[0] - endPoint[0]
36386 newSize = this.dragSpecs.startSize +
36387 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36388 endPoint[1] - this.dragSpecs.startPoint[1] :
36389 this.dragSpecs.startPoint[1] - endPoint[1]
36392 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36393 if(newSize != this.dragSpecs.startSize){
36394 if(this.fireEvent('beforeapply', this, newSize) !== false){
36395 this.adapter.setElementSize(this, newSize);
36396 this.fireEvent("moved", this, newSize);
36397 this.fireEvent("resize", this, newSize);
36403 * Get the adapter this SplitBar uses
36404 * @return The adapter object
36406 getAdapter : function(){
36407 return this.adapter;
36411 * Set the adapter this SplitBar uses
36412 * @param {Object} adapter A SplitBar adapter object
36414 setAdapter : function(adapter){
36415 this.adapter = adapter;
36416 this.adapter.init(this);
36420 * Gets the minimum size for the resizing element
36421 * @return {Number} The minimum size
36423 getMinimumSize : function(){
36424 return this.minSize;
36428 * Sets the minimum size for the resizing element
36429 * @param {Number} minSize The minimum size
36431 setMinimumSize : function(minSize){
36432 this.minSize = minSize;
36436 * Gets the maximum size for the resizing element
36437 * @return {Number} The maximum size
36439 getMaximumSize : function(){
36440 return this.maxSize;
36444 * Sets the maximum size for the resizing element
36445 * @param {Number} maxSize The maximum size
36447 setMaximumSize : function(maxSize){
36448 this.maxSize = maxSize;
36452 * Sets the initialize size for the resizing element
36453 * @param {Number} size The initial size
36455 setCurrentSize : function(size){
36456 var oldAnimate = this.animate;
36457 this.animate = false;
36458 this.adapter.setElementSize(this, size);
36459 this.animate = oldAnimate;
36463 * Destroy this splitbar.
36464 * @param {Boolean} removeEl True to remove the element
36466 destroy : function(removeEl){
36468 this.shim.remove();
36471 this.proxy.parentNode.removeChild(this.proxy);
36479 * @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.
36481 Roo.bootstrap.SplitBar.createProxy = function(dir){
36482 var proxy = new Roo.Element(document.createElement("div"));
36483 proxy.unselectable();
36484 var cls = 'roo-splitbar-proxy';
36485 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36486 document.body.appendChild(proxy.dom);
36491 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36492 * Default Adapter. It assumes the splitter and resizing element are not positioned
36493 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36495 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36498 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36499 // do nothing for now
36500 init : function(s){
36504 * Called before drag operations to get the current size of the resizing element.
36505 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36507 getElementSize : function(s){
36508 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36509 return s.resizingEl.getWidth();
36511 return s.resizingEl.getHeight();
36516 * Called after drag operations to set the size of the resizing element.
36517 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36518 * @param {Number} newSize The new size to set
36519 * @param {Function} onComplete A function to be invoked when resizing is complete
36521 setElementSize : function(s, newSize, onComplete){
36522 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36524 s.resizingEl.setWidth(newSize);
36526 onComplete(s, newSize);
36529 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36534 s.resizingEl.setHeight(newSize);
36536 onComplete(s, newSize);
36539 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36546 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36547 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36548 * Adapter that moves the splitter element to align with the resized sizing element.
36549 * Used with an absolute positioned SplitBar.
36550 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36551 * document.body, make sure you assign an id to the body element.
36553 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36554 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36555 this.container = Roo.get(container);
36558 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36559 init : function(s){
36560 this.basic.init(s);
36563 getElementSize : function(s){
36564 return this.basic.getElementSize(s);
36567 setElementSize : function(s, newSize, onComplete){
36568 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36571 moveSplitter : function(s){
36572 var yes = Roo.bootstrap.SplitBar;
36573 switch(s.placement){
36575 s.el.setX(s.resizingEl.getRight());
36578 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36581 s.el.setY(s.resizingEl.getBottom());
36584 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36591 * Orientation constant - Create a vertical SplitBar
36595 Roo.bootstrap.SplitBar.VERTICAL = 1;
36598 * Orientation constant - Create a horizontal SplitBar
36602 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36605 * Placement constant - The resizing element is to the left of the splitter element
36609 Roo.bootstrap.SplitBar.LEFT = 1;
36612 * Placement constant - The resizing element is to the right of the splitter element
36616 Roo.bootstrap.SplitBar.RIGHT = 2;
36619 * Placement constant - The resizing element is positioned above the splitter element
36623 Roo.bootstrap.SplitBar.TOP = 3;
36626 * Placement constant - The resizing element is positioned under splitter element
36630 Roo.bootstrap.SplitBar.BOTTOM = 4;
36631 Roo.namespace("Roo.bootstrap.layout");/*
36633 * Ext JS Library 1.1.1
36634 * Copyright(c) 2006-2007, Ext JS, LLC.
36636 * Originally Released Under LGPL - original licence link has changed is not relivant.
36639 * <script type="text/javascript">
36643 * @class Roo.bootstrap.layout.Manager
36644 * @extends Roo.bootstrap.Component
36645 * Base class for layout managers.
36647 Roo.bootstrap.layout.Manager = function(config)
36649 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36655 /** false to disable window resize monitoring @type Boolean */
36656 this.monitorWindowResize = true;
36661 * Fires when a layout is performed.
36662 * @param {Roo.LayoutManager} this
36666 * @event regionresized
36667 * Fires when the user resizes a region.
36668 * @param {Roo.LayoutRegion} region The resized region
36669 * @param {Number} newSize The new size (width for east/west, height for north/south)
36671 "regionresized" : true,
36673 * @event regioncollapsed
36674 * Fires when a region is collapsed.
36675 * @param {Roo.LayoutRegion} region The collapsed region
36677 "regioncollapsed" : true,
36679 * @event regionexpanded
36680 * Fires when a region is expanded.
36681 * @param {Roo.LayoutRegion} region The expanded region
36683 "regionexpanded" : true
36685 this.updating = false;
36688 this.el = Roo.get(config.el);
36694 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36699 monitorWindowResize : true,
36705 onRender : function(ct, position)
36708 this.el = Roo.get(ct);
36711 //this.fireEvent('render',this);
36715 initEvents: function()
36719 // ie scrollbar fix
36720 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36721 document.body.scroll = "no";
36722 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36723 this.el.position('relative');
36725 this.id = this.el.id;
36726 this.el.addClass("roo-layout-container");
36727 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36728 if(this.el.dom != document.body ) {
36729 this.el.on('resize', this.layout,this);
36730 this.el.on('show', this.layout,this);
36736 * Returns true if this layout is currently being updated
36737 * @return {Boolean}
36739 isUpdating : function(){
36740 return this.updating;
36744 * Suspend the LayoutManager from doing auto-layouts while
36745 * making multiple add or remove calls
36747 beginUpdate : function(){
36748 this.updating = true;
36752 * Restore auto-layouts and optionally disable the manager from performing a layout
36753 * @param {Boolean} noLayout true to disable a layout update
36755 endUpdate : function(noLayout){
36756 this.updating = false;
36762 layout: function(){
36766 onRegionResized : function(region, newSize){
36767 this.fireEvent("regionresized", region, newSize);
36771 onRegionCollapsed : function(region){
36772 this.fireEvent("regioncollapsed", region);
36775 onRegionExpanded : function(region){
36776 this.fireEvent("regionexpanded", region);
36780 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36781 * performs box-model adjustments.
36782 * @return {Object} The size as an object {width: (the width), height: (the height)}
36784 getViewSize : function()
36787 if(this.el.dom != document.body){
36788 size = this.el.getSize();
36790 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36792 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36793 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36798 * Returns the Element this layout is bound to.
36799 * @return {Roo.Element}
36801 getEl : function(){
36806 * Returns the specified region.
36807 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36808 * @return {Roo.LayoutRegion}
36810 getRegion : function(target){
36811 return this.regions[target.toLowerCase()];
36814 onWindowResize : function(){
36815 if(this.monitorWindowResize){
36822 * Ext JS Library 1.1.1
36823 * Copyright(c) 2006-2007, Ext JS, LLC.
36825 * Originally Released Under LGPL - original licence link has changed is not relivant.
36828 * <script type="text/javascript">
36831 * @class Roo.bootstrap.layout.Border
36832 * @extends Roo.bootstrap.layout.Manager
36833 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36834 * please see: examples/bootstrap/nested.html<br><br>
36836 <b>The container the layout is rendered into can be either the body element or any other element.
36837 If it is not the body element, the container needs to either be an absolute positioned element,
36838 or you will need to add "position:relative" to the css of the container. You will also need to specify
36839 the container size if it is not the body element.</b>
36842 * Create a new Border
36843 * @param {Object} config Configuration options
36845 Roo.bootstrap.layout.Border = function(config){
36846 config = config || {};
36847 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36851 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36852 if(config[region]){
36853 config[region].region = region;
36854 this.addRegion(config[region]);
36860 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
36862 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36864 parent : false, // this might point to a 'nest' or a ???
36867 * Creates and adds a new region if it doesn't already exist.
36868 * @param {String} target The target region key (north, south, east, west or center).
36869 * @param {Object} config The regions config object
36870 * @return {BorderLayoutRegion} The new region
36872 addRegion : function(config)
36874 if(!this.regions[config.region]){
36875 var r = this.factory(config);
36876 this.bindRegion(r);
36878 return this.regions[config.region];
36882 bindRegion : function(r){
36883 this.regions[r.config.region] = r;
36885 r.on("visibilitychange", this.layout, this);
36886 r.on("paneladded", this.layout, this);
36887 r.on("panelremoved", this.layout, this);
36888 r.on("invalidated", this.layout, this);
36889 r.on("resized", this.onRegionResized, this);
36890 r.on("collapsed", this.onRegionCollapsed, this);
36891 r.on("expanded", this.onRegionExpanded, this);
36895 * Performs a layout update.
36897 layout : function()
36899 if(this.updating) {
36903 // render all the rebions if they have not been done alreayd?
36904 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36905 if(this.regions[region] && !this.regions[region].bodyEl){
36906 this.regions[region].onRender(this.el)
36910 var size = this.getViewSize();
36911 var w = size.width;
36912 var h = size.height;
36917 //var x = 0, y = 0;
36919 var rs = this.regions;
36920 var north = rs["north"];
36921 var south = rs["south"];
36922 var west = rs["west"];
36923 var east = rs["east"];
36924 var center = rs["center"];
36925 //if(this.hideOnLayout){ // not supported anymore
36926 //c.el.setStyle("display", "none");
36928 if(north && north.isVisible()){
36929 var b = north.getBox();
36930 var m = north.getMargins();
36931 b.width = w - (m.left+m.right);
36934 centerY = b.height + b.y + m.bottom;
36935 centerH -= centerY;
36936 north.updateBox(this.safeBox(b));
36938 if(south && south.isVisible()){
36939 var b = south.getBox();
36940 var m = south.getMargins();
36941 b.width = w - (m.left+m.right);
36943 var totalHeight = (b.height + m.top + m.bottom);
36944 b.y = h - totalHeight + m.top;
36945 centerH -= totalHeight;
36946 south.updateBox(this.safeBox(b));
36948 if(west && west.isVisible()){
36949 var b = west.getBox();
36950 var m = west.getMargins();
36951 b.height = centerH - (m.top+m.bottom);
36953 b.y = centerY + m.top;
36954 var totalWidth = (b.width + m.left + m.right);
36955 centerX += totalWidth;
36956 centerW -= totalWidth;
36957 west.updateBox(this.safeBox(b));
36959 if(east && east.isVisible()){
36960 var b = east.getBox();
36961 var m = east.getMargins();
36962 b.height = centerH - (m.top+m.bottom);
36963 var totalWidth = (b.width + m.left + m.right);
36964 b.x = w - totalWidth + m.left;
36965 b.y = centerY + m.top;
36966 centerW -= totalWidth;
36967 east.updateBox(this.safeBox(b));
36970 var m = center.getMargins();
36972 x: centerX + m.left,
36973 y: centerY + m.top,
36974 width: centerW - (m.left+m.right),
36975 height: centerH - (m.top+m.bottom)
36977 //if(this.hideOnLayout){
36978 //center.el.setStyle("display", "block");
36980 center.updateBox(this.safeBox(centerBox));
36983 this.fireEvent("layout", this);
36987 safeBox : function(box){
36988 box.width = Math.max(0, box.width);
36989 box.height = Math.max(0, box.height);
36994 * Adds a ContentPanel (or subclass) to this layout.
36995 * @param {String} target The target region key (north, south, east, west or center).
36996 * @param {Roo.ContentPanel} panel The panel to add
36997 * @return {Roo.ContentPanel} The added panel
36999 add : function(target, panel){
37001 target = target.toLowerCase();
37002 return this.regions[target].add(panel);
37006 * Remove a ContentPanel (or subclass) to this layout.
37007 * @param {String} target The target region key (north, south, east, west or center).
37008 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37009 * @return {Roo.ContentPanel} The removed panel
37011 remove : function(target, panel){
37012 target = target.toLowerCase();
37013 return this.regions[target].remove(panel);
37017 * Searches all regions for a panel with the specified id
37018 * @param {String} panelId
37019 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37021 findPanel : function(panelId){
37022 var rs = this.regions;
37023 for(var target in rs){
37024 if(typeof rs[target] != "function"){
37025 var p = rs[target].getPanel(panelId);
37035 * Searches all regions for a panel with the specified id and activates (shows) it.
37036 * @param {String/ContentPanel} panelId The panels id or the panel itself
37037 * @return {Roo.ContentPanel} The shown panel or null
37039 showPanel : function(panelId) {
37040 var rs = this.regions;
37041 for(var target in rs){
37042 var r = rs[target];
37043 if(typeof r != "function"){
37044 if(r.hasPanel(panelId)){
37045 return r.showPanel(panelId);
37053 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37054 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37057 restoreState : function(provider){
37059 provider = Roo.state.Manager;
37061 var sm = new Roo.LayoutStateManager();
37062 sm.init(this, provider);
37068 * Adds a xtype elements to the layout.
37072 xtype : 'ContentPanel',
37079 xtype : 'NestedLayoutPanel',
37085 items : [ ... list of content panels or nested layout panels.. ]
37089 * @param {Object} cfg Xtype definition of item to add.
37091 addxtype : function(cfg)
37093 // basically accepts a pannel...
37094 // can accept a layout region..!?!?
37095 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37098 // theory? children can only be panels??
37100 //if (!cfg.xtype.match(/Panel$/)) {
37105 if (typeof(cfg.region) == 'undefined') {
37106 Roo.log("Failed to add Panel, region was not set");
37110 var region = cfg.region;
37116 xitems = cfg.items;
37121 if ( region == 'center') {
37122 Roo.log("Center: " + cfg.title);
37128 case 'Content': // ContentPanel (el, cfg)
37129 case 'Scroll': // ContentPanel (el, cfg)
37131 cfg.autoCreate = cfg.autoCreate || true;
37132 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37134 // var el = this.el.createChild();
37135 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37138 this.add(region, ret);
37142 case 'TreePanel': // our new panel!
37143 cfg.el = this.el.createChild();
37144 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37145 this.add(region, ret);
37150 // create a new Layout (which is a Border Layout...
37152 var clayout = cfg.layout;
37153 clayout.el = this.el.createChild();
37154 clayout.items = clayout.items || [];
37158 // replace this exitems with the clayout ones..
37159 xitems = clayout.items;
37161 // force background off if it's in center...
37162 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37163 cfg.background = false;
37165 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37168 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37169 //console.log('adding nested layout panel ' + cfg.toSource());
37170 this.add(region, ret);
37171 nb = {}; /// find first...
37176 // needs grid and region
37178 //var el = this.getRegion(region).el.createChild();
37180 *var el = this.el.createChild();
37181 // create the grid first...
37182 cfg.grid.container = el;
37183 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37186 if (region == 'center' && this.active ) {
37187 cfg.background = false;
37190 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37192 this.add(region, ret);
37194 if (cfg.background) {
37195 // render grid on panel activation (if panel background)
37196 ret.on('activate', function(gp) {
37197 if (!gp.grid.rendered) {
37198 // gp.grid.render(el);
37202 // cfg.grid.render(el);
37208 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37209 // it was the old xcomponent building that caused this before.
37210 // espeically if border is the top element in the tree.
37220 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37222 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37223 this.add(region, ret);
37227 throw "Can not add '" + cfg.xtype + "' to Border";
37233 this.beginUpdate();
37237 Roo.each(xitems, function(i) {
37238 region = nb && i.region ? i.region : false;
37240 var add = ret.addxtype(i);
37243 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37244 if (!i.background) {
37245 abn[region] = nb[region] ;
37252 // make the last non-background panel active..
37253 //if (nb) { Roo.log(abn); }
37256 for(var r in abn) {
37257 region = this.getRegion(r);
37259 // tried using nb[r], but it does not work..
37261 region.showPanel(abn[r]);
37272 factory : function(cfg)
37275 var validRegions = Roo.bootstrap.layout.Border.regions;
37277 var target = cfg.region;
37280 var r = Roo.bootstrap.layout;
37284 return new r.North(cfg);
37286 return new r.South(cfg);
37288 return new r.East(cfg);
37290 return new r.West(cfg);
37292 return new r.Center(cfg);
37294 throw 'Layout region "'+target+'" not supported.';
37301 * Ext JS Library 1.1.1
37302 * Copyright(c) 2006-2007, Ext JS, LLC.
37304 * Originally Released Under LGPL - original licence link has changed is not relivant.
37307 * <script type="text/javascript">
37311 * @class Roo.bootstrap.layout.Basic
37312 * @extends Roo.util.Observable
37313 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37314 * and does not have a titlebar, tabs or any other features. All it does is size and position
37315 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37316 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37317 * @cfg {string} region the region that it inhabits..
37318 * @cfg {bool} skipConfig skip config?
37322 Roo.bootstrap.layout.Basic = function(config){
37324 this.mgr = config.mgr;
37326 this.position = config.region;
37328 var skipConfig = config.skipConfig;
37332 * @scope Roo.BasicLayoutRegion
37336 * @event beforeremove
37337 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37338 * @param {Roo.LayoutRegion} this
37339 * @param {Roo.ContentPanel} panel The panel
37340 * @param {Object} e The cancel event object
37342 "beforeremove" : true,
37344 * @event invalidated
37345 * Fires when the layout for this region is changed.
37346 * @param {Roo.LayoutRegion} this
37348 "invalidated" : true,
37350 * @event visibilitychange
37351 * Fires when this region is shown or hidden
37352 * @param {Roo.LayoutRegion} this
37353 * @param {Boolean} visibility true or false
37355 "visibilitychange" : true,
37357 * @event paneladded
37358 * Fires when a panel is added.
37359 * @param {Roo.LayoutRegion} this
37360 * @param {Roo.ContentPanel} panel The panel
37362 "paneladded" : true,
37364 * @event panelremoved
37365 * Fires when a panel is removed.
37366 * @param {Roo.LayoutRegion} this
37367 * @param {Roo.ContentPanel} panel The panel
37369 "panelremoved" : true,
37371 * @event beforecollapse
37372 * Fires when this region before collapse.
37373 * @param {Roo.LayoutRegion} this
37375 "beforecollapse" : true,
37378 * Fires when this region is collapsed.
37379 * @param {Roo.LayoutRegion} this
37381 "collapsed" : true,
37384 * Fires when this region is expanded.
37385 * @param {Roo.LayoutRegion} this
37390 * Fires when this region is slid into view.
37391 * @param {Roo.LayoutRegion} this
37393 "slideshow" : true,
37396 * Fires when this region slides out of view.
37397 * @param {Roo.LayoutRegion} this
37399 "slidehide" : true,
37401 * @event panelactivated
37402 * Fires when a panel is activated.
37403 * @param {Roo.LayoutRegion} this
37404 * @param {Roo.ContentPanel} panel The activated panel
37406 "panelactivated" : true,
37409 * Fires when the user resizes this region.
37410 * @param {Roo.LayoutRegion} this
37411 * @param {Number} newSize The new size (width for east/west, height for north/south)
37415 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37416 this.panels = new Roo.util.MixedCollection();
37417 this.panels.getKey = this.getPanelId.createDelegate(this);
37419 this.activePanel = null;
37420 // ensure listeners are added...
37422 if (config.listeners || config.events) {
37423 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37424 listeners : config.listeners || {},
37425 events : config.events || {}
37429 if(skipConfig !== true){
37430 this.applyConfig(config);
37434 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37436 getPanelId : function(p){
37440 applyConfig : function(config){
37441 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37442 this.config = config;
37447 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37448 * the width, for horizontal (north, south) the height.
37449 * @param {Number} newSize The new width or height
37451 resizeTo : function(newSize){
37452 var el = this.el ? this.el :
37453 (this.activePanel ? this.activePanel.getEl() : null);
37455 switch(this.position){
37458 el.setWidth(newSize);
37459 this.fireEvent("resized", this, newSize);
37463 el.setHeight(newSize);
37464 this.fireEvent("resized", this, newSize);
37470 getBox : function(){
37471 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37474 getMargins : function(){
37475 return this.margins;
37478 updateBox : function(box){
37480 var el = this.activePanel.getEl();
37481 el.dom.style.left = box.x + "px";
37482 el.dom.style.top = box.y + "px";
37483 this.activePanel.setSize(box.width, box.height);
37487 * Returns the container element for this region.
37488 * @return {Roo.Element}
37490 getEl : function(){
37491 return this.activePanel;
37495 * Returns true if this region is currently visible.
37496 * @return {Boolean}
37498 isVisible : function(){
37499 return this.activePanel ? true : false;
37502 setActivePanel : function(panel){
37503 panel = this.getPanel(panel);
37504 if(this.activePanel && this.activePanel != panel){
37505 this.activePanel.setActiveState(false);
37506 this.activePanel.getEl().setLeftTop(-10000,-10000);
37508 this.activePanel = panel;
37509 panel.setActiveState(true);
37511 panel.setSize(this.box.width, this.box.height);
37513 this.fireEvent("panelactivated", this, panel);
37514 this.fireEvent("invalidated");
37518 * Show the specified panel.
37519 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37520 * @return {Roo.ContentPanel} The shown panel or null
37522 showPanel : function(panel){
37523 panel = this.getPanel(panel);
37525 this.setActivePanel(panel);
37531 * Get the active panel for this region.
37532 * @return {Roo.ContentPanel} The active panel or null
37534 getActivePanel : function(){
37535 return this.activePanel;
37539 * Add the passed ContentPanel(s)
37540 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37541 * @return {Roo.ContentPanel} The panel added (if only one was added)
37543 add : function(panel){
37544 if(arguments.length > 1){
37545 for(var i = 0, len = arguments.length; i < len; i++) {
37546 this.add(arguments[i]);
37550 if(this.hasPanel(panel)){
37551 this.showPanel(panel);
37554 var el = panel.getEl();
37555 if(el.dom.parentNode != this.mgr.el.dom){
37556 this.mgr.el.dom.appendChild(el.dom);
37558 if(panel.setRegion){
37559 panel.setRegion(this);
37561 this.panels.add(panel);
37562 el.setStyle("position", "absolute");
37563 if(!panel.background){
37564 this.setActivePanel(panel);
37565 if(this.config.initialSize && this.panels.getCount()==1){
37566 this.resizeTo(this.config.initialSize);
37569 this.fireEvent("paneladded", this, panel);
37574 * Returns true if the panel is in this region.
37575 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37576 * @return {Boolean}
37578 hasPanel : function(panel){
37579 if(typeof panel == "object"){ // must be panel obj
37580 panel = panel.getId();
37582 return this.getPanel(panel) ? true : false;
37586 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37587 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37588 * @param {Boolean} preservePanel Overrides the config preservePanel option
37589 * @return {Roo.ContentPanel} The panel that was removed
37591 remove : function(panel, preservePanel){
37592 panel = this.getPanel(panel);
37597 this.fireEvent("beforeremove", this, panel, e);
37598 if(e.cancel === true){
37601 var panelId = panel.getId();
37602 this.panels.removeKey(panelId);
37607 * Returns the panel specified or null if it's not in this region.
37608 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37609 * @return {Roo.ContentPanel}
37611 getPanel : function(id){
37612 if(typeof id == "object"){ // must be panel obj
37615 return this.panels.get(id);
37619 * Returns this regions position (north/south/east/west/center).
37622 getPosition: function(){
37623 return this.position;
37627 * Ext JS Library 1.1.1
37628 * Copyright(c) 2006-2007, Ext JS, LLC.
37630 * Originally Released Under LGPL - original licence link has changed is not relivant.
37633 * <script type="text/javascript">
37637 * @class Roo.bootstrap.layout.Region
37638 * @extends Roo.bootstrap.layout.Basic
37639 * This class represents a region in a layout manager.
37641 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37642 * @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})
37643 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
37644 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
37645 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
37646 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
37647 * @cfg {String} title The title for the region (overrides panel titles)
37648 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
37649 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37650 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
37651 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37652 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37653 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37654 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37655 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37656 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37657 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37659 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37660 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37661 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37662 * @cfg {Number} width For East/West panels
37663 * @cfg {Number} height For North/South panels
37664 * @cfg {Boolean} split To show the splitter
37665 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37667 * @cfg {string} cls Extra CSS classes to add to region
37669 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37670 * @cfg {string} region the region that it inhabits..
37673 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37674 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37676 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37677 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37678 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37680 Roo.bootstrap.layout.Region = function(config)
37682 this.applyConfig(config);
37684 var mgr = config.mgr;
37685 var pos = config.region;
37686 config.skipConfig = true;
37687 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37690 this.onRender(mgr.el);
37693 this.visible = true;
37694 this.collapsed = false;
37695 this.unrendered_panels = [];
37698 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37700 position: '', // set by wrapper (eg. north/south etc..)
37701 unrendered_panels : null, // unrendered panels.
37703 tabPosition : false,
37705 mgr: false, // points to 'Border'
37708 createBody : function(){
37709 /** This region's body element
37710 * @type Roo.Element */
37711 this.bodyEl = this.el.createChild({
37713 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37717 onRender: function(ctr, pos)
37719 var dh = Roo.DomHelper;
37720 /** This region's container element
37721 * @type Roo.Element */
37722 this.el = dh.append(ctr.dom, {
37724 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37726 /** This region's title element
37727 * @type Roo.Element */
37729 this.titleEl = dh.append(this.el.dom, {
37731 unselectable: "on",
37732 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37734 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
37735 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37739 this.titleEl.enableDisplayMode();
37740 /** This region's title text element
37741 * @type HTMLElement */
37742 this.titleTextEl = this.titleEl.dom.firstChild;
37743 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37745 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37746 this.closeBtn.enableDisplayMode();
37747 this.closeBtn.on("click", this.closeClicked, this);
37748 this.closeBtn.hide();
37750 this.createBody(this.config);
37751 if(this.config.hideWhenEmpty){
37753 this.on("paneladded", this.validateVisibility, this);
37754 this.on("panelremoved", this.validateVisibility, this);
37756 if(this.autoScroll){
37757 this.bodyEl.setStyle("overflow", "auto");
37759 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37761 //if(c.titlebar !== false){
37762 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37763 this.titleEl.hide();
37765 this.titleEl.show();
37766 if(this.config.title){
37767 this.titleTextEl.innerHTML = this.config.title;
37771 if(this.config.collapsed){
37772 this.collapse(true);
37774 if(this.config.hidden){
37778 if (this.unrendered_panels && this.unrendered_panels.length) {
37779 for (var i =0;i< this.unrendered_panels.length; i++) {
37780 this.add(this.unrendered_panels[i]);
37782 this.unrendered_panels = null;
37788 applyConfig : function(c)
37791 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37792 var dh = Roo.DomHelper;
37793 if(c.titlebar !== false){
37794 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37795 this.collapseBtn.on("click", this.collapse, this);
37796 this.collapseBtn.enableDisplayMode();
37798 if(c.showPin === true || this.showPin){
37799 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37800 this.stickBtn.enableDisplayMode();
37801 this.stickBtn.on("click", this.expand, this);
37802 this.stickBtn.hide();
37807 /** This region's collapsed element
37808 * @type Roo.Element */
37811 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37812 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37815 if(c.floatable !== false){
37816 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37817 this.collapsedEl.on("click", this.collapseClick, this);
37820 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37821 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37822 id: "message", unselectable: "on", style:{"float":"left"}});
37823 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37825 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37826 this.expandBtn.on("click", this.expand, this);
37830 if(this.collapseBtn){
37831 this.collapseBtn.setVisible(c.collapsible == true);
37834 this.cmargins = c.cmargins || this.cmargins ||
37835 (this.position == "west" || this.position == "east" ?
37836 {top: 0, left: 2, right:2, bottom: 0} :
37837 {top: 2, left: 0, right:0, bottom: 2});
37839 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37842 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37844 this.autoScroll = c.autoScroll || false;
37849 this.duration = c.duration || .30;
37850 this.slideDuration = c.slideDuration || .45;
37855 * Returns true if this region is currently visible.
37856 * @return {Boolean}
37858 isVisible : function(){
37859 return this.visible;
37863 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37864 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
37866 //setCollapsedTitle : function(title){
37867 // title = title || " ";
37868 // if(this.collapsedTitleTextEl){
37869 // this.collapsedTitleTextEl.innerHTML = title;
37873 getBox : function(){
37875 // if(!this.collapsed){
37876 b = this.el.getBox(false, true);
37878 // b = this.collapsedEl.getBox(false, true);
37883 getMargins : function(){
37884 return this.margins;
37885 //return this.collapsed ? this.cmargins : this.margins;
37888 highlight : function(){
37889 this.el.addClass("x-layout-panel-dragover");
37892 unhighlight : function(){
37893 this.el.removeClass("x-layout-panel-dragover");
37896 updateBox : function(box)
37898 if (!this.bodyEl) {
37899 return; // not rendered yet..
37903 if(!this.collapsed){
37904 this.el.dom.style.left = box.x + "px";
37905 this.el.dom.style.top = box.y + "px";
37906 this.updateBody(box.width, box.height);
37908 this.collapsedEl.dom.style.left = box.x + "px";
37909 this.collapsedEl.dom.style.top = box.y + "px";
37910 this.collapsedEl.setSize(box.width, box.height);
37913 this.tabs.autoSizeTabs();
37917 updateBody : function(w, h)
37920 this.el.setWidth(w);
37921 w -= this.el.getBorderWidth("rl");
37922 if(this.config.adjustments){
37923 w += this.config.adjustments[0];
37926 if(h !== null && h > 0){
37927 this.el.setHeight(h);
37928 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37929 h -= this.el.getBorderWidth("tb");
37930 if(this.config.adjustments){
37931 h += this.config.adjustments[1];
37933 this.bodyEl.setHeight(h);
37935 h = this.tabs.syncHeight(h);
37938 if(this.panelSize){
37939 w = w !== null ? w : this.panelSize.width;
37940 h = h !== null ? h : this.panelSize.height;
37942 if(this.activePanel){
37943 var el = this.activePanel.getEl();
37944 w = w !== null ? w : el.getWidth();
37945 h = h !== null ? h : el.getHeight();
37946 this.panelSize = {width: w, height: h};
37947 this.activePanel.setSize(w, h);
37949 if(Roo.isIE && this.tabs){
37950 this.tabs.el.repaint();
37955 * Returns the container element for this region.
37956 * @return {Roo.Element}
37958 getEl : function(){
37963 * Hides this region.
37966 //if(!this.collapsed){
37967 this.el.dom.style.left = "-2000px";
37970 // this.collapsedEl.dom.style.left = "-2000px";
37971 // this.collapsedEl.hide();
37973 this.visible = false;
37974 this.fireEvent("visibilitychange", this, false);
37978 * Shows this region if it was previously hidden.
37981 //if(!this.collapsed){
37984 // this.collapsedEl.show();
37986 this.visible = true;
37987 this.fireEvent("visibilitychange", this, true);
37990 closeClicked : function(){
37991 if(this.activePanel){
37992 this.remove(this.activePanel);
37996 collapseClick : function(e){
37998 e.stopPropagation();
38001 e.stopPropagation();
38007 * Collapses this region.
38008 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38011 collapse : function(skipAnim, skipCheck = false){
38012 if(this.collapsed) {
38016 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38018 this.collapsed = true;
38020 this.split.el.hide();
38022 if(this.config.animate && skipAnim !== true){
38023 this.fireEvent("invalidated", this);
38024 this.animateCollapse();
38026 this.el.setLocation(-20000,-20000);
38028 this.collapsedEl.show();
38029 this.fireEvent("collapsed", this);
38030 this.fireEvent("invalidated", this);
38036 animateCollapse : function(){
38041 * Expands this region if it was previously collapsed.
38042 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38043 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38046 expand : function(e, skipAnim){
38048 e.stopPropagation();
38050 if(!this.collapsed || this.el.hasActiveFx()) {
38054 this.afterSlideIn();
38057 this.collapsed = false;
38058 if(this.config.animate && skipAnim !== true){
38059 this.animateExpand();
38063 this.split.el.show();
38065 this.collapsedEl.setLocation(-2000,-2000);
38066 this.collapsedEl.hide();
38067 this.fireEvent("invalidated", this);
38068 this.fireEvent("expanded", this);
38072 animateExpand : function(){
38076 initTabs : function()
38078 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38080 var ts = new Roo.bootstrap.panel.Tabs({
38081 el: this.bodyEl.dom,
38083 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38084 disableTooltips: this.config.disableTabTips,
38085 toolbar : this.config.toolbar
38088 if(this.config.hideTabs){
38089 ts.stripWrap.setDisplayed(false);
38092 ts.resizeTabs = this.config.resizeTabs === true;
38093 ts.minTabWidth = this.config.minTabWidth || 40;
38094 ts.maxTabWidth = this.config.maxTabWidth || 250;
38095 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38096 ts.monitorResize = false;
38097 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38098 ts.bodyEl.addClass('roo-layout-tabs-body');
38099 this.panels.each(this.initPanelAsTab, this);
38102 initPanelAsTab : function(panel){
38103 var ti = this.tabs.addTab(
38107 this.config.closeOnTab && panel.isClosable(),
38110 if(panel.tabTip !== undefined){
38111 ti.setTooltip(panel.tabTip);
38113 ti.on("activate", function(){
38114 this.setActivePanel(panel);
38117 if(this.config.closeOnTab){
38118 ti.on("beforeclose", function(t, e){
38120 this.remove(panel);
38124 panel.tabItem = ti;
38129 updatePanelTitle : function(panel, title)
38131 if(this.activePanel == panel){
38132 this.updateTitle(title);
38135 var ti = this.tabs.getTab(panel.getEl().id);
38137 if(panel.tabTip !== undefined){
38138 ti.setTooltip(panel.tabTip);
38143 updateTitle : function(title){
38144 if(this.titleTextEl && !this.config.title){
38145 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38149 setActivePanel : function(panel)
38151 panel = this.getPanel(panel);
38152 if(this.activePanel && this.activePanel != panel){
38153 if(this.activePanel.setActiveState(false) === false){
38157 this.activePanel = panel;
38158 panel.setActiveState(true);
38159 if(this.panelSize){
38160 panel.setSize(this.panelSize.width, this.panelSize.height);
38163 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38165 this.updateTitle(panel.getTitle());
38167 this.fireEvent("invalidated", this);
38169 this.fireEvent("panelactivated", this, panel);
38173 * Shows the specified panel.
38174 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38175 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38177 showPanel : function(panel)
38179 panel = this.getPanel(panel);
38182 var tab = this.tabs.getTab(panel.getEl().id);
38183 if(tab.isHidden()){
38184 this.tabs.unhideTab(tab.id);
38188 this.setActivePanel(panel);
38195 * Get the active panel for this region.
38196 * @return {Roo.ContentPanel} The active panel or null
38198 getActivePanel : function(){
38199 return this.activePanel;
38202 validateVisibility : function(){
38203 if(this.panels.getCount() < 1){
38204 this.updateTitle(" ");
38205 this.closeBtn.hide();
38208 if(!this.isVisible()){
38215 * Adds the passed ContentPanel(s) to this region.
38216 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38217 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38219 add : function(panel)
38221 if(arguments.length > 1){
38222 for(var i = 0, len = arguments.length; i < len; i++) {
38223 this.add(arguments[i]);
38228 // if we have not been rendered yet, then we can not really do much of this..
38229 if (!this.bodyEl) {
38230 this.unrendered_panels.push(panel);
38237 if(this.hasPanel(panel)){
38238 this.showPanel(panel);
38241 panel.setRegion(this);
38242 this.panels.add(panel);
38243 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38244 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38245 // and hide them... ???
38246 this.bodyEl.dom.appendChild(panel.getEl().dom);
38247 if(panel.background !== true){
38248 this.setActivePanel(panel);
38250 this.fireEvent("paneladded", this, panel);
38257 this.initPanelAsTab(panel);
38261 if(panel.background !== true){
38262 this.tabs.activate(panel.getEl().id);
38264 this.fireEvent("paneladded", this, panel);
38269 * Hides the tab for the specified panel.
38270 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38272 hidePanel : function(panel){
38273 if(this.tabs && (panel = this.getPanel(panel))){
38274 this.tabs.hideTab(panel.getEl().id);
38279 * Unhides the tab for a previously hidden panel.
38280 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38282 unhidePanel : function(panel){
38283 if(this.tabs && (panel = this.getPanel(panel))){
38284 this.tabs.unhideTab(panel.getEl().id);
38288 clearPanels : function(){
38289 while(this.panels.getCount() > 0){
38290 this.remove(this.panels.first());
38295 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38296 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38297 * @param {Boolean} preservePanel Overrides the config preservePanel option
38298 * @return {Roo.ContentPanel} The panel that was removed
38300 remove : function(panel, preservePanel)
38302 panel = this.getPanel(panel);
38307 this.fireEvent("beforeremove", this, panel, e);
38308 if(e.cancel === true){
38311 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38312 var panelId = panel.getId();
38313 this.panels.removeKey(panelId);
38315 document.body.appendChild(panel.getEl().dom);
38318 this.tabs.removeTab(panel.getEl().id);
38319 }else if (!preservePanel){
38320 this.bodyEl.dom.removeChild(panel.getEl().dom);
38322 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38323 var p = this.panels.first();
38324 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38325 tempEl.appendChild(p.getEl().dom);
38326 this.bodyEl.update("");
38327 this.bodyEl.dom.appendChild(p.getEl().dom);
38329 this.updateTitle(p.getTitle());
38331 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38332 this.setActivePanel(p);
38334 panel.setRegion(null);
38335 if(this.activePanel == panel){
38336 this.activePanel = null;
38338 if(this.config.autoDestroy !== false && preservePanel !== true){
38339 try{panel.destroy();}catch(e){}
38341 this.fireEvent("panelremoved", this, panel);
38346 * Returns the TabPanel component used by this region
38347 * @return {Roo.TabPanel}
38349 getTabs : function(){
38353 createTool : function(parentEl, className){
38354 var btn = Roo.DomHelper.append(parentEl, {
38356 cls: "x-layout-tools-button",
38359 cls: "roo-layout-tools-button-inner " + className,
38363 btn.addClassOnOver("roo-layout-tools-button-over");
38368 * Ext JS Library 1.1.1
38369 * Copyright(c) 2006-2007, Ext JS, LLC.
38371 * Originally Released Under LGPL - original licence link has changed is not relivant.
38374 * <script type="text/javascript">
38380 * @class Roo.SplitLayoutRegion
38381 * @extends Roo.LayoutRegion
38382 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38384 Roo.bootstrap.layout.Split = function(config){
38385 this.cursor = config.cursor;
38386 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38389 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38391 splitTip : "Drag to resize.",
38392 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38393 useSplitTips : false,
38395 applyConfig : function(config){
38396 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38399 onRender : function(ctr,pos) {
38401 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38402 if(!this.config.split){
38407 var splitEl = Roo.DomHelper.append(ctr.dom, {
38409 id: this.el.id + "-split",
38410 cls: "roo-layout-split roo-layout-split-"+this.position,
38413 /** The SplitBar for this region
38414 * @type Roo.SplitBar */
38415 // does not exist yet...
38416 Roo.log([this.position, this.orientation]);
38418 this.split = new Roo.bootstrap.SplitBar({
38419 dragElement : splitEl,
38420 resizingElement: this.el,
38421 orientation : this.orientation
38424 this.split.on("moved", this.onSplitMove, this);
38425 this.split.useShim = this.config.useShim === true;
38426 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38427 if(this.useSplitTips){
38428 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38430 //if(config.collapsible){
38431 // this.split.el.on("dblclick", this.collapse, this);
38434 if(typeof this.config.minSize != "undefined"){
38435 this.split.minSize = this.config.minSize;
38437 if(typeof this.config.maxSize != "undefined"){
38438 this.split.maxSize = this.config.maxSize;
38440 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38441 this.hideSplitter();
38446 getHMaxSize : function(){
38447 var cmax = this.config.maxSize || 10000;
38448 var center = this.mgr.getRegion("center");
38449 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38452 getVMaxSize : function(){
38453 var cmax = this.config.maxSize || 10000;
38454 var center = this.mgr.getRegion("center");
38455 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38458 onSplitMove : function(split, newSize){
38459 this.fireEvent("resized", this, newSize);
38463 * Returns the {@link Roo.SplitBar} for this region.
38464 * @return {Roo.SplitBar}
38466 getSplitBar : function(){
38471 this.hideSplitter();
38472 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38475 hideSplitter : function(){
38477 this.split.el.setLocation(-2000,-2000);
38478 this.split.el.hide();
38484 this.split.el.show();
38486 Roo.bootstrap.layout.Split.superclass.show.call(this);
38489 beforeSlide: function(){
38490 if(Roo.isGecko){// firefox overflow auto bug workaround
38491 this.bodyEl.clip();
38493 this.tabs.bodyEl.clip();
38495 if(this.activePanel){
38496 this.activePanel.getEl().clip();
38498 if(this.activePanel.beforeSlide){
38499 this.activePanel.beforeSlide();
38505 afterSlide : function(){
38506 if(Roo.isGecko){// firefox overflow auto bug workaround
38507 this.bodyEl.unclip();
38509 this.tabs.bodyEl.unclip();
38511 if(this.activePanel){
38512 this.activePanel.getEl().unclip();
38513 if(this.activePanel.afterSlide){
38514 this.activePanel.afterSlide();
38520 initAutoHide : function(){
38521 if(this.autoHide !== false){
38522 if(!this.autoHideHd){
38523 var st = new Roo.util.DelayedTask(this.slideIn, this);
38524 this.autoHideHd = {
38525 "mouseout": function(e){
38526 if(!e.within(this.el, true)){
38530 "mouseover" : function(e){
38536 this.el.on(this.autoHideHd);
38540 clearAutoHide : function(){
38541 if(this.autoHide !== false){
38542 this.el.un("mouseout", this.autoHideHd.mouseout);
38543 this.el.un("mouseover", this.autoHideHd.mouseover);
38547 clearMonitor : function(){
38548 Roo.get(document).un("click", this.slideInIf, this);
38551 // these names are backwards but not changed for compat
38552 slideOut : function(){
38553 if(this.isSlid || this.el.hasActiveFx()){
38556 this.isSlid = true;
38557 if(this.collapseBtn){
38558 this.collapseBtn.hide();
38560 this.closeBtnState = this.closeBtn.getStyle('display');
38561 this.closeBtn.hide();
38563 this.stickBtn.show();
38566 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38567 this.beforeSlide();
38568 this.el.setStyle("z-index", 10001);
38569 this.el.slideIn(this.getSlideAnchor(), {
38570 callback: function(){
38572 this.initAutoHide();
38573 Roo.get(document).on("click", this.slideInIf, this);
38574 this.fireEvent("slideshow", this);
38581 afterSlideIn : function(){
38582 this.clearAutoHide();
38583 this.isSlid = false;
38584 this.clearMonitor();
38585 this.el.setStyle("z-index", "");
38586 if(this.collapseBtn){
38587 this.collapseBtn.show();
38589 this.closeBtn.setStyle('display', this.closeBtnState);
38591 this.stickBtn.hide();
38593 this.fireEvent("slidehide", this);
38596 slideIn : function(cb){
38597 if(!this.isSlid || this.el.hasActiveFx()){
38601 this.isSlid = false;
38602 this.beforeSlide();
38603 this.el.slideOut(this.getSlideAnchor(), {
38604 callback: function(){
38605 this.el.setLeftTop(-10000, -10000);
38607 this.afterSlideIn();
38615 slideInIf : function(e){
38616 if(!e.within(this.el)){
38621 animateCollapse : function(){
38622 this.beforeSlide();
38623 this.el.setStyle("z-index", 20000);
38624 var anchor = this.getSlideAnchor();
38625 this.el.slideOut(anchor, {
38626 callback : function(){
38627 this.el.setStyle("z-index", "");
38628 this.collapsedEl.slideIn(anchor, {duration:.3});
38630 this.el.setLocation(-10000,-10000);
38632 this.fireEvent("collapsed", this);
38639 animateExpand : function(){
38640 this.beforeSlide();
38641 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38642 this.el.setStyle("z-index", 20000);
38643 this.collapsedEl.hide({
38646 this.el.slideIn(this.getSlideAnchor(), {
38647 callback : function(){
38648 this.el.setStyle("z-index", "");
38651 this.split.el.show();
38653 this.fireEvent("invalidated", this);
38654 this.fireEvent("expanded", this);
38682 getAnchor : function(){
38683 return this.anchors[this.position];
38686 getCollapseAnchor : function(){
38687 return this.canchors[this.position];
38690 getSlideAnchor : function(){
38691 return this.sanchors[this.position];
38694 getAlignAdj : function(){
38695 var cm = this.cmargins;
38696 switch(this.position){
38712 getExpandAdj : function(){
38713 var c = this.collapsedEl, cm = this.cmargins;
38714 switch(this.position){
38716 return [-(cm.right+c.getWidth()+cm.left), 0];
38719 return [cm.right+c.getWidth()+cm.left, 0];
38722 return [0, -(cm.top+cm.bottom+c.getHeight())];
38725 return [0, cm.top+cm.bottom+c.getHeight()];
38731 * Ext JS Library 1.1.1
38732 * Copyright(c) 2006-2007, Ext JS, LLC.
38734 * Originally Released Under LGPL - original licence link has changed is not relivant.
38737 * <script type="text/javascript">
38740 * These classes are private internal classes
38742 Roo.bootstrap.layout.Center = function(config){
38743 config.region = "center";
38744 Roo.bootstrap.layout.Region.call(this, config);
38745 this.visible = true;
38746 this.minWidth = config.minWidth || 20;
38747 this.minHeight = config.minHeight || 20;
38750 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38752 // center panel can't be hidden
38756 // center panel can't be hidden
38759 getMinWidth: function(){
38760 return this.minWidth;
38763 getMinHeight: function(){
38764 return this.minHeight;
38778 Roo.bootstrap.layout.North = function(config)
38780 config.region = 'north';
38781 config.cursor = 'n-resize';
38783 Roo.bootstrap.layout.Split.call(this, config);
38787 this.split.placement = Roo.bootstrap.SplitBar.TOP;
38788 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38789 this.split.el.addClass("roo-layout-split-v");
38791 var size = config.initialSize || config.height;
38792 if(typeof size != "undefined"){
38793 this.el.setHeight(size);
38796 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38798 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38802 getBox : function(){
38803 if(this.collapsed){
38804 return this.collapsedEl.getBox();
38806 var box = this.el.getBox();
38808 box.height += this.split.el.getHeight();
38813 updateBox : function(box){
38814 if(this.split && !this.collapsed){
38815 box.height -= this.split.el.getHeight();
38816 this.split.el.setLeft(box.x);
38817 this.split.el.setTop(box.y+box.height);
38818 this.split.el.setWidth(box.width);
38820 if(this.collapsed){
38821 this.updateBody(box.width, null);
38823 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38831 Roo.bootstrap.layout.South = function(config){
38832 config.region = 'south';
38833 config.cursor = 's-resize';
38834 Roo.bootstrap.layout.Split.call(this, config);
38836 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38837 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38838 this.split.el.addClass("roo-layout-split-v");
38840 var size = config.initialSize || config.height;
38841 if(typeof size != "undefined"){
38842 this.el.setHeight(size);
38846 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38847 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38848 getBox : function(){
38849 if(this.collapsed){
38850 return this.collapsedEl.getBox();
38852 var box = this.el.getBox();
38854 var sh = this.split.el.getHeight();
38861 updateBox : function(box){
38862 if(this.split && !this.collapsed){
38863 var sh = this.split.el.getHeight();
38866 this.split.el.setLeft(box.x);
38867 this.split.el.setTop(box.y-sh);
38868 this.split.el.setWidth(box.width);
38870 if(this.collapsed){
38871 this.updateBody(box.width, null);
38873 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38877 Roo.bootstrap.layout.East = function(config){
38878 config.region = "east";
38879 config.cursor = "e-resize";
38880 Roo.bootstrap.layout.Split.call(this, config);
38882 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38883 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38884 this.split.el.addClass("roo-layout-split-h");
38886 var size = config.initialSize || config.width;
38887 if(typeof size != "undefined"){
38888 this.el.setWidth(size);
38891 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38892 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38893 getBox : function(){
38894 if(this.collapsed){
38895 return this.collapsedEl.getBox();
38897 var box = this.el.getBox();
38899 var sw = this.split.el.getWidth();
38906 updateBox : function(box){
38907 if(this.split && !this.collapsed){
38908 var sw = this.split.el.getWidth();
38910 this.split.el.setLeft(box.x);
38911 this.split.el.setTop(box.y);
38912 this.split.el.setHeight(box.height);
38915 if(this.collapsed){
38916 this.updateBody(null, box.height);
38918 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38922 Roo.bootstrap.layout.West = function(config){
38923 config.region = "west";
38924 config.cursor = "w-resize";
38926 Roo.bootstrap.layout.Split.call(this, config);
38928 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38929 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38930 this.split.el.addClass("roo-layout-split-h");
38934 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38935 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38937 onRender: function(ctr, pos)
38939 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38940 var size = this.config.initialSize || this.config.width;
38941 if(typeof size != "undefined"){
38942 this.el.setWidth(size);
38946 getBox : function(){
38947 if(this.collapsed){
38948 return this.collapsedEl.getBox();
38950 var box = this.el.getBox();
38952 box.width += this.split.el.getWidth();
38957 updateBox : function(box){
38958 if(this.split && !this.collapsed){
38959 var sw = this.split.el.getWidth();
38961 this.split.el.setLeft(box.x+box.width);
38962 this.split.el.setTop(box.y);
38963 this.split.el.setHeight(box.height);
38965 if(this.collapsed){
38966 this.updateBody(null, box.height);
38968 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38970 });Roo.namespace("Roo.bootstrap.panel");/*
38972 * Ext JS Library 1.1.1
38973 * Copyright(c) 2006-2007, Ext JS, LLC.
38975 * Originally Released Under LGPL - original licence link has changed is not relivant.
38978 * <script type="text/javascript">
38981 * @class Roo.ContentPanel
38982 * @extends Roo.util.Observable
38983 * A basic ContentPanel element.
38984 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
38985 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
38986 * @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
38987 * @cfg {Boolean} closable True if the panel can be closed/removed
38988 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
38989 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38990 * @cfg {Toolbar} toolbar A toolbar for this panel
38991 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
38992 * @cfg {String} title The title for this panel
38993 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38994 * @cfg {String} url Calls {@link #setUrl} with this value
38995 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38996 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
38997 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
38998 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
38999 * @cfg {Boolean} badges render the badges
39002 * Create a new ContentPanel.
39003 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39004 * @param {String/Object} config A string to set only the title or a config object
39005 * @param {String} content (optional) Set the HTML content for this panel
39006 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39008 Roo.bootstrap.panel.Content = function( config){
39010 this.tpl = config.tpl || false;
39012 var el = config.el;
39013 var content = config.content;
39015 if(config.autoCreate){ // xtype is available if this is called from factory
39018 this.el = Roo.get(el);
39019 if(!this.el && config && config.autoCreate){
39020 if(typeof config.autoCreate == "object"){
39021 if(!config.autoCreate.id){
39022 config.autoCreate.id = config.id||el;
39024 this.el = Roo.DomHelper.append(document.body,
39025 config.autoCreate, true);
39027 var elcfg = { tag: "div",
39028 cls: "roo-layout-inactive-content",
39032 elcfg.html = config.html;
39036 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39039 this.closable = false;
39040 this.loaded = false;
39041 this.active = false;
39044 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39046 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39048 this.wrapEl = this.el; //this.el.wrap();
39050 if (config.toolbar.items) {
39051 ti = config.toolbar.items ;
39052 delete config.toolbar.items ;
39056 this.toolbar.render(this.wrapEl, 'before');
39057 for(var i =0;i < ti.length;i++) {
39058 // Roo.log(['add child', items[i]]);
39059 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39061 this.toolbar.items = nitems;
39062 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39063 delete config.toolbar;
39067 // xtype created footer. - not sure if will work as we normally have to render first..
39068 if (this.footer && !this.footer.el && this.footer.xtype) {
39069 if (!this.wrapEl) {
39070 this.wrapEl = this.el.wrap();
39073 this.footer.container = this.wrapEl.createChild();
39075 this.footer = Roo.factory(this.footer, Roo);
39080 if(typeof config == "string"){
39081 this.title = config;
39083 Roo.apply(this, config);
39087 this.resizeEl = Roo.get(this.resizeEl, true);
39089 this.resizeEl = this.el;
39091 // handle view.xtype
39099 * Fires when this panel is activated.
39100 * @param {Roo.ContentPanel} this
39104 * @event deactivate
39105 * Fires when this panel is activated.
39106 * @param {Roo.ContentPanel} this
39108 "deactivate" : true,
39112 * Fires when this panel is resized if fitToFrame is true.
39113 * @param {Roo.ContentPanel} this
39114 * @param {Number} width The width after any component adjustments
39115 * @param {Number} height The height after any component adjustments
39121 * Fires when this tab is created
39122 * @param {Roo.ContentPanel} this
39133 if(this.autoScroll){
39134 this.resizeEl.setStyle("overflow", "auto");
39136 // fix randome scrolling
39137 //this.el.on('scroll', function() {
39138 // Roo.log('fix random scolling');
39139 // this.scrollTo('top',0);
39142 content = content || this.content;
39144 this.setContent(content);
39146 if(config && config.url){
39147 this.setUrl(this.url, this.params, this.loadOnce);
39152 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39154 if (this.view && typeof(this.view.xtype) != 'undefined') {
39155 this.view.el = this.el.appendChild(document.createElement("div"));
39156 this.view = Roo.factory(this.view);
39157 this.view.render && this.view.render(false, '');
39161 this.fireEvent('render', this);
39164 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39168 setRegion : function(region){
39169 this.region = region;
39170 this.setActiveClass(region && !this.background);
39174 setActiveClass: function(state)
39177 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39178 this.el.setStyle('position','relative');
39180 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39181 this.el.setStyle('position', 'absolute');
39186 * Returns the toolbar for this Panel if one was configured.
39187 * @return {Roo.Toolbar}
39189 getToolbar : function(){
39190 return this.toolbar;
39193 setActiveState : function(active)
39195 this.active = active;
39196 this.setActiveClass(active);
39198 if(this.fireEvent("deactivate", this) === false){
39203 this.fireEvent("activate", this);
39207 * Updates this panel's element
39208 * @param {String} content The new content
39209 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39211 setContent : function(content, loadScripts){
39212 this.el.update(content, loadScripts);
39215 ignoreResize : function(w, h){
39216 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39219 this.lastSize = {width: w, height: h};
39224 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39225 * @return {Roo.UpdateManager} The UpdateManager
39227 getUpdateManager : function(){
39228 return this.el.getUpdateManager();
39231 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39232 * @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:
39235 url: "your-url.php",
39236 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39237 callback: yourFunction,
39238 scope: yourObject, //(optional scope)
39241 text: "Loading...",
39246 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39247 * 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.
39248 * @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}
39249 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39250 * @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.
39251 * @return {Roo.ContentPanel} this
39254 var um = this.el.getUpdateManager();
39255 um.update.apply(um, arguments);
39261 * 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.
39262 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39263 * @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)
39264 * @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)
39265 * @return {Roo.UpdateManager} The UpdateManager
39267 setUrl : function(url, params, loadOnce){
39268 if(this.refreshDelegate){
39269 this.removeListener("activate", this.refreshDelegate);
39271 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39272 this.on("activate", this.refreshDelegate);
39273 return this.el.getUpdateManager();
39276 _handleRefresh : function(url, params, loadOnce){
39277 if(!loadOnce || !this.loaded){
39278 var updater = this.el.getUpdateManager();
39279 updater.update(url, params, this._setLoaded.createDelegate(this));
39283 _setLoaded : function(){
39284 this.loaded = true;
39288 * Returns this panel's id
39291 getId : function(){
39296 * Returns this panel's element - used by regiosn to add.
39297 * @return {Roo.Element}
39299 getEl : function(){
39300 return this.wrapEl || this.el;
39305 adjustForComponents : function(width, height)
39307 //Roo.log('adjustForComponents ');
39308 if(this.resizeEl != this.el){
39309 width -= this.el.getFrameWidth('lr');
39310 height -= this.el.getFrameWidth('tb');
39313 var te = this.toolbar.getEl();
39314 te.setWidth(width);
39315 height -= te.getHeight();
39318 var te = this.footer.getEl();
39319 te.setWidth(width);
39320 height -= te.getHeight();
39324 if(this.adjustments){
39325 width += this.adjustments[0];
39326 height += this.adjustments[1];
39328 return {"width": width, "height": height};
39331 setSize : function(width, height){
39332 if(this.fitToFrame && !this.ignoreResize(width, height)){
39333 if(this.fitContainer && this.resizeEl != this.el){
39334 this.el.setSize(width, height);
39336 var size = this.adjustForComponents(width, height);
39337 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39338 this.fireEvent('resize', this, size.width, size.height);
39343 * Returns this panel's title
39346 getTitle : function(){
39348 if (typeof(this.title) != 'object') {
39353 for (var k in this.title) {
39354 if (!this.title.hasOwnProperty(k)) {
39358 if (k.indexOf('-') >= 0) {
39359 var s = k.split('-');
39360 for (var i = 0; i<s.length; i++) {
39361 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39364 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39371 * Set this panel's title
39372 * @param {String} title
39374 setTitle : function(title){
39375 this.title = title;
39377 this.region.updatePanelTitle(this, title);
39382 * Returns true is this panel was configured to be closable
39383 * @return {Boolean}
39385 isClosable : function(){
39386 return this.closable;
39389 beforeSlide : function(){
39391 this.resizeEl.clip();
39394 afterSlide : function(){
39396 this.resizeEl.unclip();
39400 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39401 * Will fail silently if the {@link #setUrl} method has not been called.
39402 * This does not activate the panel, just updates its content.
39404 refresh : function(){
39405 if(this.refreshDelegate){
39406 this.loaded = false;
39407 this.refreshDelegate();
39412 * Destroys this panel
39414 destroy : function(){
39415 this.el.removeAllListeners();
39416 var tempEl = document.createElement("span");
39417 tempEl.appendChild(this.el.dom);
39418 tempEl.innerHTML = "";
39424 * form - if the content panel contains a form - this is a reference to it.
39425 * @type {Roo.form.Form}
39429 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39430 * This contains a reference to it.
39436 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39446 * @param {Object} cfg Xtype definition of item to add.
39450 getChildContainer: function () {
39451 return this.getEl();
39456 var ret = new Roo.factory(cfg);
39461 if (cfg.xtype.match(/^Form$/)) {
39464 //if (this.footer) {
39465 // el = this.footer.container.insertSibling(false, 'before');
39467 el = this.el.createChild();
39470 this.form = new Roo.form.Form(cfg);
39473 if ( this.form.allItems.length) {
39474 this.form.render(el.dom);
39478 // should only have one of theses..
39479 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39480 // views.. should not be just added - used named prop 'view''
39482 cfg.el = this.el.appendChild(document.createElement("div"));
39485 var ret = new Roo.factory(cfg);
39487 ret.render && ret.render(false, ''); // render blank..
39497 * @class Roo.bootstrap.panel.Grid
39498 * @extends Roo.bootstrap.panel.Content
39500 * Create a new GridPanel.
39501 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39502 * @param {Object} config A the config object
39508 Roo.bootstrap.panel.Grid = function(config)
39512 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39513 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39515 config.el = this.wrapper;
39516 //this.el = this.wrapper;
39518 if (config.container) {
39519 // ctor'ed from a Border/panel.grid
39522 this.wrapper.setStyle("overflow", "hidden");
39523 this.wrapper.addClass('roo-grid-container');
39528 if(config.toolbar){
39529 var tool_el = this.wrapper.createChild();
39530 this.toolbar = Roo.factory(config.toolbar);
39532 if (config.toolbar.items) {
39533 ti = config.toolbar.items ;
39534 delete config.toolbar.items ;
39538 this.toolbar.render(tool_el);
39539 for(var i =0;i < ti.length;i++) {
39540 // Roo.log(['add child', items[i]]);
39541 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39543 this.toolbar.items = nitems;
39545 delete config.toolbar;
39548 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39549 config.grid.scrollBody = true;;
39550 config.grid.monitorWindowResize = false; // turn off autosizing
39551 config.grid.autoHeight = false;
39552 config.grid.autoWidth = false;
39554 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39556 if (config.background) {
39557 // render grid on panel activation (if panel background)
39558 this.on('activate', function(gp) {
39559 if (!gp.grid.rendered) {
39560 gp.grid.render(this.wrapper);
39561 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39566 this.grid.render(this.wrapper);
39567 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39570 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39571 // ??? needed ??? config.el = this.wrapper;
39576 // xtype created footer. - not sure if will work as we normally have to render first..
39577 if (this.footer && !this.footer.el && this.footer.xtype) {
39579 var ctr = this.grid.getView().getFooterPanel(true);
39580 this.footer.dataSource = this.grid.dataSource;
39581 this.footer = Roo.factory(this.footer, Roo);
39582 this.footer.render(ctr);
39592 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39593 getId : function(){
39594 return this.grid.id;
39598 * Returns the grid for this panel
39599 * @return {Roo.bootstrap.Table}
39601 getGrid : function(){
39605 setSize : function(width, height){
39606 if(!this.ignoreResize(width, height)){
39607 var grid = this.grid;
39608 var size = this.adjustForComponents(width, height);
39609 // tfoot is not a footer?
39612 var gridel = grid.getGridEl();
39613 gridel.setSize(size.width, size.height);
39615 var tbd = grid.getGridEl().select('tbody', true).first();
39616 var thd = grid.getGridEl().select('thead',true).first();
39617 var tbf= grid.getGridEl().select('tfoot', true).first();
39620 size.height -= thd.getHeight();
39623 size.height -= thd.getHeight();
39626 tbd.setSize(size.width, size.height );
39627 // this is for the account management tab -seems to work there.
39628 var thd = grid.getGridEl().select('thead',true).first();
39630 // tbd.setSize(size.width, size.height - thd.getHeight());
39639 beforeSlide : function(){
39640 this.grid.getView().scroller.clip();
39643 afterSlide : function(){
39644 this.grid.getView().scroller.unclip();
39647 destroy : function(){
39648 this.grid.destroy();
39650 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
39655 * @class Roo.bootstrap.panel.Nest
39656 * @extends Roo.bootstrap.panel.Content
39658 * Create a new Panel, that can contain a layout.Border.
39661 * @param {Roo.BorderLayout} layout The layout for this panel
39662 * @param {String/Object} config A string to set only the title or a config object
39664 Roo.bootstrap.panel.Nest = function(config)
39666 // construct with only one argument..
39667 /* FIXME - implement nicer consturctors
39668 if (layout.layout) {
39670 layout = config.layout;
39671 delete config.layout;
39673 if (layout.xtype && !layout.getEl) {
39674 // then layout needs constructing..
39675 layout = Roo.factory(layout, Roo);
39679 config.el = config.layout.getEl();
39681 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39683 config.layout.monitorWindowResize = false; // turn off autosizing
39684 this.layout = config.layout;
39685 this.layout.getEl().addClass("roo-layout-nested-layout");
39686 this.layout.parent = this;
39693 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39695 setSize : function(width, height){
39696 if(!this.ignoreResize(width, height)){
39697 var size = this.adjustForComponents(width, height);
39698 var el = this.layout.getEl();
39699 if (size.height < 1) {
39700 el.setWidth(size.width);
39702 el.setSize(size.width, size.height);
39704 var touch = el.dom.offsetWidth;
39705 this.layout.layout();
39706 // ie requires a double layout on the first pass
39707 if(Roo.isIE && !this.initialized){
39708 this.initialized = true;
39709 this.layout.layout();
39714 // activate all subpanels if not currently active..
39716 setActiveState : function(active){
39717 this.active = active;
39718 this.setActiveClass(active);
39721 this.fireEvent("deactivate", this);
39725 this.fireEvent("activate", this);
39726 // not sure if this should happen before or after..
39727 if (!this.layout) {
39728 return; // should not happen..
39731 for (var r in this.layout.regions) {
39732 reg = this.layout.getRegion(r);
39733 if (reg.getActivePanel()) {
39734 //reg.showPanel(reg.getActivePanel()); // force it to activate..
39735 reg.setActivePanel(reg.getActivePanel());
39738 if (!reg.panels.length) {
39741 reg.showPanel(reg.getPanel(0));
39750 * Returns the nested BorderLayout for this panel
39751 * @return {Roo.BorderLayout}
39753 getLayout : function(){
39754 return this.layout;
39758 * Adds a xtype elements to the layout of the nested panel
39762 xtype : 'ContentPanel',
39769 xtype : 'NestedLayoutPanel',
39775 items : [ ... list of content panels or nested layout panels.. ]
39779 * @param {Object} cfg Xtype definition of item to add.
39781 addxtype : function(cfg) {
39782 return this.layout.addxtype(cfg);
39787 * Ext JS Library 1.1.1
39788 * Copyright(c) 2006-2007, Ext JS, LLC.
39790 * Originally Released Under LGPL - original licence link has changed is not relivant.
39793 * <script type="text/javascript">
39796 * @class Roo.TabPanel
39797 * @extends Roo.util.Observable
39798 * A lightweight tab container.
39802 // basic tabs 1, built from existing content
39803 var tabs = new Roo.TabPanel("tabs1");
39804 tabs.addTab("script", "View Script");
39805 tabs.addTab("markup", "View Markup");
39806 tabs.activate("script");
39808 // more advanced tabs, built from javascript
39809 var jtabs = new Roo.TabPanel("jtabs");
39810 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39812 // set up the UpdateManager
39813 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39814 var updater = tab2.getUpdateManager();
39815 updater.setDefaultUrl("ajax1.htm");
39816 tab2.on('activate', updater.refresh, updater, true);
39818 // Use setUrl for Ajax loading
39819 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39820 tab3.setUrl("ajax2.htm", null, true);
39823 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39826 jtabs.activate("jtabs-1");
39829 * Create a new TabPanel.
39830 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39831 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39833 Roo.bootstrap.panel.Tabs = function(config){
39835 * The container element for this TabPanel.
39836 * @type Roo.Element
39838 this.el = Roo.get(config.el);
39841 if(typeof config == "boolean"){
39842 this.tabPosition = config ? "bottom" : "top";
39844 Roo.apply(this, config);
39848 if(this.tabPosition == "bottom"){
39849 // if tabs are at the bottom = create the body first.
39850 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39851 this.el.addClass("roo-tabs-bottom");
39853 // next create the tabs holders
39855 if (this.tabPosition == "west"){
39857 var reg = this.region; // fake it..
39859 if (!reg.mgr.parent) {
39862 reg = reg.mgr.parent.region;
39864 Roo.log("got nest?");
39866 if (reg.mgr.getRegion('west')) {
39867 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39868 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39869 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39870 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39871 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39879 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39880 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39881 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39882 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39887 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39890 // finally - if tabs are at the top, then create the body last..
39891 if(this.tabPosition != "bottom"){
39892 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39893 * @type Roo.Element
39895 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39896 this.el.addClass("roo-tabs-top");
39900 this.bodyEl.setStyle("position", "relative");
39902 this.active = null;
39903 this.activateDelegate = this.activate.createDelegate(this);
39908 * Fires when the active tab changes
39909 * @param {Roo.TabPanel} this
39910 * @param {Roo.TabPanelItem} activePanel The new active tab
39914 * @event beforetabchange
39915 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39916 * @param {Roo.TabPanel} this
39917 * @param {Object} e Set cancel to true on this object to cancel the tab change
39918 * @param {Roo.TabPanelItem} tab The tab being changed to
39920 "beforetabchange" : true
39923 Roo.EventManager.onWindowResize(this.onResize, this);
39924 this.cpad = this.el.getPadding("lr");
39925 this.hiddenCount = 0;
39928 // toolbar on the tabbar support...
39929 if (this.toolbar) {
39930 alert("no toolbar support yet");
39931 this.toolbar = false;
39933 var tcfg = this.toolbar;
39934 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
39935 this.toolbar = new Roo.Toolbar(tcfg);
39936 if (Roo.isSafari) {
39937 var tbl = tcfg.container.child('table', true);
39938 tbl.setAttribute('width', '100%');
39946 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39949 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39951 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39953 tabPosition : "top",
39955 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39957 currentTabWidth : 0,
39959 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39963 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39967 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39969 preferredTabWidth : 175,
39971 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39973 resizeTabs : false,
39975 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39977 monitorResize : true,
39979 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
39981 toolbar : false, // set by caller..
39983 region : false, /// set by caller
39985 disableTooltips : true, // not used yet...
39988 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39989 * @param {String} id The id of the div to use <b>or create</b>
39990 * @param {String} text The text for the tab
39991 * @param {String} content (optional) Content to put in the TabPanelItem body
39992 * @param {Boolean} closable (optional) True to create a close icon on the tab
39993 * @return {Roo.TabPanelItem} The created TabPanelItem
39995 addTab : function(id, text, content, closable, tpl)
39997 var item = new Roo.bootstrap.panel.TabItem({
40001 closable : closable,
40004 this.addTabItem(item);
40006 item.setContent(content);
40012 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40013 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40014 * @return {Roo.TabPanelItem}
40016 getTab : function(id){
40017 return this.items[id];
40021 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40022 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40024 hideTab : function(id){
40025 var t = this.items[id];
40028 this.hiddenCount++;
40029 this.autoSizeTabs();
40034 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40035 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40037 unhideTab : function(id){
40038 var t = this.items[id];
40040 t.setHidden(false);
40041 this.hiddenCount--;
40042 this.autoSizeTabs();
40047 * Adds an existing {@link Roo.TabPanelItem}.
40048 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40050 addTabItem : function(item)
40052 this.items[item.id] = item;
40053 this.items.push(item);
40054 this.autoSizeTabs();
40055 // if(this.resizeTabs){
40056 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40057 // this.autoSizeTabs();
40059 // item.autoSize();
40064 * Removes a {@link Roo.TabPanelItem}.
40065 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40067 removeTab : function(id){
40068 var items = this.items;
40069 var tab = items[id];
40070 if(!tab) { return; }
40071 var index = items.indexOf(tab);
40072 if(this.active == tab && items.length > 1){
40073 var newTab = this.getNextAvailable(index);
40078 this.stripEl.dom.removeChild(tab.pnode.dom);
40079 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40080 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40082 items.splice(index, 1);
40083 delete this.items[tab.id];
40084 tab.fireEvent("close", tab);
40085 tab.purgeListeners();
40086 this.autoSizeTabs();
40089 getNextAvailable : function(start){
40090 var items = this.items;
40092 // look for a next tab that will slide over to
40093 // replace the one being removed
40094 while(index < items.length){
40095 var item = items[++index];
40096 if(item && !item.isHidden()){
40100 // if one isn't found select the previous tab (on the left)
40103 var item = items[--index];
40104 if(item && !item.isHidden()){
40112 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40113 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40115 disableTab : function(id){
40116 var tab = this.items[id];
40117 if(tab && this.active != tab){
40123 * Enables a {@link Roo.TabPanelItem} that is disabled.
40124 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40126 enableTab : function(id){
40127 var tab = this.items[id];
40132 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40133 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40134 * @return {Roo.TabPanelItem} The TabPanelItem.
40136 activate : function(id)
40138 //Roo.log('activite:' + id);
40140 var tab = this.items[id];
40144 if(tab == this.active || tab.disabled){
40148 this.fireEvent("beforetabchange", this, e, tab);
40149 if(e.cancel !== true && !tab.disabled){
40151 this.active.hide();
40153 this.active = this.items[id];
40154 this.active.show();
40155 this.fireEvent("tabchange", this, this.active);
40161 * Gets the active {@link Roo.TabPanelItem}.
40162 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40164 getActiveTab : function(){
40165 return this.active;
40169 * Updates the tab body element to fit the height of the container element
40170 * for overflow scrolling
40171 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40173 syncHeight : function(targetHeight){
40174 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40175 var bm = this.bodyEl.getMargins();
40176 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40177 this.bodyEl.setHeight(newHeight);
40181 onResize : function(){
40182 if(this.monitorResize){
40183 this.autoSizeTabs();
40188 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40190 beginUpdate : function(){
40191 this.updating = true;
40195 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40197 endUpdate : function(){
40198 this.updating = false;
40199 this.autoSizeTabs();
40203 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40205 autoSizeTabs : function()
40207 var count = this.items.length;
40208 var vcount = count - this.hiddenCount;
40211 this.stripEl.hide();
40213 this.stripEl.show();
40216 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40221 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40222 var availWidth = Math.floor(w / vcount);
40223 var b = this.stripBody;
40224 if(b.getWidth() > w){
40225 var tabs = this.items;
40226 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40227 if(availWidth < this.minTabWidth){
40228 /*if(!this.sleft){ // incomplete scrolling code
40229 this.createScrollButtons();
40232 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40235 if(this.currentTabWidth < this.preferredTabWidth){
40236 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40242 * Returns the number of tabs in this TabPanel.
40245 getCount : function(){
40246 return this.items.length;
40250 * Resizes all the tabs to the passed width
40251 * @param {Number} The new width
40253 setTabWidth : function(width){
40254 this.currentTabWidth = width;
40255 for(var i = 0, len = this.items.length; i < len; i++) {
40256 if(!this.items[i].isHidden()) {
40257 this.items[i].setWidth(width);
40263 * Destroys this TabPanel
40264 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40266 destroy : function(removeEl){
40267 Roo.EventManager.removeResizeListener(this.onResize, this);
40268 for(var i = 0, len = this.items.length; i < len; i++){
40269 this.items[i].purgeListeners();
40271 if(removeEl === true){
40272 this.el.update("");
40277 createStrip : function(container)
40279 var strip = document.createElement("nav");
40280 strip.className = Roo.bootstrap.version == 4 ?
40281 "navbar-light bg-light" :
40282 "navbar navbar-default"; //"x-tabs-wrap";
40283 container.appendChild(strip);
40287 createStripList : function(strip)
40289 // div wrapper for retard IE
40290 // returns the "tr" element.
40291 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40292 //'<div class="x-tabs-strip-wrap">'+
40293 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40294 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40295 return strip.firstChild; //.firstChild.firstChild.firstChild;
40297 createBody : function(container)
40299 var body = document.createElement("div");
40300 Roo.id(body, "tab-body");
40301 //Roo.fly(body).addClass("x-tabs-body");
40302 Roo.fly(body).addClass("tab-content");
40303 container.appendChild(body);
40306 createItemBody :function(bodyEl, id){
40307 var body = Roo.getDom(id);
40309 body = document.createElement("div");
40312 //Roo.fly(body).addClass("x-tabs-item-body");
40313 Roo.fly(body).addClass("tab-pane");
40314 bodyEl.insertBefore(body, bodyEl.firstChild);
40318 createStripElements : function(stripEl, text, closable, tpl)
40320 var td = document.createElement("li"); // was td..
40321 td.className = 'nav-item';
40323 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40326 stripEl.appendChild(td);
40328 td.className = "x-tabs-closable";
40329 if(!this.closeTpl){
40330 this.closeTpl = new Roo.Template(
40331 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40332 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40333 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40336 var el = this.closeTpl.overwrite(td, {"text": text});
40337 var close = el.getElementsByTagName("div")[0];
40338 var inner = el.getElementsByTagName("em")[0];
40339 return {"el": el, "close": close, "inner": inner};
40342 // not sure what this is..
40343 // if(!this.tabTpl){
40344 //this.tabTpl = new Roo.Template(
40345 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40346 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40348 // this.tabTpl = new Roo.Template(
40349 // '<a href="#">' +
40350 // '<span unselectable="on"' +
40351 // (this.disableTooltips ? '' : ' title="{text}"') +
40352 // ' >{text}</span></a>'
40358 var template = tpl || this.tabTpl || false;
40361 template = new Roo.Template(
40362 Roo.bootstrap.version == 4 ?
40364 '<a class="nav-link" href="#" unselectable="on"' +
40365 (this.disableTooltips ? '' : ' title="{text}"') +
40368 '<a class="nav-link" href="#">' +
40369 '<span unselectable="on"' +
40370 (this.disableTooltips ? '' : ' title="{text}"') +
40371 ' >{text}</span></a>'
40376 switch (typeof(template)) {
40380 template = new Roo.Template(template);
40386 var el = template.overwrite(td, {"text": text});
40388 var inner = el.getElementsByTagName("span")[0];
40390 return {"el": el, "inner": inner};
40398 * @class Roo.TabPanelItem
40399 * @extends Roo.util.Observable
40400 * Represents an individual item (tab plus body) in a TabPanel.
40401 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40402 * @param {String} id The id of this TabPanelItem
40403 * @param {String} text The text for the tab of this TabPanelItem
40404 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40406 Roo.bootstrap.panel.TabItem = function(config){
40408 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40409 * @type Roo.TabPanel
40411 this.tabPanel = config.panel;
40413 * The id for this TabPanelItem
40416 this.id = config.id;
40418 this.disabled = false;
40420 this.text = config.text;
40422 this.loaded = false;
40423 this.closable = config.closable;
40426 * The body element for this TabPanelItem.
40427 * @type Roo.Element
40429 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40430 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40431 this.bodyEl.setStyle("display", "block");
40432 this.bodyEl.setStyle("zoom", "1");
40433 //this.hideAction();
40435 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40437 this.el = Roo.get(els.el);
40438 this.inner = Roo.get(els.inner, true);
40439 this.textEl = Roo.bootstrap.version == 4 ?
40440 this.el : Roo.get(this.el.dom.firstChild, true);
40442 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40443 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40446 // this.el.on("mousedown", this.onTabMouseDown, this);
40447 this.el.on("click", this.onTabClick, this);
40449 if(config.closable){
40450 var c = Roo.get(els.close, true);
40451 c.dom.title = this.closeText;
40452 c.addClassOnOver("close-over");
40453 c.on("click", this.closeClick, this);
40459 * Fires when this tab becomes the active tab.
40460 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40461 * @param {Roo.TabPanelItem} this
40465 * @event beforeclose
40466 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40467 * @param {Roo.TabPanelItem} this
40468 * @param {Object} e Set cancel to true on this object to cancel the close.
40470 "beforeclose": true,
40473 * Fires when this tab is closed.
40474 * @param {Roo.TabPanelItem} this
40478 * @event deactivate
40479 * Fires when this tab is no longer the active tab.
40480 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40481 * @param {Roo.TabPanelItem} this
40483 "deactivate" : true
40485 this.hidden = false;
40487 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40490 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40492 purgeListeners : function(){
40493 Roo.util.Observable.prototype.purgeListeners.call(this);
40494 this.el.removeAllListeners();
40497 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40500 this.status_node.addClass("active");
40503 this.tabPanel.stripWrap.repaint();
40505 this.fireEvent("activate", this.tabPanel, this);
40509 * Returns true if this tab is the active tab.
40510 * @return {Boolean}
40512 isActive : function(){
40513 return this.tabPanel.getActiveTab() == this;
40517 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40520 this.status_node.removeClass("active");
40522 this.fireEvent("deactivate", this.tabPanel, this);
40525 hideAction : function(){
40526 this.bodyEl.hide();
40527 this.bodyEl.setStyle("position", "absolute");
40528 this.bodyEl.setLeft("-20000px");
40529 this.bodyEl.setTop("-20000px");
40532 showAction : function(){
40533 this.bodyEl.setStyle("position", "relative");
40534 this.bodyEl.setTop("");
40535 this.bodyEl.setLeft("");
40536 this.bodyEl.show();
40540 * Set the tooltip for the tab.
40541 * @param {String} tooltip The tab's tooltip
40543 setTooltip : function(text){
40544 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40545 this.textEl.dom.qtip = text;
40546 this.textEl.dom.removeAttribute('title');
40548 this.textEl.dom.title = text;
40552 onTabClick : function(e){
40553 e.preventDefault();
40554 this.tabPanel.activate(this.id);
40557 onTabMouseDown : function(e){
40558 e.preventDefault();
40559 this.tabPanel.activate(this.id);
40562 getWidth : function(){
40563 return this.inner.getWidth();
40566 setWidth : function(width){
40567 var iwidth = width - this.linode.getPadding("lr");
40568 this.inner.setWidth(iwidth);
40569 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40570 this.linode.setWidth(width);
40574 * Show or hide the tab
40575 * @param {Boolean} hidden True to hide or false to show.
40577 setHidden : function(hidden){
40578 this.hidden = hidden;
40579 this.linode.setStyle("display", hidden ? "none" : "");
40583 * Returns true if this tab is "hidden"
40584 * @return {Boolean}
40586 isHidden : function(){
40587 return this.hidden;
40591 * Returns the text for this tab
40594 getText : function(){
40598 autoSize : function(){
40599 //this.el.beginMeasure();
40600 this.textEl.setWidth(1);
40602 * #2804 [new] Tabs in Roojs
40603 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40605 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40606 //this.el.endMeasure();
40610 * Sets the text for the tab (Note: this also sets the tooltip text)
40611 * @param {String} text The tab's text and tooltip
40613 setText : function(text){
40615 this.textEl.update(text);
40616 this.setTooltip(text);
40617 //if(!this.tabPanel.resizeTabs){
40618 // this.autoSize();
40622 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40624 activate : function(){
40625 this.tabPanel.activate(this.id);
40629 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40631 disable : function(){
40632 if(this.tabPanel.active != this){
40633 this.disabled = true;
40634 this.status_node.addClass("disabled");
40639 * Enables this TabPanelItem if it was previously disabled.
40641 enable : function(){
40642 this.disabled = false;
40643 this.status_node.removeClass("disabled");
40647 * Sets the content for this TabPanelItem.
40648 * @param {String} content The content
40649 * @param {Boolean} loadScripts true to look for and load scripts
40651 setContent : function(content, loadScripts){
40652 this.bodyEl.update(content, loadScripts);
40656 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40657 * @return {Roo.UpdateManager} The UpdateManager
40659 getUpdateManager : function(){
40660 return this.bodyEl.getUpdateManager();
40664 * Set a URL to be used to load the content for this TabPanelItem.
40665 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40666 * @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)
40667 * @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)
40668 * @return {Roo.UpdateManager} The UpdateManager
40670 setUrl : function(url, params, loadOnce){
40671 if(this.refreshDelegate){
40672 this.un('activate', this.refreshDelegate);
40674 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40675 this.on("activate", this.refreshDelegate);
40676 return this.bodyEl.getUpdateManager();
40680 _handleRefresh : function(url, params, loadOnce){
40681 if(!loadOnce || !this.loaded){
40682 var updater = this.bodyEl.getUpdateManager();
40683 updater.update(url, params, this._setLoaded.createDelegate(this));
40688 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40689 * Will fail silently if the setUrl method has not been called.
40690 * This does not activate the panel, just updates its content.
40692 refresh : function(){
40693 if(this.refreshDelegate){
40694 this.loaded = false;
40695 this.refreshDelegate();
40700 _setLoaded : function(){
40701 this.loaded = true;
40705 closeClick : function(e){
40708 this.fireEvent("beforeclose", this, o);
40709 if(o.cancel !== true){
40710 this.tabPanel.removeTab(this.id);
40714 * The text displayed in the tooltip for the close icon.
40717 closeText : "Close this tab"
40720 * This script refer to:
40721 * Title: International Telephone Input
40722 * Author: Jack O'Connor
40723 * Code version: v12.1.12
40724 * Availability: https://github.com/jackocnr/intl-tel-input.git
40727 Roo.bootstrap.PhoneInputData = function() {
40730 "Afghanistan (افغانستان)",
40735 "Albania (Shqipëri)",
40740 "Algeria (الجزائر)",
40765 "Antigua and Barbuda",
40775 "Armenia (Հայաստան)",
40791 "Austria (Österreich)",
40796 "Azerbaijan (Azərbaycan)",
40806 "Bahrain (البحرين)",
40811 "Bangladesh (বাংলাদেশ)",
40821 "Belarus (Беларусь)",
40826 "Belgium (België)",
40856 "Bosnia and Herzegovina (Босна и Херцеговина)",
40871 "British Indian Ocean Territory",
40876 "British Virgin Islands",
40886 "Bulgaria (България)",
40896 "Burundi (Uburundi)",
40901 "Cambodia (កម្ពុជា)",
40906 "Cameroon (Cameroun)",
40915 ["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"]
40918 "Cape Verde (Kabu Verdi)",
40923 "Caribbean Netherlands",
40934 "Central African Republic (République centrafricaine)",
40954 "Christmas Island",
40960 "Cocos (Keeling) Islands",
40971 "Comoros (جزر القمر)",
40976 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40981 "Congo (Republic) (Congo-Brazzaville)",
41001 "Croatia (Hrvatska)",
41022 "Czech Republic (Česká republika)",
41027 "Denmark (Danmark)",
41042 "Dominican Republic (República Dominicana)",
41046 ["809", "829", "849"]
41064 "Equatorial Guinea (Guinea Ecuatorial)",
41084 "Falkland Islands (Islas Malvinas)",
41089 "Faroe Islands (Føroyar)",
41110 "French Guiana (Guyane française)",
41115 "French Polynesia (Polynésie française)",
41130 "Georgia (საქართველო)",
41135 "Germany (Deutschland)",
41155 "Greenland (Kalaallit Nunaat)",
41192 "Guinea-Bissau (Guiné Bissau)",
41217 "Hungary (Magyarország)",
41222 "Iceland (Ísland)",
41242 "Iraq (العراق)",
41258 "Israel (ישראל)",
41285 "Jordan (الأردن)",
41290 "Kazakhstan (Казахстан)",
41311 "Kuwait (الكويت)",
41316 "Kyrgyzstan (Кыргызстан)",
41326 "Latvia (Latvija)",
41331 "Lebanon (لبنان)",
41346 "Libya (ليبيا)",
41356 "Lithuania (Lietuva)",
41371 "Macedonia (FYROM) (Македонија)",
41376 "Madagascar (Madagasikara)",
41406 "Marshall Islands",
41416 "Mauritania (موريتانيا)",
41421 "Mauritius (Moris)",
41442 "Moldova (Republica Moldova)",
41452 "Mongolia (Монгол)",
41457 "Montenegro (Crna Gora)",
41467 "Morocco (المغرب)",
41473 "Mozambique (Moçambique)",
41478 "Myanmar (Burma) (မြန်မာ)",
41483 "Namibia (Namibië)",
41498 "Netherlands (Nederland)",
41503 "New Caledonia (Nouvelle-Calédonie)",
41538 "North Korea (조선 민주주의 인민 공화국)",
41543 "Northern Mariana Islands",
41559 "Pakistan (پاکستان)",
41569 "Palestine (فلسطين)",
41579 "Papua New Guinea",
41621 "Réunion (La Réunion)",
41627 "Romania (România)",
41643 "Saint Barthélemy",
41654 "Saint Kitts and Nevis",
41664 "Saint Martin (Saint-Martin (partie française))",
41670 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41675 "Saint Vincent and the Grenadines",
41690 "São Tomé and Príncipe (São Tomé e Príncipe)",
41695 "Saudi Arabia (المملكة العربية السعودية)",
41700 "Senegal (Sénégal)",
41730 "Slovakia (Slovensko)",
41735 "Slovenia (Slovenija)",
41745 "Somalia (Soomaaliya)",
41755 "South Korea (대한민국)",
41760 "South Sudan (جنوب السودان)",
41770 "Sri Lanka (ශ්රී ලංකාව)",
41775 "Sudan (السودان)",
41785 "Svalbard and Jan Mayen",
41796 "Sweden (Sverige)",
41801 "Switzerland (Schweiz)",
41806 "Syria (سوريا)",
41851 "Trinidad and Tobago",
41856 "Tunisia (تونس)",
41861 "Turkey (Türkiye)",
41871 "Turks and Caicos Islands",
41881 "U.S. Virgin Islands",
41891 "Ukraine (Україна)",
41896 "United Arab Emirates (الإمارات العربية المتحدة)",
41918 "Uzbekistan (Oʻzbekiston)",
41928 "Vatican City (Città del Vaticano)",
41939 "Vietnam (Việt Nam)",
41944 "Wallis and Futuna (Wallis-et-Futuna)",
41949 "Western Sahara (الصحراء الغربية)",
41955 "Yemen (اليمن)",
41979 * This script refer to:
41980 * Title: International Telephone Input
41981 * Author: Jack O'Connor
41982 * Code version: v12.1.12
41983 * Availability: https://github.com/jackocnr/intl-tel-input.git
41987 * @class Roo.bootstrap.PhoneInput
41988 * @extends Roo.bootstrap.TriggerField
41989 * An input with International dial-code selection
41991 * @cfg {String} defaultDialCode default '+852'
41992 * @cfg {Array} preferedCountries default []
41995 * Create a new PhoneInput.
41996 * @param {Object} config Configuration options
41999 Roo.bootstrap.PhoneInput = function(config) {
42000 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42003 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42005 listWidth: undefined,
42007 selectedClass: 'active',
42009 invalidClass : "has-warning",
42011 validClass: 'has-success',
42013 allowed: '0123456789',
42018 * @cfg {String} defaultDialCode The default dial code when initializing the input
42020 defaultDialCode: '+852',
42023 * @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
42025 preferedCountries: false,
42027 getAutoCreate : function()
42029 var data = Roo.bootstrap.PhoneInputData();
42030 var align = this.labelAlign || this.parentLabelAlign();
42033 this.allCountries = [];
42034 this.dialCodeMapping = [];
42036 for (var i = 0; i < data.length; i++) {
42038 this.allCountries[i] = {
42042 priority: c[3] || 0,
42043 areaCodes: c[4] || null
42045 this.dialCodeMapping[c[2]] = {
42048 priority: c[3] || 0,
42049 areaCodes: c[4] || null
42061 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42062 maxlength: this.max_length,
42063 cls : 'form-control tel-input',
42064 autocomplete: 'new-password'
42067 var hiddenInput = {
42070 cls: 'hidden-tel-input'
42074 hiddenInput.name = this.name;
42077 if (this.disabled) {
42078 input.disabled = true;
42081 var flag_container = {
42098 cls: this.hasFeedback ? 'has-feedback' : '',
42104 cls: 'dial-code-holder',
42111 cls: 'roo-select2-container input-group',
42118 if (this.fieldLabel.length) {
42121 tooltip: 'This field is required'
42127 cls: 'control-label',
42133 html: this.fieldLabel
42136 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42142 if(this.indicatorpos == 'right') {
42143 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42150 if(align == 'left') {
42158 if(this.labelWidth > 12){
42159 label.style = "width: " + this.labelWidth + 'px';
42161 if(this.labelWidth < 13 && this.labelmd == 0){
42162 this.labelmd = this.labelWidth;
42164 if(this.labellg > 0){
42165 label.cls += ' col-lg-' + this.labellg;
42166 input.cls += ' col-lg-' + (12 - this.labellg);
42168 if(this.labelmd > 0){
42169 label.cls += ' col-md-' + this.labelmd;
42170 container.cls += ' col-md-' + (12 - this.labelmd);
42172 if(this.labelsm > 0){
42173 label.cls += ' col-sm-' + this.labelsm;
42174 container.cls += ' col-sm-' + (12 - this.labelsm);
42176 if(this.labelxs > 0){
42177 label.cls += ' col-xs-' + this.labelxs;
42178 container.cls += ' col-xs-' + (12 - this.labelxs);
42188 var settings = this;
42190 ['xs','sm','md','lg'].map(function(size){
42191 if (settings[size]) {
42192 cfg.cls += ' col-' + size + '-' + settings[size];
42196 this.store = new Roo.data.Store({
42197 proxy : new Roo.data.MemoryProxy({}),
42198 reader : new Roo.data.JsonReader({
42209 'name' : 'dialCode',
42213 'name' : 'priority',
42217 'name' : 'areaCodes',
42224 if(!this.preferedCountries) {
42225 this.preferedCountries = [
42232 var p = this.preferedCountries.reverse();
42235 for (var i = 0; i < p.length; i++) {
42236 for (var j = 0; j < this.allCountries.length; j++) {
42237 if(this.allCountries[j].iso2 == p[i]) {
42238 var t = this.allCountries[j];
42239 this.allCountries.splice(j,1);
42240 this.allCountries.unshift(t);
42246 this.store.proxy.data = {
42248 data: this.allCountries
42254 initEvents : function()
42257 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42259 this.indicator = this.indicatorEl();
42260 this.flag = this.flagEl();
42261 this.dialCodeHolder = this.dialCodeHolderEl();
42263 this.trigger = this.el.select('div.flag-box',true).first();
42264 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42269 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42270 _this.list.setWidth(lw);
42273 this.list.on('mouseover', this.onViewOver, this);
42274 this.list.on('mousemove', this.onViewMove, this);
42275 this.inputEl().on("keyup", this.onKeyUp, this);
42276 this.inputEl().on("keypress", this.onKeyPress, this);
42278 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42280 this.view = new Roo.View(this.list, this.tpl, {
42281 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42284 this.view.on('click', this.onViewClick, this);
42285 this.setValue(this.defaultDialCode);
42288 onTriggerClick : function(e)
42290 Roo.log('trigger click');
42295 if(this.isExpanded()){
42297 this.hasFocus = false;
42299 this.store.load({});
42300 this.hasFocus = true;
42305 isExpanded : function()
42307 return this.list.isVisible();
42310 collapse : function()
42312 if(!this.isExpanded()){
42316 Roo.get(document).un('mousedown', this.collapseIf, this);
42317 Roo.get(document).un('mousewheel', this.collapseIf, this);
42318 this.fireEvent('collapse', this);
42322 expand : function()
42326 if(this.isExpanded() || !this.hasFocus){
42330 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42331 this.list.setWidth(lw);
42334 this.restrictHeight();
42336 Roo.get(document).on('mousedown', this.collapseIf, this);
42337 Roo.get(document).on('mousewheel', this.collapseIf, this);
42339 this.fireEvent('expand', this);
42342 restrictHeight : function()
42344 this.list.alignTo(this.inputEl(), this.listAlign);
42345 this.list.alignTo(this.inputEl(), this.listAlign);
42348 onViewOver : function(e, t)
42350 if(this.inKeyMode){
42353 var item = this.view.findItemFromChild(t);
42356 var index = this.view.indexOf(item);
42357 this.select(index, false);
42362 onViewClick : function(view, doFocus, el, e)
42364 var index = this.view.getSelectedIndexes()[0];
42366 var r = this.store.getAt(index);
42369 this.onSelect(r, index);
42371 if(doFocus !== false && !this.blockFocus){
42372 this.inputEl().focus();
42376 onViewMove : function(e, t)
42378 this.inKeyMode = false;
42381 select : function(index, scrollIntoView)
42383 this.selectedIndex = index;
42384 this.view.select(index);
42385 if(scrollIntoView !== false){
42386 var el = this.view.getNode(index);
42388 this.list.scrollChildIntoView(el, false);
42393 createList : function()
42395 this.list = Roo.get(document.body).createChild({
42397 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42398 style: 'display:none'
42401 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42404 collapseIf : function(e)
42406 var in_combo = e.within(this.el);
42407 var in_list = e.within(this.list);
42408 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42410 if (in_combo || in_list || is_list) {
42416 onSelect : function(record, index)
42418 if(this.fireEvent('beforeselect', this, record, index) !== false){
42420 this.setFlagClass(record.data.iso2);
42421 this.setDialCode(record.data.dialCode);
42422 this.hasFocus = false;
42424 this.fireEvent('select', this, record, index);
42428 flagEl : function()
42430 var flag = this.el.select('div.flag',true).first();
42437 dialCodeHolderEl : function()
42439 var d = this.el.select('input.dial-code-holder',true).first();
42446 setDialCode : function(v)
42448 this.dialCodeHolder.dom.value = '+'+v;
42451 setFlagClass : function(n)
42453 this.flag.dom.className = 'flag '+n;
42456 getValue : function()
42458 var v = this.inputEl().getValue();
42459 if(this.dialCodeHolder) {
42460 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42465 setValue : function(v)
42467 var d = this.getDialCode(v);
42469 //invalid dial code
42470 if(v.length == 0 || !d || d.length == 0) {
42472 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42473 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42479 this.setFlagClass(this.dialCodeMapping[d].iso2);
42480 this.setDialCode(d);
42481 this.inputEl().dom.value = v.replace('+'+d,'');
42482 this.hiddenEl().dom.value = this.getValue();
42487 getDialCode : function(v)
42491 if (v.length == 0) {
42492 return this.dialCodeHolder.dom.value;
42496 if (v.charAt(0) != "+") {
42499 var numericChars = "";
42500 for (var i = 1; i < v.length; i++) {
42501 var c = v.charAt(i);
42504 if (this.dialCodeMapping[numericChars]) {
42505 dialCode = v.substr(1, i);
42507 if (numericChars.length == 4) {
42517 this.setValue(this.defaultDialCode);
42521 hiddenEl : function()
42523 return this.el.select('input.hidden-tel-input',true).first();
42526 // after setting val
42527 onKeyUp : function(e){
42528 this.setValue(this.getValue());
42531 onKeyPress : function(e){
42532 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42539 * @class Roo.bootstrap.MoneyField
42540 * @extends Roo.bootstrap.ComboBox
42541 * Bootstrap MoneyField class
42544 * Create a new MoneyField.
42545 * @param {Object} config Configuration options
42548 Roo.bootstrap.MoneyField = function(config) {
42550 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42554 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42557 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42559 allowDecimals : true,
42561 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42563 decimalSeparator : ".",
42565 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42567 decimalPrecision : 0,
42569 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42571 allowNegative : true,
42573 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42577 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42579 minValue : Number.NEGATIVE_INFINITY,
42581 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42583 maxValue : Number.MAX_VALUE,
42585 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42587 minText : "The minimum value for this field is {0}",
42589 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42591 maxText : "The maximum value for this field is {0}",
42593 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
42594 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42596 nanText : "{0} is not a valid number",
42598 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42602 * @cfg {String} defaults currency of the MoneyField
42603 * value should be in lkey
42605 defaultCurrency : false,
42607 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42609 thousandsDelimiter : false,
42611 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42622 getAutoCreate : function()
42624 var align = this.labelAlign || this.parentLabelAlign();
42636 cls : 'form-control roo-money-amount-input',
42637 autocomplete: 'new-password'
42640 var hiddenInput = {
42644 cls: 'hidden-number-input'
42647 if(this.max_length) {
42648 input.maxlength = this.max_length;
42652 hiddenInput.name = this.name;
42655 if (this.disabled) {
42656 input.disabled = true;
42659 var clg = 12 - this.inputlg;
42660 var cmd = 12 - this.inputmd;
42661 var csm = 12 - this.inputsm;
42662 var cxs = 12 - this.inputxs;
42666 cls : 'row roo-money-field',
42670 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42674 cls: 'roo-select2-container input-group',
42678 cls : 'form-control roo-money-currency-input',
42679 autocomplete: 'new-password',
42681 name : this.currencyName
42685 cls : 'input-group-addon',
42699 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42703 cls: this.hasFeedback ? 'has-feedback' : '',
42714 if (this.fieldLabel.length) {
42717 tooltip: 'This field is required'
42723 cls: 'control-label',
42729 html: this.fieldLabel
42732 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42738 if(this.indicatorpos == 'right') {
42739 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42746 if(align == 'left') {
42754 if(this.labelWidth > 12){
42755 label.style = "width: " + this.labelWidth + 'px';
42757 if(this.labelWidth < 13 && this.labelmd == 0){
42758 this.labelmd = this.labelWidth;
42760 if(this.labellg > 0){
42761 label.cls += ' col-lg-' + this.labellg;
42762 input.cls += ' col-lg-' + (12 - this.labellg);
42764 if(this.labelmd > 0){
42765 label.cls += ' col-md-' + this.labelmd;
42766 container.cls += ' col-md-' + (12 - this.labelmd);
42768 if(this.labelsm > 0){
42769 label.cls += ' col-sm-' + this.labelsm;
42770 container.cls += ' col-sm-' + (12 - this.labelsm);
42772 if(this.labelxs > 0){
42773 label.cls += ' col-xs-' + this.labelxs;
42774 container.cls += ' col-xs-' + (12 - this.labelxs);
42785 var settings = this;
42787 ['xs','sm','md','lg'].map(function(size){
42788 if (settings[size]) {
42789 cfg.cls += ' col-' + size + '-' + settings[size];
42796 initEvents : function()
42798 this.indicator = this.indicatorEl();
42800 this.initCurrencyEvent();
42802 this.initNumberEvent();
42805 initCurrencyEvent : function()
42808 throw "can not find store for combo";
42811 this.store = Roo.factory(this.store, Roo.data);
42812 this.store.parent = this;
42816 this.triggerEl = this.el.select('.input-group-addon', true).first();
42818 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42823 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42824 _this.list.setWidth(lw);
42827 this.list.on('mouseover', this.onViewOver, this);
42828 this.list.on('mousemove', this.onViewMove, this);
42829 this.list.on('scroll', this.onViewScroll, this);
42832 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42835 this.view = new Roo.View(this.list, this.tpl, {
42836 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42839 this.view.on('click', this.onViewClick, this);
42841 this.store.on('beforeload', this.onBeforeLoad, this);
42842 this.store.on('load', this.onLoad, this);
42843 this.store.on('loadexception', this.onLoadException, this);
42845 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42846 "up" : function(e){
42847 this.inKeyMode = true;
42851 "down" : function(e){
42852 if(!this.isExpanded()){
42853 this.onTriggerClick();
42855 this.inKeyMode = true;
42860 "enter" : function(e){
42863 if(this.fireEvent("specialkey", this, e)){
42864 this.onViewClick(false);
42870 "esc" : function(e){
42874 "tab" : function(e){
42877 if(this.fireEvent("specialkey", this, e)){
42878 this.onViewClick(false);
42886 doRelay : function(foo, bar, hname){
42887 if(hname == 'down' || this.scope.isExpanded()){
42888 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42896 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42900 initNumberEvent : function(e)
42902 this.inputEl().on("keydown" , this.fireKey, this);
42903 this.inputEl().on("focus", this.onFocus, this);
42904 this.inputEl().on("blur", this.onBlur, this);
42906 this.inputEl().relayEvent('keyup', this);
42908 if(this.indicator){
42909 this.indicator.addClass('invisible');
42912 this.originalValue = this.getValue();
42914 if(this.validationEvent == 'keyup'){
42915 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42916 this.inputEl().on('keyup', this.filterValidation, this);
42918 else if(this.validationEvent !== false){
42919 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42922 if(this.selectOnFocus){
42923 this.on("focus", this.preFocus, this);
42926 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42927 this.inputEl().on("keypress", this.filterKeys, this);
42929 this.inputEl().relayEvent('keypress', this);
42932 var allowed = "0123456789";
42934 if(this.allowDecimals){
42935 allowed += this.decimalSeparator;
42938 if(this.allowNegative){
42942 if(this.thousandsDelimiter) {
42946 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42948 var keyPress = function(e){
42950 var k = e.getKey();
42952 var c = e.getCharCode();
42955 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42956 allowed.indexOf(String.fromCharCode(c)) === -1
42962 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42966 if(allowed.indexOf(String.fromCharCode(c)) === -1){
42971 this.inputEl().on("keypress", keyPress, this);
42975 onTriggerClick : function(e)
42982 this.loadNext = false;
42984 if(this.isExpanded()){
42989 this.hasFocus = true;
42991 if(this.triggerAction == 'all') {
42992 this.doQuery(this.allQuery, true);
42996 this.doQuery(this.getRawValue());
42999 getCurrency : function()
43001 var v = this.currencyEl().getValue();
43006 restrictHeight : function()
43008 this.list.alignTo(this.currencyEl(), this.listAlign);
43009 this.list.alignTo(this.currencyEl(), this.listAlign);
43012 onViewClick : function(view, doFocus, el, e)
43014 var index = this.view.getSelectedIndexes()[0];
43016 var r = this.store.getAt(index);
43019 this.onSelect(r, index);
43023 onSelect : function(record, index){
43025 if(this.fireEvent('beforeselect', this, record, index) !== false){
43027 this.setFromCurrencyData(index > -1 ? record.data : false);
43031 this.fireEvent('select', this, record, index);
43035 setFromCurrencyData : function(o)
43039 this.lastCurrency = o;
43041 if (this.currencyField) {
43042 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43044 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43047 this.lastSelectionText = currency;
43049 //setting default currency
43050 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43051 this.setCurrency(this.defaultCurrency);
43055 this.setCurrency(currency);
43058 setFromData : function(o)
43062 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43064 this.setFromCurrencyData(c);
43069 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43071 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43074 this.setValue(value);
43078 setCurrency : function(v)
43080 this.currencyValue = v;
43083 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43088 setValue : function(v)
43090 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43096 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43098 this.inputEl().dom.value = (v == '') ? '' :
43099 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43101 if(!this.allowZero && v === '0') {
43102 this.hiddenEl().dom.value = '';
43103 this.inputEl().dom.value = '';
43110 getRawValue : function()
43112 var v = this.inputEl().getValue();
43117 getValue : function()
43119 return this.fixPrecision(this.parseValue(this.getRawValue()));
43122 parseValue : function(value)
43124 if(this.thousandsDelimiter) {
43126 r = new RegExp(",", "g");
43127 value = value.replace(r, "");
43130 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43131 return isNaN(value) ? '' : value;
43135 fixPrecision : function(value)
43137 if(this.thousandsDelimiter) {
43139 r = new RegExp(",", "g");
43140 value = value.replace(r, "");
43143 var nan = isNaN(value);
43145 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43146 return nan ? '' : value;
43148 return parseFloat(value).toFixed(this.decimalPrecision);
43151 decimalPrecisionFcn : function(v)
43153 return Math.floor(v);
43156 validateValue : function(value)
43158 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43162 var num = this.parseValue(value);
43165 this.markInvalid(String.format(this.nanText, value));
43169 if(num < this.minValue){
43170 this.markInvalid(String.format(this.minText, this.minValue));
43174 if(num > this.maxValue){
43175 this.markInvalid(String.format(this.maxText, this.maxValue));
43182 validate : function()
43184 if(this.disabled || this.allowBlank){
43189 var currency = this.getCurrency();
43191 if(this.validateValue(this.getRawValue()) && currency.length){
43196 this.markInvalid();
43200 getName: function()
43205 beforeBlur : function()
43211 var v = this.parseValue(this.getRawValue());
43218 onBlur : function()
43222 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43223 //this.el.removeClass(this.focusClass);
43226 this.hasFocus = false;
43228 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43232 var v = this.getValue();
43234 if(String(v) !== String(this.startValue)){
43235 this.fireEvent('change', this, v, this.startValue);
43238 this.fireEvent("blur", this);
43241 inputEl : function()
43243 return this.el.select('.roo-money-amount-input', true).first();
43246 currencyEl : function()
43248 return this.el.select('.roo-money-currency-input', true).first();
43251 hiddenEl : function()
43253 return this.el.select('input.hidden-number-input',true).first();
43257 * @class Roo.bootstrap.BezierSignature
43258 * @extends Roo.bootstrap.Component
43259 * Bootstrap BezierSignature class
43260 * This script refer to:
43261 * Title: Signature Pad
43263 * Availability: https://github.com/szimek/signature_pad
43266 * Create a new BezierSignature
43267 * @param {Object} config The config object
43270 Roo.bootstrap.BezierSignature = function(config){
43271 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43277 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43284 mouse_btn_down: true,
43287 * @cfg {int} canvas height
43289 canvas_height: '200px',
43292 * @cfg {float|function} Radius of a single dot.
43297 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43302 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43307 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43312 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43317 * @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.
43319 bg_color: 'rgba(0, 0, 0, 0)',
43322 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43324 dot_color: 'black',
43327 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43329 velocity_filter_weight: 0.7,
43332 * @cfg {function} Callback when stroke begin.
43337 * @cfg {function} Callback when stroke end.
43341 getAutoCreate : function()
43343 var cls = 'roo-signature column';
43346 cls += ' ' + this.cls;
43356 for(var i = 0; i < col_sizes.length; i++) {
43357 if(this[col_sizes[i]]) {
43358 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43368 cls: 'roo-signature-body',
43372 cls: 'roo-signature-body-canvas',
43373 height: this.canvas_height,
43374 width: this.canvas_width
43381 style: 'display: none'
43389 initEvents: function()
43391 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43393 var canvas = this.canvasEl();
43395 // mouse && touch event swapping...
43396 canvas.dom.style.touchAction = 'none';
43397 canvas.dom.style.msTouchAction = 'none';
43399 this.mouse_btn_down = false;
43400 canvas.on('mousedown', this._handleMouseDown, this);
43401 canvas.on('mousemove', this._handleMouseMove, this);
43402 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43404 if (window.PointerEvent) {
43405 canvas.on('pointerdown', this._handleMouseDown, this);
43406 canvas.on('pointermove', this._handleMouseMove, this);
43407 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43410 if ('ontouchstart' in window) {
43411 canvas.on('touchstart', this._handleTouchStart, this);
43412 canvas.on('touchmove', this._handleTouchMove, this);
43413 canvas.on('touchend', this._handleTouchEnd, this);
43416 Roo.EventManager.onWindowResize(this.resize, this, true);
43418 // file input event
43419 this.fileEl().on('change', this.uploadImage, this);
43426 resize: function(){
43428 var canvas = this.canvasEl().dom;
43429 var ctx = this.canvasElCtx();
43430 var img_data = false;
43432 if(canvas.width > 0) {
43433 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43435 // setting canvas width will clean img data
43438 var style = window.getComputedStyle ?
43439 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43441 var padding_left = parseInt(style.paddingLeft) || 0;
43442 var padding_right = parseInt(style.paddingRight) || 0;
43444 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43447 ctx.putImageData(img_data, 0, 0);
43451 _handleMouseDown: function(e)
43453 if (e.browserEvent.which === 1) {
43454 this.mouse_btn_down = true;
43455 this.strokeBegin(e);
43459 _handleMouseMove: function (e)
43461 if (this.mouse_btn_down) {
43462 this.strokeMoveUpdate(e);
43466 _handleMouseUp: function (e)
43468 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43469 this.mouse_btn_down = false;
43474 _handleTouchStart: function (e) {
43476 e.preventDefault();
43477 if (e.browserEvent.targetTouches.length === 1) {
43478 // var touch = e.browserEvent.changedTouches[0];
43479 // this.strokeBegin(touch);
43481 this.strokeBegin(e); // assume e catching the correct xy...
43485 _handleTouchMove: function (e) {
43486 e.preventDefault();
43487 // var touch = event.targetTouches[0];
43488 // _this._strokeMoveUpdate(touch);
43489 this.strokeMoveUpdate(e);
43492 _handleTouchEnd: function (e) {
43493 var wasCanvasTouched = e.target === this.canvasEl().dom;
43494 if (wasCanvasTouched) {
43495 e.preventDefault();
43496 // var touch = event.changedTouches[0];
43497 // _this._strokeEnd(touch);
43502 reset: function () {
43503 this._lastPoints = [];
43504 this._lastVelocity = 0;
43505 this._lastWidth = (this.min_width + this.max_width) / 2;
43506 this.canvasElCtx().fillStyle = this.dot_color;
43509 strokeMoveUpdate: function(e)
43511 this.strokeUpdate(e);
43513 if (this.throttle) {
43514 this.throttleStroke(this.strokeUpdate, this.throttle);
43517 this.strokeUpdate(e);
43521 strokeBegin: function(e)
43523 var newPointGroup = {
43524 color: this.dot_color,
43528 if (typeof this.onBegin === 'function') {
43532 this.curve_data.push(newPointGroup);
43534 this.strokeUpdate(e);
43537 strokeUpdate: function(e)
43539 var rect = this.canvasEl().dom.getBoundingClientRect();
43540 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43541 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43542 var lastPoints = lastPointGroup.points;
43543 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43544 var isLastPointTooClose = lastPoint
43545 ? point.distanceTo(lastPoint) <= this.min_distance
43547 var color = lastPointGroup.color;
43548 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43549 var curve = this.addPoint(point);
43551 this.drawDot({color: color, point: point});
43554 this.drawCurve({color: color, curve: curve});
43564 strokeEnd: function(e)
43566 this.strokeUpdate(e);
43567 if (typeof this.onEnd === 'function') {
43572 addPoint: function (point) {
43573 var _lastPoints = this._lastPoints;
43574 _lastPoints.push(point);
43575 if (_lastPoints.length > 2) {
43576 if (_lastPoints.length === 3) {
43577 _lastPoints.unshift(_lastPoints[0]);
43579 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43580 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43581 _lastPoints.shift();
43587 calculateCurveWidths: function (startPoint, endPoint) {
43588 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43589 (1 - this.velocity_filter_weight) * this._lastVelocity;
43591 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43594 start: this._lastWidth
43597 this._lastVelocity = velocity;
43598 this._lastWidth = newWidth;
43602 drawDot: function (_a) {
43603 var color = _a.color, point = _a.point;
43604 var ctx = this.canvasElCtx();
43605 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43607 this.drawCurveSegment(point.x, point.y, width);
43609 ctx.fillStyle = color;
43613 drawCurve: function (_a) {
43614 var color = _a.color, curve = _a.curve;
43615 var ctx = this.canvasElCtx();
43616 var widthDelta = curve.endWidth - curve.startWidth;
43617 var drawSteps = Math.floor(curve.length()) * 2;
43619 ctx.fillStyle = color;
43620 for (var i = 0; i < drawSteps; i += 1) {
43621 var t = i / drawSteps;
43627 var x = uuu * curve.startPoint.x;
43628 x += 3 * uu * t * curve.control1.x;
43629 x += 3 * u * tt * curve.control2.x;
43630 x += ttt * curve.endPoint.x;
43631 var y = uuu * curve.startPoint.y;
43632 y += 3 * uu * t * curve.control1.y;
43633 y += 3 * u * tt * curve.control2.y;
43634 y += ttt * curve.endPoint.y;
43635 var width = curve.startWidth + ttt * widthDelta;
43636 this.drawCurveSegment(x, y, width);
43642 drawCurveSegment: function (x, y, width) {
43643 var ctx = this.canvasElCtx();
43645 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43646 this.is_empty = false;
43651 var ctx = this.canvasElCtx();
43652 var canvas = this.canvasEl().dom;
43653 ctx.fillStyle = this.bg_color;
43654 ctx.clearRect(0, 0, canvas.width, canvas.height);
43655 ctx.fillRect(0, 0, canvas.width, canvas.height);
43656 this.curve_data = [];
43658 this.is_empty = true;
43663 return this.el.select('input',true).first();
43666 canvasEl: function()
43668 return this.el.select('canvas',true).first();
43671 canvasElCtx: function()
43673 return this.el.select('canvas',true).first().dom.getContext('2d');
43676 getImage: function(type)
43678 if(this.is_empty) {
43683 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43686 drawFromImage: function(img_src)
43688 var img = new Image();
43690 img.onload = function(){
43691 this.canvasElCtx().drawImage(img, 0, 0);
43696 this.is_empty = false;
43699 selectImage: function()
43701 this.fileEl().dom.click();
43704 uploadImage: function(e)
43706 var reader = new FileReader();
43708 reader.onload = function(e){
43709 var img = new Image();
43710 img.onload = function(){
43712 this.canvasElCtx().drawImage(img, 0, 0);
43714 img.src = e.target.result;
43717 reader.readAsDataURL(e.target.files[0]);
43720 // Bezier Point Constructor
43721 Point: (function () {
43722 function Point(x, y, time) {
43725 this.time = time || Date.now();
43727 Point.prototype.distanceTo = function (start) {
43728 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43730 Point.prototype.equals = function (other) {
43731 return this.x === other.x && this.y === other.y && this.time === other.time;
43733 Point.prototype.velocityFrom = function (start) {
43734 return this.time !== start.time
43735 ? this.distanceTo(start) / (this.time - start.time)
43742 // Bezier Constructor
43743 Bezier: (function () {
43744 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43745 this.startPoint = startPoint;
43746 this.control2 = control2;
43747 this.control1 = control1;
43748 this.endPoint = endPoint;
43749 this.startWidth = startWidth;
43750 this.endWidth = endWidth;
43752 Bezier.fromPoints = function (points, widths, scope) {
43753 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43754 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43755 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43757 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43758 var dx1 = s1.x - s2.x;
43759 var dy1 = s1.y - s2.y;
43760 var dx2 = s2.x - s3.x;
43761 var dy2 = s2.y - s3.y;
43762 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43763 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43764 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43765 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43766 var dxm = m1.x - m2.x;
43767 var dym = m1.y - m2.y;
43768 var k = l2 / (l1 + l2);
43769 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43770 var tx = s2.x - cm.x;
43771 var ty = s2.y - cm.y;
43773 c1: new scope.Point(m1.x + tx, m1.y + ty),
43774 c2: new scope.Point(m2.x + tx, m2.y + ty)
43777 Bezier.prototype.length = function () {
43782 for (var i = 0; i <= steps; i += 1) {
43784 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43785 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43787 var xdiff = cx - px;
43788 var ydiff = cy - py;
43789 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43796 Bezier.prototype.point = function (t, start, c1, c2, end) {
43797 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43798 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43799 + (3.0 * c2 * (1.0 - t) * t * t)
43800 + (end * t * t * t);
43805 throttleStroke: function(fn, wait) {
43806 if (wait === void 0) { wait = 250; }
43808 var timeout = null;
43812 var later = function () {
43813 previous = Date.now();
43815 result = fn.apply(storedContext, storedArgs);
43817 storedContext = null;
43821 return function wrapper() {
43823 for (var _i = 0; _i < arguments.length; _i++) {
43824 args[_i] = arguments[_i];
43826 var now = Date.now();
43827 var remaining = wait - (now - previous);
43828 storedContext = this;
43830 if (remaining <= 0 || remaining > wait) {
43832 clearTimeout(timeout);
43836 result = fn.apply(storedContext, storedArgs);
43838 storedContext = null;
43842 else if (!timeout) {
43843 timeout = window.setTimeout(later, remaining);