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)
100 * @cfg {String} offset
101 * The number of pixels to offset the shadow from the element (defaults to 4)
109 * Displays the shadow under the target element
110 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
112 show : function(target){
113 target = Roo.get(target);
115 this.el = Roo.Shadow.Pool.pull();
116 if(this.el.dom.nextSibling != target.dom){
117 this.el.insertBefore(target);
120 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
122 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
125 target.getLeft(true),
130 this.el.dom.style.display = "block";
134 * Returns true if the shadow is visible, else false
136 isVisible : function(){
137 return this.el ? true : false;
141 * Direct alignment when values are already available. Show must be called at least once before
142 * calling this method to ensure it is initialized.
143 * @param {Number} left The target element left position
144 * @param {Number} top The target element top position
145 * @param {Number} width The target element width
146 * @param {Number} height The target element height
148 realign : function(l, t, w, h){
152 var a = this.adjusts, d = this.el.dom, s = d.style;
154 s.left = (l+a.l)+"px";
155 s.top = (t+a.t)+"px";
156 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
158 if(s.width != sws || s.height != shs){
162 var cn = d.childNodes;
163 var sww = Math.max(0, (sw-12))+"px";
164 cn[0].childNodes[1].style.width = sww;
165 cn[1].childNodes[1].style.width = sww;
166 cn[2].childNodes[1].style.width = sww;
167 cn[1].style.height = Math.max(0, (sh-12))+"px";
177 this.el.dom.style.display = "none";
178 Roo.Shadow.Pool.push(this.el);
184 * Adjust the z-index of this shadow
185 * @param {Number} zindex The new z-index
187 setZIndex : function(z){
190 this.el.setStyle("z-index", z);
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
198 var markup = Roo.isIE ?
199 '<div class="x-ie-shadow"></div>' :
200 '<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>';
205 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206 sh.autoBoxAdjust = false;
218 * base class for bootstrap elements.
222 Roo.bootstrap = Roo.bootstrap || {};
224 * @class Roo.bootstrap.Component
225 * @extends Roo.Component
226 * Bootstrap Component base class
227 * @cfg {String} cls css class
228 * @cfg {String} style any extra css
229 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
230 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
231 * @cfg {string} dataId cutomer id
232 * @cfg {string} name Specifies name attribute
233 * @cfg {string} tooltip Text for the tooltip
234 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
235 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
238 * Do not use directly - it does not do anything..
239 * @param {Object} config The config object
244 Roo.bootstrap.Component = function(config){
245 Roo.bootstrap.Component.superclass.constructor.call(this, config);
249 * @event childrenrendered
250 * Fires when the children have been rendered..
251 * @param {Roo.bootstrap.Component} this
253 "childrenrendered" : true
262 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
265 allowDomMove : false, // to stop relocations in parent onRender...
275 * Initialize Events for the element
277 initEvents : function() { },
283 can_build_overlaid : true,
285 container_method : false,
292 // returns the parent component..
293 return Roo.ComponentMgr.get(this.parentId)
299 onRender : function(ct, position)
301 // Roo.log("Call onRender: " + this.xtype);
303 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
306 if (this.el.attr('xtype')) {
307 this.el.attr('xtypex', this.el.attr('xtype'));
308 this.el.dom.removeAttribute('xtype');
318 var cfg = Roo.apply({}, this.getAutoCreate());
320 cfg.id = this.id || Roo.id();
322 // fill in the extra attributes
323 if (this.xattr && typeof(this.xattr) =='object') {
324 for (var i in this.xattr) {
325 cfg[i] = this.xattr[i];
330 cfg.dataId = this.dataId;
334 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
337 if (this.style) { // fixme needs to support more complex style data.
338 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
342 cfg.name = this.name;
345 this.el = ct.createChild(cfg, position);
348 this.tooltipEl().attr('tooltip', this.tooltip);
351 if(this.tabIndex !== undefined){
352 this.el.dom.setAttribute('tabIndex', this.tabIndex);
359 * Fetch the element to add children to
360 * @return {Roo.Element} defaults to this.el
362 getChildContainer : function()
366 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
368 return Roo.get(document.body);
372 * Fetch the element to display the tooltip on.
373 * @return {Roo.Element} defaults to this.el
375 tooltipEl : function()
380 addxtype : function(tree,cntr)
384 cn = Roo.factory(tree);
385 //Roo.log(['addxtype', cn]);
387 cn.parentType = this.xtype; //??
388 cn.parentId = this.id;
390 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
391 if (typeof(cn.container_method) == 'string') {
392 cntr = cn.container_method;
396 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
398 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
400 var build_from_html = Roo.XComponent.build_from_html;
402 var is_body = (tree.xtype == 'Body') ;
404 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
406 var self_cntr_el = Roo.get(this[cntr](false));
408 // do not try and build conditional elements
409 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
413 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
414 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
415 return this.addxtypeChild(tree,cntr, is_body);
418 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
421 return this.addxtypeChild(Roo.apply({}, tree),cntr);
424 Roo.log('skipping render');
430 if (!build_from_html) {
434 // this i think handles overlaying multiple children of the same type
435 // with the sam eelement.. - which might be buggy..
437 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
443 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
447 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
454 addxtypeChild : function (tree, cntr, is_body)
456 Roo.debug && Roo.log('addxtypeChild:' + cntr);
458 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
461 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
462 (typeof(tree['flexy:foreach']) != 'undefined');
466 skip_children = false;
467 // render the element if it's not BODY.
470 // if parent was disabled, then do not try and create the children..
471 if(!this[cntr](true)){
476 cn = Roo.factory(tree);
478 cn.parentType = this.xtype; //??
479 cn.parentId = this.id;
481 var build_from_html = Roo.XComponent.build_from_html;
484 // does the container contain child eleemnts with 'xtype' attributes.
485 // that match this xtype..
486 // note - when we render we create these as well..
487 // so we should check to see if body has xtype set.
488 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
490 var self_cntr_el = Roo.get(this[cntr](false));
491 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
493 //Roo.log(Roo.XComponent.build_from_html);
494 //Roo.log("got echild:");
497 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
498 // and are not displayed -this causes this to use up the wrong element when matching.
499 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
502 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
503 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
509 //echild.dom.removeAttribute('xtype');
511 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
512 Roo.debug && Roo.log(self_cntr_el);
513 Roo.debug && Roo.log(echild);
514 Roo.debug && Roo.log(cn);
520 // if object has flexy:if - then it may or may not be rendered.
521 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
522 // skip a flexy if element.
523 Roo.debug && Roo.log('skipping render');
524 Roo.debug && Roo.log(tree);
526 Roo.debug && Roo.log('skipping all children');
527 skip_children = true;
532 // actually if flexy:foreach is found, we really want to create
533 // multiple copies here...
535 //Roo.log(this[cntr]());
536 // some elements do not have render methods.. like the layouts...
538 if(this[cntr](true) === false){
543 cn.render && cn.render(this[cntr](true));
546 // then add the element..
553 if (typeof (tree.menu) != 'undefined') {
554 tree.menu.parentType = cn.xtype;
555 tree.menu.triggerEl = cn.el;
556 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
560 if (!tree.items || !tree.items.length) {
562 //Roo.log(["no children", this]);
567 var items = tree.items;
570 //Roo.log(items.length);
572 if (!skip_children) {
573 for(var i =0;i < items.length;i++) {
574 // Roo.log(['add child', items[i]]);
575 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
581 //Roo.log("fire childrenrendered");
583 cn.fireEvent('childrenrendered', this);
589 * Set the element that will be used to show or hide
591 setVisibilityEl : function(el)
593 this.visibilityEl = el;
597 * Get the element that will be used to show or hide
599 getVisibilityEl : function()
601 if (typeof(this.visibilityEl) == 'object') {
602 return this.visibilityEl;
605 if (typeof(this.visibilityEl) == 'string') {
606 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
613 * Show a component - removes 'hidden' class
617 if(!this.getVisibilityEl()){
621 this.getVisibilityEl().removeClass(['hidden','d-none']);
623 this.fireEvent('show', this);
628 * Hide a component - adds 'hidden' class
632 if(!this.getVisibilityEl()){
636 this.getVisibilityEl().addClass(['hidden','d-none']);
638 this.fireEvent('hide', this);
651 * @class Roo.bootstrap.Element
652 * @extends Roo.bootstrap.Component
653 * Bootstrap Element class
654 * @cfg {String} html contents of the element
655 * @cfg {String} tag tag of the element
656 * @cfg {String} cls class of the element
657 * @cfg {Boolean} preventDefault (true|false) default false
658 * @cfg {Boolean} clickable (true|false) default false
659 * @cfg {String} role default blank - set to button to force cursor pointer
663 * Create a new Element
664 * @param {Object} config The config object
667 Roo.bootstrap.Element = function(config){
668 Roo.bootstrap.Element.superclass.constructor.call(this, config);
674 * When a element is chick
675 * @param {Roo.bootstrap.Element} this
676 * @param {Roo.EventObject} e
684 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
689 preventDefault: false,
694 getAutoCreate : function(){
698 // cls: this.cls, double assign in parent class Component.js :: onRender
701 if (this.role !== false) {
702 cfg.role = this.role;
708 initEvents: function()
710 Roo.bootstrap.Element.superclass.initEvents.call(this);
713 this.el.on('click', this.onClick, this);
719 onClick : function(e)
721 if(this.preventDefault){
725 this.fireEvent('click', this, e); // why was this double click before?
733 getValue : function()
735 return this.el.dom.innerHTML;
738 setValue : function(value)
740 this.el.dom.innerHTML = value;
755 * @class Roo.bootstrap.DropTarget
756 * @extends Roo.bootstrap.Element
757 * Bootstrap DropTarget class
759 * @cfg {string} name dropable name
762 * Create a new Dropable Area
763 * @param {Object} config The config object
766 Roo.bootstrap.DropTarget = function(config){
767 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
773 * When a element is chick
774 * @param {Roo.bootstrap.Element} this
775 * @param {Roo.EventObject} e
781 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
784 getAutoCreate : function(){
789 initEvents: function()
791 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
792 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
795 drop : this.dragDrop.createDelegate(this),
796 enter : this.dragEnter.createDelegate(this),
797 out : this.dragOut.createDelegate(this),
798 over : this.dragOver.createDelegate(this)
802 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
805 dragDrop : function(source,e,data)
807 // user has to decide how to impliment this.
810 //this.fireEvent('drop', this, source, e ,data);
814 dragEnter : function(n, dd, e, data)
816 // probably want to resize the element to match the dropped element..
818 this.originalSize = this.el.getSize();
819 this.el.setSize( n.el.getSize());
820 this.dropZone.DDM.refreshCache(this.name);
821 Roo.log([n, dd, e, data]);
824 dragOut : function(value)
826 // resize back to normal
828 this.el.setSize(this.originalSize);
829 this.dropZone.resetConstraints();
832 dragOver : function()
849 * @class Roo.bootstrap.Body
850 * @extends Roo.bootstrap.Component
851 * Bootstrap Body class
855 * @param {Object} config The config object
858 Roo.bootstrap.Body = function(config){
860 config = config || {};
862 Roo.bootstrap.Body.superclass.constructor.call(this, config);
863 this.el = Roo.get(config.el ? config.el : document.body );
864 if (this.cls && this.cls.length) {
865 Roo.get(document.body).addClass(this.cls);
869 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
871 is_body : true,// just to make sure it's constructed?
876 onRender : function(ct, position)
878 /* Roo.log("Roo.bootstrap.Body - onRender");
879 if (this.cls && this.cls.length) {
880 Roo.get(document.body).addClass(this.cls);
899 * @class Roo.bootstrap.ButtonGroup
900 * @extends Roo.bootstrap.Component
901 * Bootstrap ButtonGroup class
902 * @cfg {String} size lg | sm | xs (default empty normal)
903 * @cfg {String} align vertical | justified (default none)
904 * @cfg {String} direction up | down (default down)
905 * @cfg {Boolean} toolbar false | true
906 * @cfg {Boolean} btn true | false
911 * @param {Object} config The config object
914 Roo.bootstrap.ButtonGroup = function(config){
915 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
918 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
926 getAutoCreate : function(){
932 cfg.html = this.html || cfg.html;
943 if (['vertical','justified'].indexOf(this.align)!==-1) {
944 cfg.cls = 'btn-group-' + this.align;
946 if (this.align == 'justified') {
947 console.log(this.items);
951 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
952 cfg.cls += ' btn-group-' + this.size;
955 if (this.direction == 'up') {
956 cfg.cls += ' dropup' ;
962 * Add a button to the group (similar to NavItem API.)
964 addItem : function(cfg)
966 var cn = new Roo.bootstrap.Button(cfg);
968 cn.parentId = this.id;
969 cn.onRender(this.el, null);
983 * @class Roo.bootstrap.Button
984 * @extends Roo.bootstrap.Component
985 * Bootstrap Button class
986 * @cfg {String} html The button content
987 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
988 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
989 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
990 * @cfg {String} size (lg|sm|xs)
991 * @cfg {String} tag (a|input|submit)
992 * @cfg {String} href empty or href
993 * @cfg {Boolean} disabled default false;
994 * @cfg {Boolean} isClose default false;
995 * @cfg {String} glyphicon depricated - use fa
996 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
997 * @cfg {String} badge text for badge
998 * @cfg {String} theme (default|glow)
999 * @cfg {Boolean} inverse dark themed version
1000 * @cfg {Boolean} toggle is it a slidy toggle button
1001 * @cfg {Boolean} pressed default null - if the button ahs active state
1002 * @cfg {String} ontext text for on slidy toggle state
1003 * @cfg {String} offtext text for off slidy toggle state
1004 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1005 * @cfg {Boolean} removeClass remove the standard class..
1006 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1007 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1010 * Create a new button
1011 * @param {Object} config The config object
1015 Roo.bootstrap.Button = function(config){
1016 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1022 * When a button is pressed
1023 * @param {Roo.bootstrap.Button} btn
1024 * @param {Roo.EventObject} e
1029 * When a button is double clicked
1030 * @param {Roo.bootstrap.Button} btn
1031 * @param {Roo.EventObject} e
1036 * After the button has been toggles
1037 * @param {Roo.bootstrap.Button} btn
1038 * @param {Roo.EventObject} e
1039 * @param {boolean} pressed (also available as button.pressed)
1045 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1066 preventDefault: true,
1075 getAutoCreate : function(){
1083 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1084 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1085 this.tag = 'button';
1089 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1091 if (this.toggle == true) {
1094 cls: 'slider-frame roo-button',
1098 'data-on-text':'ON',
1099 'data-off-text':'OFF',
1100 cls: 'slider-button',
1105 // why are we validating the weights?
1106 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1107 cfg.cls += ' ' + this.weight;
1114 cfg.cls += ' close';
1116 cfg["aria-hidden"] = true;
1118 cfg.html = "×";
1124 if (this.theme==='default') {
1125 cfg.cls = 'btn roo-button';
1127 //if (this.parentType != 'Navbar') {
1128 this.weight = this.weight.length ? this.weight : 'default';
1130 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1132 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1133 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1134 cfg.cls += ' btn-' + outline + weight;
1135 if (this.weight == 'default') {
1137 cfg.cls += ' btn-' + this.weight;
1140 } else if (this.theme==='glow') {
1143 cfg.cls = 'btn-glow roo-button';
1145 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1147 cfg.cls += ' ' + this.weight;
1153 this.cls += ' inverse';
1157 if (this.active || this.pressed === true) {
1158 cfg.cls += ' active';
1161 if (this.disabled) {
1162 cfg.disabled = 'disabled';
1166 Roo.log('changing to ul' );
1168 this.glyphicon = 'caret';
1169 if (Roo.bootstrap.version == 4) {
1170 this.fa = 'caret-down';
1175 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1177 //gsRoo.log(this.parentType);
1178 if (this.parentType === 'Navbar' && !this.parent().bar) {
1179 Roo.log('changing to li?');
1188 href : this.href || '#'
1191 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1192 cfg.cls += ' dropdown';
1199 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1201 if (this.glyphicon) {
1202 cfg.html = ' ' + cfg.html;
1207 cls: 'glyphicon glyphicon-' + this.glyphicon
1212 cfg.html = ' ' + cfg.html;
1217 cls: 'fa fas fa-' + this.fa
1227 // cfg.cls='btn roo-button';
1231 var value = cfg.html;
1236 cls: 'glyphicon glyphicon-' + this.glyphicon,
1243 cls: 'fa fas fa-' + this.fa,
1248 var bw = this.badge_weight.length ? this.badge_weight :
1249 (this.weight.length ? this.weight : 'secondary');
1250 bw = bw == 'default' ? 'secondary' : bw;
1256 cls: 'badge badge-' + bw,
1265 cfg.cls += ' dropdown';
1266 cfg.html = typeof(cfg.html) != 'undefined' ?
1267 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1270 if (cfg.tag !== 'a' && this.href !== '') {
1271 throw "Tag must be a to set href.";
1272 } else if (this.href.length > 0) {
1273 cfg.href = this.href;
1276 if(this.removeClass){
1281 cfg.target = this.target;
1286 initEvents: function() {
1287 // Roo.log('init events?');
1288 // Roo.log(this.el.dom);
1291 if (typeof (this.menu) != 'undefined') {
1292 this.menu.parentType = this.xtype;
1293 this.menu.triggerEl = this.el;
1294 this.addxtype(Roo.apply({}, this.menu));
1298 if (this.el.hasClass('roo-button')) {
1299 this.el.on('click', this.onClick, this);
1300 this.el.on('dblclick', this.onDblClick, this);
1302 this.el.select('.roo-button').on('click', this.onClick, this);
1303 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1307 if(this.removeClass){
1308 this.el.on('click', this.onClick, this);
1311 if (this.group === true) {
1312 if (this.pressed === false || this.pressed === true) {
1315 this.pressed = false;
1316 this.setActive(this.pressed);
1321 this.el.enableDisplayMode();
1324 onClick : function(e)
1326 if (this.disabled) {
1330 Roo.log('button on click ');
1331 if(this.preventDefault){
1340 this.setActive(true);
1341 var pi = this.parent().items;
1342 for (var i = 0;i < pi.length;i++) {
1343 if (this == pi[i]) {
1346 if (pi[i].el.hasClass('roo-button')) {
1347 pi[i].setActive(false);
1350 this.fireEvent('click', this, e);
1354 if (this.pressed === true || this.pressed === false) {
1355 this.toggleActive(e);
1359 this.fireEvent('click', this, e);
1361 onDblClick: function(e)
1363 if (this.disabled) {
1366 if(this.preventDefault){
1369 this.fireEvent('dblclick', this, e);
1372 * Enables this button
1376 this.disabled = false;
1377 this.el.removeClass('disabled');
1378 this.el.dom.removeAttribute("disabled");
1382 * Disable this button
1384 disable : function()
1386 this.disabled = true;
1387 this.el.addClass('disabled');
1388 this.el.attr("disabled", "disabled")
1391 * sets the active state on/off,
1392 * @param {Boolean} state (optional) Force a particular state
1394 setActive : function(v) {
1396 this.el[v ? 'addClass' : 'removeClass']('active');
1400 * toggles the current active state
1402 toggleActive : function(e)
1404 this.setActive(!this.pressed); // this modifies pressed...
1405 this.fireEvent('toggle', this, e, this.pressed);
1408 * get the current active state
1409 * @return {boolean} true if it's active
1411 isActive : function()
1413 return this.el.hasClass('active');
1416 * set the text of the first selected button
1418 setText : function(str)
1420 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1423 * get the text of the first selected button
1425 getText : function()
1427 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1430 setWeight : function(str)
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1433 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1435 var outline = this.outline ? 'outline-' : '';
1436 if (str == 'default') {
1437 this.el.addClass('btn-default btn-outline-secondary');
1440 this.el.addClass('btn-' + outline + str);
1445 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1447 Roo.bootstrap.Button.weights = [
1467 * @class Roo.bootstrap.Column
1468 * @extends Roo.bootstrap.Component
1469 * Bootstrap Column class
1470 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1471 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1472 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1473 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1474 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1475 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1476 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1477 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1480 * @cfg {Boolean} hidden (true|false) hide the element
1481 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1482 * @cfg {String} fa (ban|check|...) font awesome icon
1483 * @cfg {Number} fasize (1|2|....) font awsome size
1485 * @cfg {String} icon (info-sign|check|...) glyphicon name
1487 * @cfg {String} html content of column.
1490 * Create a new Column
1491 * @param {Object} config The config object
1494 Roo.bootstrap.Column = function(config){
1495 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1498 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1516 getAutoCreate : function(){
1517 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1525 var sizes = ['xs','sm','md','lg'];
1526 sizes.map(function(size ,ix){
1527 //Roo.log( size + ':' + settings[size]);
1529 if (settings[size+'off'] !== false) {
1530 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1533 if (settings[size] === false) {
1537 if (!settings[size]) { // 0 = hidden
1538 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1540 for (var i = ix; i > -1; i--) {
1541 cfg.cls += ' d-' + sizes[i] + '-none';
1547 cfg.cls += ' col-' + size + '-' + settings[size] + (
1548 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1554 cfg.cls += ' hidden';
1557 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1558 cfg.cls +=' alert alert-' + this.alert;
1562 if (this.html.length) {
1563 cfg.html = this.html;
1567 if (this.fasize > 1) {
1568 fasize = ' fa-' + this.fasize + 'x';
1570 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1575 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1594 * @class Roo.bootstrap.Container
1595 * @extends Roo.bootstrap.Component
1596 * Bootstrap Container class
1597 * @cfg {Boolean} jumbotron is it a jumbotron element
1598 * @cfg {String} html content of element
1599 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1600 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1601 * @cfg {String} header content of header (for panel)
1602 * @cfg {String} footer content of footer (for panel)
1603 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1604 * @cfg {String} tag (header|aside|section) type of HTML tag.
1605 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1606 * @cfg {String} fa font awesome icon
1607 * @cfg {String} icon (info-sign|check|...) glyphicon name
1608 * @cfg {Boolean} hidden (true|false) hide the element
1609 * @cfg {Boolean} expandable (true|false) default false
1610 * @cfg {Boolean} expanded (true|false) default true
1611 * @cfg {String} rheader contet on the right of header
1612 * @cfg {Boolean} clickable (true|false) default false
1616 * Create a new Container
1617 * @param {Object} config The config object
1620 Roo.bootstrap.Container = function(config){
1621 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1627 * After the panel has been expand
1629 * @param {Roo.bootstrap.Container} this
1634 * After the panel has been collapsed
1636 * @param {Roo.bootstrap.Container} this
1641 * When a element is chick
1642 * @param {Roo.bootstrap.Container} this
1643 * @param {Roo.EventObject} e
1649 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1667 getChildContainer : function() {
1673 if (this.panel.length) {
1674 return this.el.select('.panel-body',true).first();
1681 getAutoCreate : function(){
1684 tag : this.tag || 'div',
1688 if (this.jumbotron) {
1689 cfg.cls = 'jumbotron';
1694 // - this is applied by the parent..
1696 // cfg.cls = this.cls + '';
1699 if (this.sticky.length) {
1701 var bd = Roo.get(document.body);
1702 if (!bd.hasClass('bootstrap-sticky')) {
1703 bd.addClass('bootstrap-sticky');
1704 Roo.select('html',true).setStyle('height', '100%');
1707 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1711 if (this.well.length) {
1712 switch (this.well) {
1715 cfg.cls +=' well well-' +this.well;
1724 cfg.cls += ' hidden';
1728 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1729 cfg.cls +=' alert alert-' + this.alert;
1734 if (this.panel.length) {
1735 cfg.cls += ' panel panel-' + this.panel;
1737 if (this.header.length) {
1741 if(this.expandable){
1743 cfg.cls = cfg.cls + ' expandable';
1747 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1755 cls : 'panel-title',
1756 html : (this.expandable ? ' ' : '') + this.header
1760 cls: 'panel-header-right',
1766 cls : 'panel-heading',
1767 style : this.expandable ? 'cursor: pointer' : '',
1775 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1780 if (this.footer.length) {
1782 cls : 'panel-footer',
1791 body.html = this.html || cfg.html;
1792 // prefix with the icons..
1794 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1797 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1802 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1803 cfg.cls = 'container';
1809 initEvents: function()
1811 if(this.expandable){
1812 var headerEl = this.headerEl();
1815 headerEl.on('click', this.onToggleClick, this);
1820 this.el.on('click', this.onClick, this);
1825 onToggleClick : function()
1827 var headerEl = this.headerEl();
1843 if(this.fireEvent('expand', this)) {
1845 this.expanded = true;
1847 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1849 this.el.select('.panel-body',true).first().removeClass('hide');
1851 var toggleEl = this.toggleEl();
1857 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1862 collapse : function()
1864 if(this.fireEvent('collapse', this)) {
1866 this.expanded = false;
1868 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1869 this.el.select('.panel-body',true).first().addClass('hide');
1871 var toggleEl = this.toggleEl();
1877 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1881 toggleEl : function()
1883 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1887 return this.el.select('.panel-heading .fa',true).first();
1890 headerEl : function()
1892 if(!this.el || !this.panel.length || !this.header.length){
1896 return this.el.select('.panel-heading',true).first()
1901 if(!this.el || !this.panel.length){
1905 return this.el.select('.panel-body',true).first()
1908 titleEl : function()
1910 if(!this.el || !this.panel.length || !this.header.length){
1914 return this.el.select('.panel-title',true).first();
1917 setTitle : function(v)
1919 var titleEl = this.titleEl();
1925 titleEl.dom.innerHTML = v;
1928 getTitle : function()
1931 var titleEl = this.titleEl();
1937 return titleEl.dom.innerHTML;
1940 setRightTitle : function(v)
1942 var t = this.el.select('.panel-header-right',true).first();
1948 t.dom.innerHTML = v;
1951 onClick : function(e)
1955 this.fireEvent('click', this, e);
1962 * This is BS4's Card element.. - similar to our containers probably..
1966 * @class Roo.bootstrap.Card
1967 * @extends Roo.bootstrap.Component
1968 * Bootstrap Card class
1971 * possible... may not be implemented..
1972 * @cfg {String} header_image src url of image.
1973 * @cfg {String|Object} header
1974 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1975 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1977 * @cfg {String} title
1978 * @cfg {String} subtitle
1979 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1980 * @cfg {String} footer
1982 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1984 * @cfg {String} margin (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1990 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1992 * @cfg {String} padding (0|1|2|3|4|5)
1993 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1994 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1995 * @cfg {String} padding_left (0|1|2|3|4|5)
1996 * @cfg {String} padding_right (0|1|2|3|4|5)
1997 * @cfg {String} padding_x (0|1|2|3|4|5)
1998 * @cfg {String} padding_y (0|1|2|3|4|5)
2000 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2006 * @config {Boolean} dragable if this card can be dragged.
2007 * @config {String} drag_group group for drag
2008 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2009 * @config {String} drop_group group for drag
2011 * @config {Boolean} collapsable can the body be collapsed.
2012 * @config {Boolean} collapsed is the body collapsed when rendered...
2013 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2014 * @config {Boolean} rotated is the body rotated when rendered...
2017 * Create a new Container
2018 * @param {Object} config The config object
2021 Roo.bootstrap.Card = function(config){
2022 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2028 * When a element a card is dropped
2029 * @param {Roo.bootstrap.Card} this
2032 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2033 * @param {String} position 'above' or 'below'
2034 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2040 * When a element a card is rotate
2041 * @param {Roo.bootstrap.Card} this
2042 * @param {Roo.Element} n the node being dropped?
2043 * @param {Boolean} rotate status
2048 * When a card element is dragged over ready to drop (return false to block dropable)
2049 * @param {Roo.bootstrap.Card} this
2050 * @param {Object} data from dragdrop
2058 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2063 margin: '', /// may be better in component?
2093 collapsable : false,
2102 childContainer : false,
2103 dropEl : false, /// the dom placeholde element that indicates drop location.
2104 containerEl: false, // body container
2105 bodyEl: false, // card-body
2106 headerContainerEl : false, //
2108 header_imageEl : false,
2111 layoutCls : function()
2115 Roo.log(this.margin_bottom.length);
2116 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2117 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2119 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2120 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2122 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2123 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2127 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2128 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2129 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2133 // more generic support?
2141 // Roo.log("Call onRender: " + this.xtype);
2142 /* We are looking at something like this.
2144 <img src="..." class="card-img-top" alt="...">
2145 <div class="card-body">
2146 <h5 class="card-title">Card title</h5>
2147 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2149 >> this bit is really the body...
2150 <div> << we will ad dthis in hopefully it will not break shit.
2152 ** card text does not actually have any styling...
2154 <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>
2157 <a href="#" class="card-link">Card link</a>
2160 <div class="card-footer">
2161 <small class="text-muted">Last updated 3 mins ago</small>
2165 getAutoCreate : function(){
2173 if (this.weight.length && this.weight != 'light') {
2174 cfg.cls += ' text-white';
2176 cfg.cls += ' text-dark'; // need as it's nested..
2178 if (this.weight.length) {
2179 cfg.cls += ' bg-' + this.weight;
2182 cfg.cls += ' ' + this.layoutCls();
2185 var hdr_ctr = false;
2186 if (this.header.length) {
2188 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2189 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2197 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2203 if (this.collapsable) {
2206 cls : 'd-block user-select-none',
2210 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2215 hdr.cn.push(hdr_ctr);
2220 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2225 if (this.header_image.length) {
2228 cls : 'card-img-top',
2229 src: this.header_image // escape?
2234 cls : 'card-img-top d-none'
2240 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2244 if (this.collapsable || this.rotateable) {
2247 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2254 if (this.title.length) {
2258 src: this.title // escape?
2262 if (this.subtitle.length) {
2266 src: this.subtitle // escape?
2272 cls : 'roo-card-body-ctr'
2275 if (this.html.length) {
2281 // fixme ? handle objects?
2283 if (this.footer.length) {
2286 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2291 cfg.cn.push({cls : 'card-footer d-none'});
2300 getCardHeader : function()
2302 var ret = this.el.select('.card-header',true).first();
2303 if (ret.hasClass('d-none')) {
2304 ret.removeClass('d-none');
2309 getCardFooter : function()
2311 var ret = this.el.select('.card-footer',true).first();
2312 if (ret.hasClass('d-none')) {
2313 ret.removeClass('d-none');
2318 getCardImageTop : function()
2320 var ret = this.header_imageEl;
2321 if (ret.hasClass('d-none')) {
2322 ret.removeClass('d-none');
2328 getChildContainer : function()
2334 return this.el.select('.roo-card-body-ctr',true).first();
2337 initEvents: function()
2339 this.bodyEl = this.el.select('.card-body',true).first();
2340 this.containerEl = this.getChildContainer();
2342 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2343 containerScroll: true,
2344 ddGroup: this.drag_group || 'default_card_drag_group'
2346 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2348 if (this.dropable) {
2349 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2350 containerScroll: true,
2351 ddGroup: this.drop_group || 'default_card_drag_group'
2353 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2354 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2355 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2356 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2357 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2360 if (this.collapsable) {
2361 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2363 if (this.rotateable) {
2364 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2366 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2368 this.footerEl = this.el.select('.card-footer',true).first();
2369 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2370 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2371 this.headerEl = this.el.select('.card-header',true).first();
2374 this.el.addClass('roo-card-rotated');
2375 this.fireEvent('rotate', this, true);
2377 this.header_imageEl = this.el.select('.card-img-top',true).first();
2378 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2381 getDragData : function(e)
2383 var target = this.getEl();
2385 //this.handleSelection(e);
2390 nodes: this.getEl(),
2395 dragData.ddel = target.dom ; // the div element
2396 Roo.log(target.getWidth( ));
2397 dragData.ddel.style.width = target.getWidth() + 'px';
2404 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2405 * whole Element becomes the target, and this causes the drop gesture to append.
2407 * Returns an object:
2410 position : 'below' or 'above'
2411 card : relateive to card OBJECT (or true for no cards listed)
2412 items_n : relative to nth item in list
2413 card_n : relative to nth card in list
2418 getTargetFromEvent : function(e, dragged_card_el)
2420 var target = e.getTarget();
2421 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2422 target = target.parentNode;
2433 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2434 // see if target is one of the 'cards'...
2437 //Roo.log(this.items.length);
2440 var last_card_n = 0;
2442 for (var i = 0;i< this.items.length;i++) {
2444 if (!this.items[i].el.hasClass('card')) {
2447 pos = this.getDropPoint(e, this.items[i].el.dom);
2449 cards_len = ret.cards.length;
2450 //Roo.log(this.items[i].el.dom.id);
2451 ret.cards.push(this.items[i]);
2453 if (ret.card_n < 0 && pos == 'above') {
2454 ret.position = cards_len > 0 ? 'below' : pos;
2455 ret.items_n = i > 0 ? i - 1 : 0;
2456 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2457 ret.card = ret.cards[ret.card_n];
2460 if (!ret.cards.length) {
2462 ret.position = 'below';
2466 // could not find a card.. stick it at the end..
2467 if (ret.card_n < 0) {
2468 ret.card_n = last_card_n;
2469 ret.card = ret.cards[last_card_n];
2470 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2471 ret.position = 'below';
2474 if (this.items[ret.items_n].el == dragged_card_el) {
2478 if (ret.position == 'below') {
2479 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2481 if (card_after && card_after.el == dragged_card_el) {
2488 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2490 if (card_before && card_before.el == dragged_card_el) {
2497 onNodeEnter : function(n, dd, e, data){
2500 onNodeOver : function(n, dd, e, data)
2503 var target_info = this.getTargetFromEvent(e,data.source.el);
2504 if (target_info === false) {
2505 this.dropPlaceHolder('hide');
2508 Roo.log(['getTargetFromEvent', target_info ]);
2511 if (this.fireEvent('cardover', this, [ data ]) === false) {
2515 this.dropPlaceHolder('show', target_info,data);
2519 onNodeOut : function(n, dd, e, data){
2520 this.dropPlaceHolder('hide');
2523 onNodeDrop : function(n, dd, e, data)
2526 // call drop - return false if
2528 // this could actually fail - if the Network drops..
2529 // we will ignore this at present..- client should probably reload
2530 // the whole set of cards if stuff like that fails.
2533 var info = this.getTargetFromEvent(e,data.source.el);
2534 if (info === false) {
2537 this.dropPlaceHolder('hide');
2541 this.acceptCard(data.source, info.position, info.card, info.items_n);
2545 firstChildCard : function()
2547 for (var i = 0;i< this.items.length;i++) {
2549 if (!this.items[i].el.hasClass('card')) {
2552 return this.items[i];
2554 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2559 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2561 acceptCard : function(move_card, position, next_to_card )
2563 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2567 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2569 move_card.parent().removeCard(move_card);
2572 var dom = move_card.el.dom;
2573 dom.style.width = ''; // clear with - which is set by drag.
2575 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2576 var cardel = next_to_card.el.dom;
2578 if (position == 'above' ) {
2579 cardel.parentNode.insertBefore(dom, cardel);
2580 } else if (cardel.nextSibling) {
2581 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2583 cardel.parentNode.append(dom);
2586 // card container???
2587 this.containerEl.dom.append(dom);
2590 //FIXME HANDLE card = true
2592 // add this to the correct place in items.
2594 // remove Card from items.
2597 if (this.items.length) {
2599 //Roo.log([info.items_n, info.position, this.items.length]);
2600 for (var i =0; i < this.items.length; i++) {
2601 if (i == to_items_n && position == 'above') {
2602 nitems.push(move_card);
2604 nitems.push(this.items[i]);
2605 if (i == to_items_n && position == 'below') {
2606 nitems.push(move_card);
2609 this.items = nitems;
2610 Roo.log(this.items);
2612 this.items.push(move_card);
2615 move_card.parentId = this.id;
2621 removeCard : function(c)
2623 this.items = this.items.filter(function(e) { return e != c });
2626 dom.parentNode.removeChild(dom);
2627 dom.style.width = ''; // clear with - which is set by drag.
2632 /** Decide whether to drop above or below a View node. */
2633 getDropPoint : function(e, n, dd)
2638 if (n == this.containerEl.dom) {
2641 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2642 var c = t + (b - t) / 2;
2643 var y = Roo.lib.Event.getPageY(e);
2650 onToggleCollapse : function(e)
2652 if (this.collapsed) {
2653 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2654 this.collapsableEl.addClass('show');
2655 this.collapsed = false;
2658 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2659 this.collapsableEl.removeClass('show');
2660 this.collapsed = true;
2665 onToggleRotate : function(e)
2667 this.collapsableEl.removeClass('show');
2668 this.footerEl.removeClass('d-none');
2669 this.el.removeClass('roo-card-rotated');
2670 this.el.removeClass('d-none');
2673 this.collapsableEl.addClass('show');
2674 this.rotated = false;
2675 this.fireEvent('rotate', this, this.rotated);
2678 this.el.addClass('roo-card-rotated');
2679 this.footerEl.addClass('d-none');
2680 this.el.select('.roo-collapsable').removeClass('show');
2682 this.rotated = true;
2683 this.fireEvent('rotate', this, this.rotated);
2687 dropPlaceHolder: function (action, info, data)
2689 if (this.dropEl === false) {
2690 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2694 this.dropEl.removeClass(['d-none', 'd-block']);
2695 if (action == 'hide') {
2697 this.dropEl.addClass('d-none');
2700 // FIXME - info.card == true!!!
2701 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2703 if (info.card !== true) {
2704 var cardel = info.card.el.dom;
2706 if (info.position == 'above') {
2707 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2708 } else if (cardel.nextSibling) {
2709 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2711 cardel.parentNode.append(this.dropEl.dom);
2714 // card container???
2715 this.containerEl.dom.append(this.dropEl.dom);
2718 this.dropEl.addClass('d-block roo-card-dropzone');
2720 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2727 setHeaderText: function(html)
2730 if (this.headerContainerEl) {
2731 this.headerContainerEl.dom.innerHTML = html;
2734 onHeaderImageLoad : function(ev, he)
2736 if (!this.header_image_fit_square) {
2740 var hw = he.naturalHeight / he.naturalWidth;
2743 //var w = he.dom.naturalWidth;
2746 he.style.position = 'relative';
2748 var nw = (ww * (1/hw));
2749 Roo.get(he).setSize( ww * (1/hw), ww);
2750 he.style.left = ((ww - nw)/ 2) + 'px';
2751 he.style.position = 'relative';
2762 * Card header - holder for the card header elements.
2767 * @class Roo.bootstrap.CardHeader
2768 * @extends Roo.bootstrap.Element
2769 * Bootstrap CardHeader class
2771 * Create a new Card Header - that you can embed children into
2772 * @param {Object} config The config object
2775 Roo.bootstrap.CardHeader = function(config){
2776 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2779 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2782 container_method : 'getCardHeader'
2795 * Card footer - holder for the card footer elements.
2800 * @class Roo.bootstrap.CardFooter
2801 * @extends Roo.bootstrap.Element
2802 * Bootstrap CardFooter class
2804 * Create a new Card Footer - that you can embed children into
2805 * @param {Object} config The config object
2808 Roo.bootstrap.CardFooter = function(config){
2809 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2812 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2815 container_method : 'getCardFooter'
2828 * Card header - holder for the card header elements.
2833 * @class Roo.bootstrap.CardImageTop
2834 * @extends Roo.bootstrap.Element
2835 * Bootstrap CardImageTop class
2837 * Create a new Card Image Top container
2838 * @param {Object} config The config object
2841 Roo.bootstrap.CardImageTop = function(config){
2842 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2845 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2848 container_method : 'getCardImageTop'
2863 * @class Roo.bootstrap.ButtonUploader
2864 * @extends Roo.bootstrap.Button
2865 * Bootstrap Button Uploader class - it's a button which when you add files to it
2868 * @cfg {Number} errorTimeout default 3000
2869 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2870 * @cfg {Array} html The button text.
2871 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2874 * Create a new CardUploader
2875 * @param {Object} config The config object
2878 Roo.bootstrap.ButtonUploader = function(config){
2882 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2888 * @event beforeselect
2889 * When button is pressed, before show upload files dialog is shown
2890 * @param {Roo.bootstrap.UploaderButton} this
2893 'beforeselect' : true,
2895 * @event fired when files have been selected,
2896 * When a the download link is clicked
2897 * @param {Roo.bootstrap.UploaderButton} this
2898 * @param {Array} Array of files that have been uploaded
2905 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2908 errorTimeout : 3000,
2912 fileCollection : false,
2917 getAutoCreate : function()
2922 cls : 'd-none roo-card-upload-selector'
2925 if (this.multiple) {
2926 im.multiple = 'multiple';
2932 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2942 initEvents : function()
2945 Roo.bootstrap.Button.prototype.initEvents.call(this);
2951 this.urlAPI = (window.createObjectURL && window) ||
2952 (window.URL && URL.revokeObjectURL && URL) ||
2953 (window.webkitURL && webkitURL);
2958 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2960 this.selectorEl.on('change', this.onFileSelected, this);
2967 onClick : function(e)
2971 if ( this.fireEvent('beforeselect', this) === false) {
2975 this.selectorEl.dom.click();
2979 onFileSelected : function(e)
2983 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2986 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2987 this.selectorEl.dom.value = '';// hopefully reset..
2989 this.fireEvent('uploaded', this, files );
2997 * addCard - add an Attachment to the uploader
2998 * @param data - the data about the image to upload
3002 title : "Title of file",
3003 is_uploaded : false,
3004 src : "http://.....",
3005 srcfile : { the File upload object },
3006 mimetype : file.type,
3009 .. any other data...
3034 * @class Roo.bootstrap.Img
3035 * @extends Roo.bootstrap.Component
3036 * Bootstrap Img class
3037 * @cfg {Boolean} imgResponsive false | true
3038 * @cfg {String} border rounded | circle | thumbnail
3039 * @cfg {String} src image source
3040 * @cfg {String} alt image alternative text
3041 * @cfg {String} href a tag href
3042 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3043 * @cfg {String} xsUrl xs image source
3044 * @cfg {String} smUrl sm image source
3045 * @cfg {String} mdUrl md image source
3046 * @cfg {String} lgUrl lg image source
3047 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3050 * Create a new Input
3051 * @param {Object} config The config object
3054 Roo.bootstrap.Img = function(config){
3055 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3061 * The img click event for the img.
3062 * @param {Roo.EventObject} e
3067 * The when any image loads
3068 * @param {Roo.EventObject} e
3074 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3076 imgResponsive: true,
3085 backgroundContain : false,
3087 getAutoCreate : function()
3089 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3090 return this.createSingleImg();
3095 cls: 'roo-image-responsive-group',
3100 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3102 if(!_this[size + 'Url']){
3108 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3109 html: _this.html || cfg.html,
3110 src: _this[size + 'Url']
3113 img.cls += ' roo-image-responsive-' + size;
3115 var s = ['xs', 'sm', 'md', 'lg'];
3117 s.splice(s.indexOf(size), 1);
3119 Roo.each(s, function(ss){
3120 img.cls += ' hidden-' + ss;
3123 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3124 cfg.cls += ' img-' + _this.border;
3128 cfg.alt = _this.alt;
3141 a.target = _this.target;
3145 cfg.cn.push((_this.href) ? a : img);
3152 createSingleImg : function()
3156 cls: (this.imgResponsive) ? 'img-responsive' : '',
3158 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3161 if (this.backgroundContain) {
3162 cfg.cls += ' background-contain';
3165 cfg.html = this.html || cfg.html;
3167 if (this.backgroundContain) {
3168 cfg.style="background-image: url(" + this.src + ')';
3170 cfg.src = this.src || cfg.src;
3173 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3174 cfg.cls += ' img-' + this.border;
3191 a.target = this.target;
3196 return (this.href) ? a : cfg;
3199 initEvents: function()
3202 this.el.on('click', this.onClick, this);
3204 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205 this.el.on('load', this.onImageLoad, this);
3207 // not sure if this works.. not tested
3208 this.el.select('img', true).on('load', this.onImageLoad, this);
3213 onClick : function(e)
3215 Roo.log('img onclick');
3216 this.fireEvent('click', this, e);
3218 onImageLoad: function(e)
3220 Roo.log('img load');
3221 this.fireEvent('load', this, e);
3225 * Sets the url of the image - used to update it
3226 * @param {String} url the url of the image
3229 setSrc : function(url)
3233 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3234 if (this.backgroundContain) {
3235 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3237 this.el.dom.src = url;
3242 this.el.select('img', true).first().dom.src = url;
3258 * @class Roo.bootstrap.Link
3259 * @extends Roo.bootstrap.Component
3260 * Bootstrap Link Class
3261 * @cfg {String} alt image alternative text
3262 * @cfg {String} href a tag href
3263 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3264 * @cfg {String} html the content of the link.
3265 * @cfg {String} anchor name for the anchor link
3266 * @cfg {String} fa - favicon
3268 * @cfg {Boolean} preventDefault (true | false) default false
3272 * Create a new Input
3273 * @param {Object} config The config object
3276 Roo.bootstrap.Link = function(config){
3277 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3283 * The img click event for the img.
3284 * @param {Roo.EventObject} e
3290 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3294 preventDefault: false,
3300 getAutoCreate : function()
3302 var html = this.html || '';
3304 if (this.fa !== false) {
3305 html = '<i class="fa fa-' + this.fa + '"></i>';
3310 // anchor's do not require html/href...
3311 if (this.anchor === false) {
3313 cfg.href = this.href || '#';
3315 cfg.name = this.anchor;
3316 if (this.html !== false || this.fa !== false) {
3319 if (this.href !== false) {
3320 cfg.href = this.href;
3324 if(this.alt !== false){
3329 if(this.target !== false) {
3330 cfg.target = this.target;
3336 initEvents: function() {
3338 if(!this.href || this.preventDefault){
3339 this.el.on('click', this.onClick, this);
3343 onClick : function(e)
3345 if(this.preventDefault){
3348 //Roo.log('img onclick');
3349 this.fireEvent('click', this, e);
3362 * @class Roo.bootstrap.Header
3363 * @extends Roo.bootstrap.Component
3364 * Bootstrap Header class
3365 * @cfg {String} html content of header
3366 * @cfg {Number} level (1|2|3|4|5|6) default 1
3369 * Create a new Header
3370 * @param {Object} config The config object
3374 Roo.bootstrap.Header = function(config){
3375 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3378 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3386 getAutoCreate : function(){
3391 tag: 'h' + (1 *this.level),
3392 html: this.html || ''
3404 * Ext JS Library 1.1.1
3405 * Copyright(c) 2006-2007, Ext JS, LLC.
3407 * Originally Released Under LGPL - original licence link has changed is not relivant.
3410 * <script type="text/javascript">
3414 * @class Roo.bootstrap.MenuMgr
3415 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3418 Roo.bootstrap.MenuMgr = function(){
3419 var menus, active, groups = {}, attached = false, lastShow = new Date();
3421 // private - called when first menu is created
3424 active = new Roo.util.MixedCollection();
3425 Roo.get(document).addKeyListener(27, function(){
3426 if(active.length > 0){
3434 if(active && active.length > 0){
3435 var c = active.clone();
3445 if(active.length < 1){
3446 Roo.get(document).un("mouseup", onMouseDown);
3454 var last = active.last();
3455 lastShow = new Date();
3458 Roo.get(document).on("mouseup", onMouseDown);
3463 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3464 m.parentMenu.activeChild = m;
3465 }else if(last && last.isVisible()){
3466 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3471 function onBeforeHide(m){
3473 m.activeChild.hide();
3475 if(m.autoHideTimer){
3476 clearTimeout(m.autoHideTimer);
3477 delete m.autoHideTimer;
3482 function onBeforeShow(m){
3483 var pm = m.parentMenu;
3484 if(!pm && !m.allowOtherMenus){
3486 }else if(pm && pm.activeChild && active != m){
3487 pm.activeChild.hide();
3491 // private this should really trigger on mouseup..
3492 function onMouseDown(e){
3493 Roo.log("on Mouse Up");
3495 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3496 Roo.log("MenuManager hideAll");
3505 function onBeforeCheck(mi, state){
3507 var g = groups[mi.group];
3508 for(var i = 0, l = g.length; i < l; i++){
3510 g[i].setChecked(false);
3519 * Hides all menus that are currently visible
3521 hideAll : function(){
3526 register : function(menu){
3530 menus[menu.id] = menu;
3531 menu.on("beforehide", onBeforeHide);
3532 menu.on("hide", onHide);
3533 menu.on("beforeshow", onBeforeShow);
3534 menu.on("show", onShow);
3536 if(g && menu.events["checkchange"]){
3540 groups[g].push(menu);
3541 menu.on("checkchange", onCheck);
3546 * Returns a {@link Roo.menu.Menu} object
3547 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3548 * be used to generate and return a new Menu instance.
3550 get : function(menu){
3551 if(typeof menu == "string"){ // menu id
3553 }else if(menu.events){ // menu instance
3556 /*else if(typeof menu.length == 'number'){ // array of menu items?
3557 return new Roo.bootstrap.Menu({items:menu});
3558 }else{ // otherwise, must be a config
3559 return new Roo.bootstrap.Menu(menu);
3566 unregister : function(menu){
3567 delete menus[menu.id];
3568 menu.un("beforehide", onBeforeHide);
3569 menu.un("hide", onHide);
3570 menu.un("beforeshow", onBeforeShow);
3571 menu.un("show", onShow);
3573 if(g && menu.events["checkchange"]){
3574 groups[g].remove(menu);
3575 menu.un("checkchange", onCheck);
3580 registerCheckable : function(menuItem){
3581 var g = menuItem.group;
3586 groups[g].push(menuItem);
3587 menuItem.on("beforecheckchange", onBeforeCheck);
3592 unregisterCheckable : function(menuItem){
3593 var g = menuItem.group;
3595 groups[g].remove(menuItem);
3596 menuItem.un("beforecheckchange", onBeforeCheck);
3608 * @class Roo.bootstrap.Menu
3609 * @extends Roo.bootstrap.Component
3610 * Bootstrap Menu class - container for MenuItems
3611 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3612 * @cfg {bool} hidden if the menu should be hidden when rendered.
3613 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3614 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3615 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3616 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3620 * @param {Object} config The config object
3624 Roo.bootstrap.Menu = function(config){
3626 if (config.type == 'treeview') {
3627 // normally menu's are drawn attached to the document to handle layering etc..
3628 // however treeview (used by the docs menu is drawn into the parent element)
3629 this.container_method = 'getChildContainer';
3632 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3633 if (this.registerMenu && this.type != 'treeview') {
3634 Roo.bootstrap.MenuMgr.register(this);
3641 * Fires before this menu is displayed (return false to block)
3642 * @param {Roo.menu.Menu} this
3647 * Fires before this menu is hidden (return false to block)
3648 * @param {Roo.menu.Menu} this
3653 * Fires after this menu is displayed
3654 * @param {Roo.menu.Menu} this
3659 * Fires after this menu is hidden
3660 * @param {Roo.menu.Menu} this
3665 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3666 * @param {Roo.menu.Menu} this
3667 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3668 * @param {Roo.EventObject} e
3673 * Fires when the mouse is hovering over this menu
3674 * @param {Roo.menu.Menu} this
3675 * @param {Roo.EventObject} e
3676 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3681 * Fires when the mouse exits this menu
3682 * @param {Roo.menu.Menu} this
3683 * @param {Roo.EventObject} e
3684 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3689 * Fires when a menu item contained in this menu is clicked
3690 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3691 * @param {Roo.EventObject} e
3695 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3698 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3702 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3705 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3707 registerMenu : true,
3709 menuItems :false, // stores the menu items..
3719 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3721 hideTrigger : false,
3726 getChildContainer : function() {
3730 getAutoCreate : function(){
3732 //if (['right'].indexOf(this.align)!==-1) {
3733 // cfg.cn[1].cls += ' pull-right'
3738 cls : 'dropdown-menu shadow' ,
3739 style : 'z-index:1000'
3743 if (this.type === 'submenu') {
3744 cfg.cls = 'submenu active';
3746 if (this.type === 'treeview') {
3747 cfg.cls = 'treeview-menu';
3752 initEvents : function() {
3754 // Roo.log("ADD event");
3755 // Roo.log(this.triggerEl.dom);
3756 if (this.triggerEl) {
3758 this.triggerEl.on('click', this.onTriggerClick, this);
3760 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3762 if (!this.hideTrigger) {
3763 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3764 // dropdown toggle on the 'a' in BS4?
3765 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3767 this.triggerEl.addClass('dropdown-toggle');
3773 this.el.on('touchstart' , this.onTouch, this);
3775 this.el.on('click' , this.onClick, this);
3777 this.el.on("mouseover", this.onMouseOver, this);
3778 this.el.on("mouseout", this.onMouseOut, this);
3782 findTargetItem : function(e)
3784 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3788 //Roo.log(t); Roo.log(t.id);
3790 //Roo.log(this.menuitems);
3791 return this.menuitems.get(t.id);
3793 //return this.items.get(t.menuItemId);
3799 onTouch : function(e)
3801 Roo.log("menu.onTouch");
3802 //e.stopEvent(); this make the user popdown broken
3806 onClick : function(e)
3808 Roo.log("menu.onClick");
3810 var t = this.findTargetItem(e);
3811 if(!t || t.isContainer){
3816 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3817 if(t == this.activeItem && t.shouldDeactivate(e)){
3818 this.activeItem.deactivate();
3819 delete this.activeItem;
3823 this.setActiveItem(t, true);
3831 Roo.log('pass click event');
3835 this.fireEvent("click", this, t, e);
3839 if(!t.href.length || t.href == '#'){
3840 (function() { _this.hide(); }).defer(100);
3845 onMouseOver : function(e){
3846 var t = this.findTargetItem(e);
3849 // if(t.canActivate && !t.disabled){
3850 // this.setActiveItem(t, true);
3854 this.fireEvent("mouseover", this, e, t);
3856 isVisible : function(){
3857 return !this.hidden;
3859 onMouseOut : function(e){
3860 var t = this.findTargetItem(e);
3863 // if(t == this.activeItem && t.shouldDeactivate(e)){
3864 // this.activeItem.deactivate();
3865 // delete this.activeItem;
3868 this.fireEvent("mouseout", this, e, t);
3873 * Displays this menu relative to another element
3874 * @param {String/HTMLElement/Roo.Element} element The element to align to
3875 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3876 * the element (defaults to this.defaultAlign)
3877 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3879 show : function(el, pos, parentMenu)
3881 if (false === this.fireEvent("beforeshow", this)) {
3882 Roo.log("show canceled");
3885 this.parentMenu = parentMenu;
3889 this.el.addClass('show'); // show otherwise we do not know how big we are..
3891 var xy = this.el.getAlignToXY(el, pos);
3893 // bl-tl << left align below
3894 // tl-bl << left align
3896 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3897 // if it goes to far to the right.. -> align left.
3898 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3901 // was left align - go right?
3902 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3905 // goes down the bottom
3906 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3908 var a = this.align.replace('?', '').split('-');
3909 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3913 this.showAt( xy , parentMenu, false);
3916 * Displays this menu at a specific xy position
3917 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3918 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3920 showAt : function(xy, parentMenu, /* private: */_e){
3921 this.parentMenu = parentMenu;
3926 this.fireEvent("beforeshow", this);
3927 //xy = this.el.adjustForConstraints(xy);
3931 this.hideMenuItems();
3932 this.hidden = false;
3933 if (this.triggerEl) {
3934 this.triggerEl.addClass('open');
3937 this.el.addClass('show');
3941 // reassign x when hitting right
3943 // reassign y when hitting bottom
3945 // but the list may align on trigger left or trigger top... should it be a properity?
3947 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3952 this.fireEvent("show", this);
3958 this.doFocus.defer(50, this);
3962 doFocus : function(){
3964 this.focusEl.focus();
3969 * Hides this menu and optionally all parent menus
3970 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3972 hide : function(deep)
3974 if (false === this.fireEvent("beforehide", this)) {
3975 Roo.log("hide canceled");
3978 this.hideMenuItems();
3979 if(this.el && this.isVisible()){
3981 if(this.activeItem){
3982 this.activeItem.deactivate();
3983 this.activeItem = null;
3985 if (this.triggerEl) {
3986 this.triggerEl.removeClass('open');
3989 this.el.removeClass('show');
3991 this.fireEvent("hide", this);
3993 if(deep === true && this.parentMenu){
3994 this.parentMenu.hide(true);
3998 onTriggerClick : function(e)
4000 Roo.log('trigger click');
4002 var target = e.getTarget();
4004 Roo.log(target.nodeName.toLowerCase());
4006 if(target.nodeName.toLowerCase() === 'i'){
4012 onTriggerPress : function(e)
4014 Roo.log('trigger press');
4015 //Roo.log(e.getTarget());
4016 // Roo.log(this.triggerEl.dom);
4018 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4019 var pel = Roo.get(e.getTarget());
4020 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4021 Roo.log('is treeview or dropdown?');
4025 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4029 if (this.isVisible()) {
4035 this.show(this.triggerEl, this.align, false);
4038 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4045 hideMenuItems : function()
4047 Roo.log("hide Menu Items");
4052 this.el.select('.open',true).each(function(aa) {
4054 aa.removeClass('open');
4058 addxtypeChild : function (tree, cntr) {
4059 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4061 this.menuitems.add(comp);
4073 this.getEl().dom.innerHTML = '';
4074 this.menuitems.clear();
4088 * @class Roo.bootstrap.MenuItem
4089 * @extends Roo.bootstrap.Component
4090 * Bootstrap MenuItem class
4091 * @cfg {String} html the menu label
4092 * @cfg {String} href the link
4093 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4094 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4095 * @cfg {Boolean} active used on sidebars to highlight active itesm
4096 * @cfg {String} fa favicon to show on left of menu item.
4097 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4101 * Create a new MenuItem
4102 * @param {Object} config The config object
4106 Roo.bootstrap.MenuItem = function(config){
4107 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4112 * The raw click event for the entire grid.
4113 * @param {Roo.bootstrap.MenuItem} this
4114 * @param {Roo.EventObject} e
4120 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4124 preventDefault: false,
4125 isContainer : false,
4129 getAutoCreate : function(){
4131 if(this.isContainer){
4134 cls: 'dropdown-menu-item '
4144 cls : 'dropdown-item',
4149 if (this.fa !== false) {
4152 cls : 'fa fa-' + this.fa
4161 cls: 'dropdown-menu-item',
4164 if (this.parent().type == 'treeview') {
4165 cfg.cls = 'treeview-menu';
4168 cfg.cls += ' active';
4173 anc.href = this.href || cfg.cn[0].href ;
4174 ctag.html = this.html || cfg.cn[0].html ;
4178 initEvents: function()
4180 if (this.parent().type == 'treeview') {
4181 this.el.select('a').on('click', this.onClick, this);
4185 this.menu.parentType = this.xtype;
4186 this.menu.triggerEl = this.el;
4187 this.menu = this.addxtype(Roo.apply({}, this.menu));
4191 onClick : function(e)
4193 Roo.log('item on click ');
4195 if(this.preventDefault){
4198 //this.parent().hideMenuItems();
4200 this.fireEvent('click', this, e);
4219 * @class Roo.bootstrap.MenuSeparator
4220 * @extends Roo.bootstrap.Component
4221 * Bootstrap MenuSeparator class
4224 * Create a new MenuItem
4225 * @param {Object} config The config object
4229 Roo.bootstrap.MenuSeparator = function(config){
4230 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4233 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4235 getAutoCreate : function(){
4254 * @class Roo.bootstrap.Modal
4255 * @extends Roo.bootstrap.Component
4256 * Bootstrap Modal class
4257 * @cfg {String} title Title of dialog
4258 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4259 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4260 * @cfg {Boolean} specificTitle default false
4261 * @cfg {Array} buttons Array of buttons or standard button set..
4262 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4263 * @cfg {Boolean} animate default true
4264 * @cfg {Boolean} allow_close default true
4265 * @cfg {Boolean} fitwindow default false
4266 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4267 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4268 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4269 * @cfg {String} size (sm|lg|xl) default empty
4270 * @cfg {Number} max_width set the max width of modal
4271 * @cfg {Boolean} editableTitle can the title be edited
4276 * Create a new Modal Dialog
4277 * @param {Object} config The config object
4280 Roo.bootstrap.Modal = function(config){
4281 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4286 * The raw btnclick event for the button
4287 * @param {Roo.EventObject} e
4292 * Fire when dialog resize
4293 * @param {Roo.bootstrap.Modal} this
4294 * @param {Roo.EventObject} e
4298 * @event titlechanged
4299 * Fire when the editable title has been changed
4300 * @param {Roo.bootstrap.Modal} this
4301 * @param {Roo.EventObject} value
4303 "titlechanged" : true
4306 this.buttons = this.buttons || [];
4309 this.tmpl = Roo.factory(this.tmpl);
4314 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4316 title : 'test dialog',
4326 specificTitle: false,
4328 buttonPosition: 'right',
4350 editableTitle : false,
4352 onRender : function(ct, position)
4354 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4357 var cfg = Roo.apply({}, this.getAutoCreate());
4360 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4362 //if (!cfg.name.length) {
4366 cfg.cls += ' ' + this.cls;
4369 cfg.style = this.style;
4371 this.el = Roo.get(document.body).createChild(cfg, position);
4373 //var type = this.el.dom.type;
4376 if(this.tabIndex !== undefined){
4377 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4380 this.dialogEl = this.el.select('.modal-dialog',true).first();
4381 this.bodyEl = this.el.select('.modal-body',true).first();
4382 this.closeEl = this.el.select('.modal-header .close', true).first();
4383 this.headerEl = this.el.select('.modal-header',true).first();
4384 this.titleEl = this.el.select('.modal-title',true).first();
4385 this.footerEl = this.el.select('.modal-footer',true).first();
4387 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4389 //this.el.addClass("x-dlg-modal");
4391 if (this.buttons.length) {
4392 Roo.each(this.buttons, function(bb) {
4393 var b = Roo.apply({}, bb);
4394 b.xns = b.xns || Roo.bootstrap;
4395 b.xtype = b.xtype || 'Button';
4396 if (typeof(b.listeners) == 'undefined') {
4397 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4400 var btn = Roo.factory(b);
4402 btn.render(this.getButtonContainer());
4406 // render the children.
4409 if(typeof(this.items) != 'undefined'){
4410 var items = this.items;
4413 for(var i =0;i < items.length;i++) {
4414 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4418 this.items = nitems;
4420 // where are these used - they used to be body/close/footer
4424 //this.el.addClass([this.fieldClass, this.cls]);
4428 getAutoCreate : function()
4430 // we will default to modal-body-overflow - might need to remove or make optional later.
4432 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4433 html : this.html || ''
4438 cls : 'modal-title',
4442 if(this.specificTitle){ // WTF is this?
4447 if (this.allow_close && Roo.bootstrap.version == 3) {
4457 if (this.editableTitle) {
4459 cls: 'form-control roo-editable-title d-none',
4465 if (this.allow_close && Roo.bootstrap.version == 4) {
4475 if(this.size.length){
4476 size = 'modal-' + this.size;
4479 var footer = Roo.bootstrap.version == 3 ?
4481 cls : 'modal-footer',
4485 cls: 'btn-' + this.buttonPosition
4490 { // BS4 uses mr-auto on left buttons....
4491 cls : 'modal-footer'
4502 cls: "modal-dialog " + size,
4505 cls : "modal-content",
4508 cls : 'modal-header',
4523 modal.cls += ' fade';
4529 getChildContainer : function() {
4534 getButtonContainer : function() {
4536 return Roo.bootstrap.version == 4 ?
4537 this.el.select('.modal-footer',true).first()
4538 : this.el.select('.modal-footer div',true).first();
4541 initEvents : function()
4543 if (this.allow_close) {
4544 this.closeEl.on('click', this.hide, this);
4546 Roo.EventManager.onWindowResize(this.resize, this, true);
4547 if (this.editableTitle) {
4548 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4549 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4550 this.headerEditEl.on('keyup', function(e) {
4551 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4552 this.toggleHeaderInput(false)
4555 this.headerEditEl.on('blur', function(e) {
4556 this.toggleHeaderInput(false)
4565 this.maskEl.setSize(
4566 Roo.lib.Dom.getViewWidth(true),
4567 Roo.lib.Dom.getViewHeight(true)
4570 if (this.fitwindow) {
4572 this.dialogEl.setStyle( { 'max-width' : '100%' });
4574 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4575 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4580 if(this.max_width !== 0) {
4582 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4585 this.setSize(w, this.height);
4589 if(this.max_height) {
4590 this.setSize(w,Math.min(
4592 Roo.lib.Dom.getViewportHeight(true) - 60
4598 if(!this.fit_content) {
4599 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4603 this.setSize(w, Math.min(
4605 this.headerEl.getHeight() +
4606 this.footerEl.getHeight() +
4607 this.getChildHeight(this.bodyEl.dom.childNodes),
4608 Roo.lib.Dom.getViewportHeight(true) - 60)
4614 setSize : function(w,h)
4625 if (!this.rendered) {
4628 this.toggleHeaderInput(false);
4629 //this.el.setStyle('display', 'block');
4630 this.el.removeClass('hideing');
4631 this.el.dom.style.display='block';
4633 Roo.get(document.body).addClass('modal-open');
4635 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4638 this.el.addClass('show');
4639 this.el.addClass('in');
4642 this.el.addClass('show');
4643 this.el.addClass('in');
4646 // not sure how we can show data in here..
4648 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4651 Roo.get(document.body).addClass("x-body-masked");
4653 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4654 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4655 this.maskEl.dom.style.display = 'block';
4656 this.maskEl.addClass('show');
4661 this.fireEvent('show', this);
4663 // set zindex here - otherwise it appears to be ignored...
4664 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4667 this.items.forEach( function(e) {
4668 e.layout ? e.layout() : false;
4676 if(this.fireEvent("beforehide", this) !== false){
4678 this.maskEl.removeClass('show');
4680 this.maskEl.dom.style.display = '';
4681 Roo.get(document.body).removeClass("x-body-masked");
4682 this.el.removeClass('in');
4683 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4685 if(this.animate){ // why
4686 this.el.addClass('hideing');
4687 this.el.removeClass('show');
4689 if (!this.el.hasClass('hideing')) {
4690 return; // it's been shown again...
4693 this.el.dom.style.display='';
4695 Roo.get(document.body).removeClass('modal-open');
4696 this.el.removeClass('hideing');
4700 this.el.removeClass('show');
4701 this.el.dom.style.display='';
4702 Roo.get(document.body).removeClass('modal-open');
4705 this.fireEvent('hide', this);
4708 isVisible : function()
4711 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4715 addButton : function(str, cb)
4719 var b = Roo.apply({}, { html : str } );
4720 b.xns = b.xns || Roo.bootstrap;
4721 b.xtype = b.xtype || 'Button';
4722 if (typeof(b.listeners) == 'undefined') {
4723 b.listeners = { click : cb.createDelegate(this) };
4726 var btn = Roo.factory(b);
4728 btn.render(this.getButtonContainer());
4734 setDefaultButton : function(btn)
4736 //this.el.select('.modal-footer').()
4739 resizeTo: function(w,h)
4741 this.dialogEl.setWidth(w);
4743 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4745 this.bodyEl.setHeight(h - diff);
4747 this.fireEvent('resize', this);
4750 setContentSize : function(w, h)
4754 onButtonClick: function(btn,e)
4757 this.fireEvent('btnclick', btn.name, e);
4760 * Set the title of the Dialog
4761 * @param {String} str new Title
4763 setTitle: function(str) {
4764 this.titleEl.dom.innerHTML = str;
4768 * Set the body of the Dialog
4769 * @param {String} str new Title
4771 setBody: function(str) {
4772 this.bodyEl.dom.innerHTML = str;
4775 * Set the body of the Dialog using the template
4776 * @param {Obj} data - apply this data to the template and replace the body contents.
4778 applyBody: function(obj)
4781 Roo.log("Error - using apply Body without a template");
4784 this.tmpl.overwrite(this.bodyEl, obj);
4787 getChildHeight : function(child_nodes)
4791 child_nodes.length == 0
4796 var child_height = 0;
4798 for(var i = 0; i < child_nodes.length; i++) {
4801 * for modal with tabs...
4802 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4804 var layout_childs = child_nodes[i].childNodes;
4806 for(var j = 0; j < layout_childs.length; j++) {
4808 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4810 var layout_body_childs = layout_childs[j].childNodes;
4812 for(var k = 0; k < layout_body_childs.length; k++) {
4814 if(layout_body_childs[k].classList.contains('navbar')) {
4815 child_height += layout_body_childs[k].offsetHeight;
4819 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4821 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4823 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4825 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4826 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4841 child_height += child_nodes[i].offsetHeight;
4842 // Roo.log(child_nodes[i].offsetHeight);
4845 return child_height;
4847 toggleHeaderInput : function(is_edit)
4849 if (!this.editableTitle) {
4850 return; // not editable.
4852 if (is_edit && this.is_header_editing) {
4853 return; // already editing..
4857 this.headerEditEl.dom.value = this.title;
4858 this.headerEditEl.removeClass('d-none');
4859 this.headerEditEl.dom.focus();
4860 this.titleEl.addClass('d-none');
4862 this.is_header_editing = true;
4865 // flip back to not editing.
4866 this.title = this.headerEditEl.dom.value;
4867 this.headerEditEl.addClass('d-none');
4868 this.titleEl.removeClass('d-none');
4869 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4870 this.is_header_editing = false;
4871 this.fireEvent('titlechanged', this, this.title);
4880 Roo.apply(Roo.bootstrap.Modal, {
4882 * Button config that displays a single OK button
4891 * Button config that displays Yes and No buttons
4907 * Button config that displays OK and Cancel buttons
4922 * Button config that displays Yes, No and Cancel buttons
4947 * messagebox - can be used as a replace
4951 * @class Roo.MessageBox
4952 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4956 Roo.Msg.alert('Status', 'Changes saved successfully.');
4958 // Prompt for user data:
4959 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4961 // process text value...
4965 // Show a dialog using config options:
4967 title:'Save Changes?',
4968 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4969 buttons: Roo.Msg.YESNOCANCEL,
4976 Roo.bootstrap.MessageBox = function(){
4977 var dlg, opt, mask, waitTimer;
4978 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4979 var buttons, activeTextEl, bwidth;
4983 var handleButton = function(button){
4985 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4989 var handleHide = function(){
4991 dlg.el.removeClass(opt.cls);
4994 // Roo.TaskMgr.stop(waitTimer);
4995 // waitTimer = null;
5000 var updateButtons = function(b){
5003 buttons["ok"].hide();
5004 buttons["cancel"].hide();
5005 buttons["yes"].hide();
5006 buttons["no"].hide();
5007 dlg.footerEl.hide();
5011 dlg.footerEl.show();
5012 for(var k in buttons){
5013 if(typeof buttons[k] != "function"){
5016 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5017 width += buttons[k].el.getWidth()+15;
5027 var handleEsc = function(d, k, e){
5028 if(opt && opt.closable !== false){
5038 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5039 * @return {Roo.BasicDialog} The BasicDialog element
5041 getDialog : function(){
5043 dlg = new Roo.bootstrap.Modal( {
5046 //constraintoviewport:false,
5048 //collapsible : false,
5053 //buttonAlign:"center",
5054 closeClick : function(){
5055 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5058 handleButton("cancel");
5063 dlg.on("hide", handleHide);
5065 //dlg.addKeyListener(27, handleEsc);
5067 this.buttons = buttons;
5068 var bt = this.buttonText;
5069 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5070 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5071 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5072 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5074 bodyEl = dlg.bodyEl.createChild({
5076 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5077 '<textarea class="roo-mb-textarea"></textarea>' +
5078 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5080 msgEl = bodyEl.dom.firstChild;
5081 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5082 textboxEl.enableDisplayMode();
5083 textboxEl.addKeyListener([10,13], function(){
5084 if(dlg.isVisible() && opt && opt.buttons){
5087 }else if(opt.buttons.yes){
5088 handleButton("yes");
5092 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5093 textareaEl.enableDisplayMode();
5094 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5095 progressEl.enableDisplayMode();
5097 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5098 var pf = progressEl.dom.firstChild;
5100 pp = Roo.get(pf.firstChild);
5101 pp.setHeight(pf.offsetHeight);
5109 * Updates the message box body text
5110 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5111 * the XHTML-compliant non-breaking space character '&#160;')
5112 * @return {Roo.MessageBox} This message box
5114 updateText : function(text)
5116 if(!dlg.isVisible() && !opt.width){
5117 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5118 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5120 msgEl.innerHTML = text || ' ';
5122 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5123 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5125 Math.min(opt.width || cw , this.maxWidth),
5126 Math.max(opt.minWidth || this.minWidth, bwidth)
5129 activeTextEl.setWidth(w);
5131 if(dlg.isVisible()){
5132 dlg.fixedcenter = false;
5134 // to big, make it scroll. = But as usual stupid IE does not support
5137 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5138 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5139 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5141 bodyEl.dom.style.height = '';
5142 bodyEl.dom.style.overflowY = '';
5145 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5147 bodyEl.dom.style.overflowX = '';
5150 dlg.setContentSize(w, bodyEl.getHeight());
5151 if(dlg.isVisible()){
5152 dlg.fixedcenter = true;
5158 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5159 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5160 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5161 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5162 * @return {Roo.MessageBox} This message box
5164 updateProgress : function(value, text){
5166 this.updateText(text);
5169 if (pp) { // weird bug on my firefox - for some reason this is not defined
5170 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5171 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5177 * Returns true if the message box is currently displayed
5178 * @return {Boolean} True if the message box is visible, else false
5180 isVisible : function(){
5181 return dlg && dlg.isVisible();
5185 * Hides the message box if it is displayed
5188 if(this.isVisible()){
5194 * Displays a new message box, or reinitializes an existing message box, based on the config options
5195 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5196 * The following config object properties are supported:
5198 Property Type Description
5199 ---------- --------------- ------------------------------------------------------------------------------------
5200 animEl String/Element An id or Element from which the message box should animate as it opens and
5201 closes (defaults to undefined)
5202 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5203 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5204 closable Boolean False to hide the top-right close button (defaults to true). Note that
5205 progress and wait dialogs will ignore this property and always hide the
5206 close button as they can only be closed programmatically.
5207 cls String A custom CSS class to apply to the message box element
5208 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5209 displayed (defaults to 75)
5210 fn Function A callback function to execute after closing the dialog. The arguments to the
5211 function will be btn (the name of the button that was clicked, if applicable,
5212 e.g. "ok"), and text (the value of the active text field, if applicable).
5213 Progress and wait dialogs will ignore this option since they do not respond to
5214 user actions and can only be closed programmatically, so any required function
5215 should be called by the same code after it closes the dialog.
5216 icon String A CSS class that provides a background image to be used as an icon for
5217 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5218 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5219 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5220 modal Boolean False to allow user interaction with the page while the message box is
5221 displayed (defaults to true)
5222 msg String A string that will replace the existing message box body text (defaults
5223 to the XHTML-compliant non-breaking space character ' ')
5224 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5225 progress Boolean True to display a progress bar (defaults to false)
5226 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5227 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5228 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5229 title String The title text
5230 value String The string value to set into the active textbox element if displayed
5231 wait Boolean True to display a progress bar (defaults to false)
5232 width Number The width of the dialog in pixels
5239 msg: 'Please enter your address:',
5241 buttons: Roo.MessageBox.OKCANCEL,
5244 animEl: 'addAddressBtn'
5247 * @param {Object} config Configuration options
5248 * @return {Roo.MessageBox} This message box
5250 show : function(options)
5253 // this causes nightmares if you show one dialog after another
5254 // especially on callbacks..
5256 if(this.isVisible()){
5259 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5260 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5261 Roo.log("New Dialog Message:" + options.msg )
5262 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5263 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5266 var d = this.getDialog();
5268 d.setTitle(opt.title || " ");
5269 d.closeEl.setDisplayed(opt.closable !== false);
5270 activeTextEl = textboxEl;
5271 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5276 textareaEl.setHeight(typeof opt.multiline == "number" ?
5277 opt.multiline : this.defaultTextHeight);
5278 activeTextEl = textareaEl;
5287 progressEl.setDisplayed(opt.progress === true);
5289 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5291 this.updateProgress(0);
5292 activeTextEl.dom.value = opt.value || "";
5294 dlg.setDefaultButton(activeTextEl);
5296 var bs = opt.buttons;
5300 }else if(bs && bs.yes){
5301 db = buttons["yes"];
5303 dlg.setDefaultButton(db);
5305 bwidth = updateButtons(opt.buttons);
5306 this.updateText(opt.msg);
5308 d.el.addClass(opt.cls);
5310 d.proxyDrag = opt.proxyDrag === true;
5311 d.modal = opt.modal !== false;
5312 d.mask = opt.modal !== false ? mask : false;
5314 // force it to the end of the z-index stack so it gets a cursor in FF
5315 document.body.appendChild(dlg.el.dom);
5316 d.animateTarget = null;
5317 d.show(options.animEl);
5323 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5324 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5325 * and closing the message box when the process is complete.
5326 * @param {String} title The title bar text
5327 * @param {String} msg The message box body text
5328 * @return {Roo.MessageBox} This message box
5330 progress : function(title, msg){
5337 minWidth: this.minProgressWidth,
5344 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5345 * If a callback function is passed it will be called after the user clicks the button, and the
5346 * id of the button that was clicked will be passed as the only parameter to the callback
5347 * (could also be the top-right close button).
5348 * @param {String} title The title bar text
5349 * @param {String} msg The message box body text
5350 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5351 * @param {Object} scope (optional) The scope of the callback function
5352 * @return {Roo.MessageBox} This message box
5354 alert : function(title, msg, fn, scope)
5369 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5370 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5371 * You are responsible for closing the message box when the process is complete.
5372 * @param {String} msg The message box body text
5373 * @param {String} title (optional) The title bar text
5374 * @return {Roo.MessageBox} This message box
5376 wait : function(msg, title){
5387 waitTimer = Roo.TaskMgr.start({
5389 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5397 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5398 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5399 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5400 * @param {String} title The title bar text
5401 * @param {String} msg The message box body text
5402 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5403 * @param {Object} scope (optional) The scope of the callback function
5404 * @return {Roo.MessageBox} This message box
5406 confirm : function(title, msg, fn, scope){
5410 buttons: this.YESNO,
5419 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5420 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5421 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5422 * (could also be the top-right close button) and the text that was entered will be passed as the two
5423 * parameters to the callback.
5424 * @param {String} title The title bar text
5425 * @param {String} msg The message box body text
5426 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5427 * @param {Object} scope (optional) The scope of the callback function
5428 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5429 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5430 * @return {Roo.MessageBox} This message box
5432 prompt : function(title, msg, fn, scope, multiline){
5436 buttons: this.OKCANCEL,
5441 multiline: multiline,
5448 * Button config that displays a single OK button
5453 * Button config that displays Yes and No buttons
5456 YESNO : {yes:true, no:true},
5458 * Button config that displays OK and Cancel buttons
5461 OKCANCEL : {ok:true, cancel:true},
5463 * Button config that displays Yes, No and Cancel buttons
5466 YESNOCANCEL : {yes:true, no:true, cancel:true},
5469 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5472 defaultTextHeight : 75,
5474 * The maximum width in pixels of the message box (defaults to 600)
5479 * The minimum width in pixels of the message box (defaults to 100)
5484 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5485 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5488 minProgressWidth : 250,
5490 * An object containing the default button text strings that can be overriden for localized language support.
5491 * Supported properties are: ok, cancel, yes and no.
5492 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5505 * Shorthand for {@link Roo.MessageBox}
5507 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5508 Roo.Msg = Roo.Msg || Roo.MessageBox;
5517 * @class Roo.bootstrap.Navbar
5518 * @extends Roo.bootstrap.Component
5519 * Bootstrap Navbar class
5522 * Create a new Navbar
5523 * @param {Object} config The config object
5527 Roo.bootstrap.Navbar = function(config){
5528 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5532 * @event beforetoggle
5533 * Fire before toggle the menu
5534 * @param {Roo.EventObject} e
5536 "beforetoggle" : true
5540 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5549 getAutoCreate : function(){
5552 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5556 initEvents :function ()
5558 //Roo.log(this.el.select('.navbar-toggle',true));
5559 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5566 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5568 var size = this.el.getSize();
5569 this.maskEl.setSize(size.width, size.height);
5570 this.maskEl.enableDisplayMode("block");
5579 getChildContainer : function()
5581 if (this.el && this.el.select('.collapse').getCount()) {
5582 return this.el.select('.collapse',true).first();
5597 onToggle : function()
5600 if(this.fireEvent('beforetoggle', this) === false){
5603 var ce = this.el.select('.navbar-collapse',true).first();
5605 if (!ce.hasClass('show')) {
5615 * Expand the navbar pulldown
5617 expand : function ()
5620 var ce = this.el.select('.navbar-collapse',true).first();
5621 if (ce.hasClass('collapsing')) {
5624 ce.dom.style.height = '';
5626 ce.addClass('in'); // old...
5627 ce.removeClass('collapse');
5628 ce.addClass('show');
5629 var h = ce.getHeight();
5631 ce.removeClass('show');
5632 // at this point we should be able to see it..
5633 ce.addClass('collapsing');
5635 ce.setHeight(0); // resize it ...
5636 ce.on('transitionend', function() {
5637 //Roo.log('done transition');
5638 ce.removeClass('collapsing');
5639 ce.addClass('show');
5640 ce.removeClass('collapse');
5642 ce.dom.style.height = '';
5643 }, this, { single: true} );
5645 ce.dom.scrollTop = 0;
5648 * Collapse the navbar pulldown
5650 collapse : function()
5652 var ce = this.el.select('.navbar-collapse',true).first();
5654 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5655 // it's collapsed or collapsing..
5658 ce.removeClass('in'); // old...
5659 ce.setHeight(ce.getHeight());
5660 ce.removeClass('show');
5661 ce.addClass('collapsing');
5663 ce.on('transitionend', function() {
5664 ce.dom.style.height = '';
5665 ce.removeClass('collapsing');
5666 ce.addClass('collapse');
5667 }, this, { single: true} );
5687 * @class Roo.bootstrap.NavSimplebar
5688 * @extends Roo.bootstrap.Navbar
5689 * Bootstrap Sidebar class
5691 * @cfg {Boolean} inverse is inverted color
5693 * @cfg {String} type (nav | pills | tabs)
5694 * @cfg {Boolean} arrangement stacked | justified
5695 * @cfg {String} align (left | right) alignment
5697 * @cfg {Boolean} main (true|false) main nav bar? default false
5698 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5700 * @cfg {String} tag (header|footer|nav|div) default is nav
5702 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5706 * Create a new Sidebar
5707 * @param {Object} config The config object
5711 Roo.bootstrap.NavSimplebar = function(config){
5712 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5715 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5731 getAutoCreate : function(){
5735 tag : this.tag || 'div',
5736 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5738 if (['light','white'].indexOf(this.weight) > -1) {
5739 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5741 cfg.cls += ' bg-' + this.weight;
5744 cfg.cls += ' navbar-inverse';
5748 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5750 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5759 cls: 'nav nav-' + this.xtype,
5765 this.type = this.type || 'nav';
5766 if (['tabs','pills'].indexOf(this.type) != -1) {
5767 cfg.cn[0].cls += ' nav-' + this.type
5771 if (this.type!=='nav') {
5772 Roo.log('nav type must be nav/tabs/pills')
5774 cfg.cn[0].cls += ' navbar-nav'
5780 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5781 cfg.cn[0].cls += ' nav-' + this.arrangement;
5785 if (this.align === 'right') {
5786 cfg.cn[0].cls += ' navbar-right';
5811 * navbar-expand-md fixed-top
5815 * @class Roo.bootstrap.NavHeaderbar
5816 * @extends Roo.bootstrap.NavSimplebar
5817 * Bootstrap Sidebar class
5819 * @cfg {String} brand what is brand
5820 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5821 * @cfg {String} brand_href href of the brand
5822 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5823 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5824 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5825 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5828 * Create a new Sidebar
5829 * @param {Object} config The config object
5833 Roo.bootstrap.NavHeaderbar = function(config){
5834 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5838 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5845 desktopCenter : false,
5848 getAutoCreate : function(){
5851 tag: this.nav || 'nav',
5852 cls: 'navbar navbar-expand-md',
5858 if (this.desktopCenter) {
5859 cn.push({cls : 'container', cn : []});
5867 cls: 'navbar-toggle navbar-toggler',
5868 'data-toggle': 'collapse',
5873 html: 'Toggle navigation'
5877 cls: 'icon-bar navbar-toggler-icon'
5890 cn.push( Roo.bootstrap.version == 4 ? btn : {
5892 cls: 'navbar-header',
5901 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5905 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5907 if (['light','white'].indexOf(this.weight) > -1) {
5908 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5910 cfg.cls += ' bg-' + this.weight;
5913 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5914 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5916 // tag can override this..
5918 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5921 if (this.brand !== '') {
5922 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5923 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5925 href: this.brand_href ? this.brand_href : '#',
5926 cls: 'navbar-brand',
5934 cfg.cls += ' main-nav';
5942 getHeaderChildContainer : function()
5944 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5945 return this.el.select('.navbar-header',true).first();
5948 return this.getChildContainer();
5951 getChildContainer : function()
5954 return this.el.select('.roo-navbar-collapse',true).first();
5959 initEvents : function()
5961 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5963 if (this.autohide) {
5968 Roo.get(document).on('scroll',function(e) {
5969 var ns = Roo.get(document).getScroll().top;
5970 var os = prevScroll;
5974 ft.removeClass('slideDown');
5975 ft.addClass('slideUp');
5978 ft.removeClass('slideUp');
5979 ft.addClass('slideDown');
6000 * @class Roo.bootstrap.NavSidebar
6001 * @extends Roo.bootstrap.Navbar
6002 * Bootstrap Sidebar class
6005 * Create a new Sidebar
6006 * @param {Object} config The config object
6010 Roo.bootstrap.NavSidebar = function(config){
6011 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6014 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6016 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6018 getAutoCreate : function(){
6023 cls: 'sidebar sidebar-nav'
6045 * @class Roo.bootstrap.NavGroup
6046 * @extends Roo.bootstrap.Component
6047 * Bootstrap NavGroup class
6048 * @cfg {String} align (left|right)
6049 * @cfg {Boolean} inverse
6050 * @cfg {String} type (nav|pills|tab) default nav
6051 * @cfg {String} navId - reference Id for navbar.
6052 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6055 * Create a new nav group
6056 * @param {Object} config The config object
6059 Roo.bootstrap.NavGroup = function(config){
6060 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6063 Roo.bootstrap.NavGroup.register(this);
6067 * Fires when the active item changes
6068 * @param {Roo.bootstrap.NavGroup} this
6069 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6070 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6077 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6089 getAutoCreate : function()
6091 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6097 if (Roo.bootstrap.version == 4) {
6098 if (['tabs','pills'].indexOf(this.type) != -1) {
6099 cfg.cls += ' nav-' + this.type;
6101 // trying to remove so header bar can right align top?
6102 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6103 // do not use on header bar...
6104 cfg.cls += ' navbar-nav';
6109 if (['tabs','pills'].indexOf(this.type) != -1) {
6110 cfg.cls += ' nav-' + this.type
6112 if (this.type !== 'nav') {
6113 Roo.log('nav type must be nav/tabs/pills')
6115 cfg.cls += ' navbar-nav'
6119 if (this.parent() && this.parent().sidebar) {
6122 cls: 'dashboard-menu sidebar-menu'
6128 if (this.form === true) {
6131 cls: 'navbar-form form-inline'
6133 //nav navbar-right ml-md-auto
6134 if (this.align === 'right') {
6135 cfg.cls += ' navbar-right ml-md-auto';
6137 cfg.cls += ' navbar-left';
6141 if (this.align === 'right') {
6142 cfg.cls += ' navbar-right ml-md-auto';
6144 cfg.cls += ' mr-auto';
6148 cfg.cls += ' navbar-inverse';
6156 * sets the active Navigation item
6157 * @param {Roo.bootstrap.NavItem} the new current navitem
6159 setActiveItem : function(item)
6162 Roo.each(this.navItems, function(v){
6167 v.setActive(false, true);
6174 item.setActive(true, true);
6175 this.fireEvent('changed', this, item, prev);
6180 * gets the active Navigation item
6181 * @return {Roo.bootstrap.NavItem} the current navitem
6183 getActive : function()
6187 Roo.each(this.navItems, function(v){
6198 indexOfNav : function()
6202 Roo.each(this.navItems, function(v,i){
6213 * adds a Navigation item
6214 * @param {Roo.bootstrap.NavItem} the navitem to add
6216 addItem : function(cfg)
6218 if (this.form && Roo.bootstrap.version == 4) {
6221 var cn = new Roo.bootstrap.NavItem(cfg);
6223 cn.parentId = this.id;
6224 cn.onRender(this.el, null);
6228 * register a Navigation item
6229 * @param {Roo.bootstrap.NavItem} the navitem to add
6231 register : function(item)
6233 this.navItems.push( item);
6234 item.navId = this.navId;
6239 * clear all the Navigation item
6242 clearAll : function()
6245 this.el.dom.innerHTML = '';
6248 getNavItem: function(tabId)
6251 Roo.each(this.navItems, function(e) {
6252 if (e.tabId == tabId) {
6262 setActiveNext : function()
6264 var i = this.indexOfNav(this.getActive());
6265 if (i > this.navItems.length) {
6268 this.setActiveItem(this.navItems[i+1]);
6270 setActivePrev : function()
6272 var i = this.indexOfNav(this.getActive());
6276 this.setActiveItem(this.navItems[i-1]);
6278 clearWasActive : function(except) {
6279 Roo.each(this.navItems, function(e) {
6280 if (e.tabId != except.tabId && e.was_active) {
6281 e.was_active = false;
6288 getWasActive : function ()
6291 Roo.each(this.navItems, function(e) {
6306 Roo.apply(Roo.bootstrap.NavGroup, {
6310 * register a Navigation Group
6311 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6313 register : function(navgrp)
6315 this.groups[navgrp.navId] = navgrp;
6319 * fetch a Navigation Group based on the navigation ID
6320 * @param {string} the navgroup to add
6321 * @returns {Roo.bootstrap.NavGroup} the navgroup
6323 get: function(navId) {
6324 if (typeof(this.groups[navId]) == 'undefined') {
6326 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6328 return this.groups[navId] ;
6343 * @class Roo.bootstrap.NavItem
6344 * @extends Roo.bootstrap.Component
6345 * Bootstrap Navbar.NavItem class
6346 * @cfg {String} href link to
6347 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6348 * @cfg {Boolean} button_outline show and outlined button
6349 * @cfg {String} html content of button
6350 * @cfg {String} badge text inside badge
6351 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6352 * @cfg {String} glyphicon DEPRICATED - use fa
6353 * @cfg {String} icon DEPRICATED - use fa
6354 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6355 * @cfg {Boolean} active Is item active
6356 * @cfg {Boolean} disabled Is item disabled
6357 * @cfg {String} linkcls Link Class
6358 * @cfg {Boolean} preventDefault (true | false) default false
6359 * @cfg {String} tabId the tab that this item activates.
6360 * @cfg {String} tagtype (a|span) render as a href or span?
6361 * @cfg {Boolean} animateRef (true|false) link to element default false
6364 * Create a new Navbar Item
6365 * @param {Object} config The config object
6367 Roo.bootstrap.NavItem = function(config){
6368 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6373 * The raw click event for the entire grid.
6374 * @param {Roo.EventObject} e
6379 * Fires when the active item active state changes
6380 * @param {Roo.bootstrap.NavItem} this
6381 * @param {boolean} state the new state
6387 * Fires when scroll to element
6388 * @param {Roo.bootstrap.NavItem} this
6389 * @param {Object} options
6390 * @param {Roo.EventObject} e
6398 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6407 preventDefault : false,
6415 button_outline : false,
6419 getAutoCreate : function(){
6426 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6429 cfg.cls += ' active' ;
6431 if (this.disabled) {
6432 cfg.cls += ' disabled';
6436 if (this.button_weight.length) {
6437 cfg.tag = this.href ? 'a' : 'button';
6438 cfg.html = this.html || '';
6439 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6441 cfg.href = this.href;
6444 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6446 cfg.cls += " nav-html";
6449 // menu .. should add dropdown-menu class - so no need for carat..
6451 if (this.badge !== '') {
6453 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6458 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6462 href : this.href || "#",
6463 html: this.html || '',
6467 if (this.tagtype == 'a') {
6468 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6472 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6473 } else if (this.fa) {
6474 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6475 } else if(this.glyphicon) {
6476 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6478 cfg.cn[0].cls += " nav-html";
6482 cfg.cn[0].html += " <span class='caret'></span>";
6486 if (this.badge !== '') {
6487 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6495 onRender : function(ct, position)
6497 // Roo.log("Call onRender: " + this.xtype);
6498 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6502 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6503 this.navLink = this.el.select('.nav-link',true).first();
6504 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6509 initEvents: function()
6511 if (typeof (this.menu) != 'undefined') {
6512 this.menu.parentType = this.xtype;
6513 this.menu.triggerEl = this.el;
6514 this.menu = this.addxtype(Roo.apply({}, this.menu));
6517 this.el.on('click', this.onClick, this);
6519 //if(this.tagtype == 'span'){
6520 // this.el.select('span',true).on('click', this.onClick, this);
6523 // at this point parent should be available..
6524 this.parent().register(this);
6527 onClick : function(e)
6529 if (e.getTarget('.dropdown-menu-item')) {
6530 // did you click on a menu itemm.... - then don't trigger onclick..
6535 this.preventDefault ||
6538 Roo.log("NavItem - prevent Default?");
6542 if (this.disabled) {
6546 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6547 if (tg && tg.transition) {
6548 Roo.log("waiting for the transitionend");
6554 //Roo.log("fire event clicked");
6555 if(this.fireEvent('click', this, e) === false){
6559 if(this.tagtype == 'span'){
6563 //Roo.log(this.href);
6564 var ael = this.el.select('a',true).first();
6567 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6568 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6569 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6570 return; // ignore... - it's a 'hash' to another page.
6572 Roo.log("NavItem - prevent Default?");
6574 this.scrollToElement(e);
6578 var p = this.parent();
6580 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6581 if (typeof(p.setActiveItem) !== 'undefined') {
6582 p.setActiveItem(this);
6586 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6587 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6588 // remove the collapsed menu expand...
6589 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6593 isActive: function () {
6596 setActive : function(state, fire, is_was_active)
6598 if (this.active && !state && this.navId) {
6599 this.was_active = true;
6600 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6602 nv.clearWasActive(this);
6606 this.active = state;
6609 this.el.removeClass('active');
6610 this.navLink ? this.navLink.removeClass('active') : false;
6611 } else if (!this.el.hasClass('active')) {
6613 this.el.addClass('active');
6614 if (Roo.bootstrap.version == 4 && this.navLink ) {
6615 this.navLink.addClass('active');
6620 this.fireEvent('changed', this, state);
6623 // show a panel if it's registered and related..
6625 if (!this.navId || !this.tabId || !state || is_was_active) {
6629 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6633 var pan = tg.getPanelByName(this.tabId);
6637 // if we can not flip to new panel - go back to old nav highlight..
6638 if (false == tg.showPanel(pan)) {
6639 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6641 var onav = nv.getWasActive();
6643 onav.setActive(true, false, true);
6652 // this should not be here...
6653 setDisabled : function(state)
6655 this.disabled = state;
6657 this.el.removeClass('disabled');
6658 } else if (!this.el.hasClass('disabled')) {
6659 this.el.addClass('disabled');
6665 * Fetch the element to display the tooltip on.
6666 * @return {Roo.Element} defaults to this.el
6668 tooltipEl : function()
6670 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6673 scrollToElement : function(e)
6675 var c = document.body;
6678 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6680 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6681 c = document.documentElement;
6684 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6690 var o = target.calcOffsetsTo(c);
6697 this.fireEvent('scrollto', this, options, e);
6699 Roo.get(c).scrollTo('top', options.value, true);
6704 * Set the HTML (text content) of the item
6705 * @param {string} html content for the nav item
6707 setHtml : function(html)
6710 this.htmlEl.dom.innerHTML = html;
6722 * <span> icon </span>
6723 * <span> text </span>
6724 * <span>badge </span>
6728 * @class Roo.bootstrap.NavSidebarItem
6729 * @extends Roo.bootstrap.NavItem
6730 * Bootstrap Navbar.NavSidebarItem class
6731 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6732 * {Boolean} open is the menu open
6733 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6734 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6735 * {String} buttonSize (sm|md|lg)the extra classes for the button
6736 * {Boolean} showArrow show arrow next to the text (default true)
6738 * Create a new Navbar Button
6739 * @param {Object} config The config object
6741 Roo.bootstrap.NavSidebarItem = function(config){
6742 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6747 * The raw click event for the entire grid.
6748 * @param {Roo.EventObject} e
6753 * Fires when the active item active state changes
6754 * @param {Roo.bootstrap.NavSidebarItem} this
6755 * @param {boolean} state the new state
6763 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6765 badgeWeight : 'default',
6771 buttonWeight : 'default',
6777 getAutoCreate : function(){
6782 href : this.href || '#',
6788 if(this.buttonView){
6791 href : this.href || '#',
6792 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6805 cfg.cls += ' active';
6808 if (this.disabled) {
6809 cfg.cls += ' disabled';
6812 cfg.cls += ' open x-open';
6815 if (this.glyphicon || this.icon) {
6816 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6817 a.cn.push({ tag : 'i', cls : c }) ;
6820 if(!this.buttonView){
6823 html : this.html || ''
6830 if (this.badge !== '') {
6831 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6837 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6840 a.cls += ' dropdown-toggle treeview' ;
6846 initEvents : function()
6848 if (typeof (this.menu) != 'undefined') {
6849 this.menu.parentType = this.xtype;
6850 this.menu.triggerEl = this.el;
6851 this.menu = this.addxtype(Roo.apply({}, this.menu));
6854 this.el.on('click', this.onClick, this);
6856 if(this.badge !== ''){
6857 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6862 onClick : function(e)
6869 if(this.preventDefault){
6873 this.fireEvent('click', this, e);
6876 disable : function()
6878 this.setDisabled(true);
6883 this.setDisabled(false);
6886 setDisabled : function(state)
6888 if(this.disabled == state){
6892 this.disabled = state;
6895 this.el.addClass('disabled');
6899 this.el.removeClass('disabled');
6904 setActive : function(state)
6906 if(this.active == state){
6910 this.active = state;
6913 this.el.addClass('active');
6917 this.el.removeClass('active');
6922 isActive: function ()
6927 setBadge : function(str)
6933 this.badgeEl.dom.innerHTML = str;
6948 Roo.namespace('Roo.bootstrap.breadcrumb');
6952 * @class Roo.bootstrap.breadcrumb.Nav
6953 * @extends Roo.bootstrap.Component
6954 * Bootstrap Breadcrumb Nav Class
6956 * @children Roo.bootstrap.breadcrumb.Item
6959 * Create a new breadcrumb.Nav
6960 * @param {Object} config The config object
6964 Roo.bootstrap.breadcrumb.Nav = function(config){
6965 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6970 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6972 getAutoCreate : function()
6989 initEvents: function()
6991 this.olEl = this.el.select('ol',true).first();
6993 getChildContainer : function()
7009 * @class Roo.bootstrap.breadcrumb.Nav
7010 * @extends Roo.bootstrap.Component
7011 * Bootstrap Breadcrumb Nav Class
7013 * @children Roo.bootstrap.breadcrumb.Component
7014 * @cfg {String} html the content of the link.
7015 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7016 * @cfg {Boolean} active is it active
7020 * Create a new breadcrumb.Nav
7021 * @param {Object} config The config object
7024 Roo.bootstrap.breadcrumb.Item = function(config){
7025 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7030 * The img click event for the img.
7031 * @param {Roo.EventObject} e
7038 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7043 getAutoCreate : function()
7048 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7050 if (this.href !== false) {
7057 cfg.html = this.html;
7063 initEvents: function()
7066 this.el.select('a', true).first().on('click',this.onClick, this)
7070 onClick : function(e)
7073 this.fireEvent('click',this, e);
7086 * @class Roo.bootstrap.Row
7087 * @extends Roo.bootstrap.Component
7088 * Bootstrap Row class (contains columns...)
7092 * @param {Object} config The config object
7095 Roo.bootstrap.Row = function(config){
7096 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7099 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7101 getAutoCreate : function(){
7120 * @class Roo.bootstrap.Pagination
7121 * @extends Roo.bootstrap.Component
7122 * Bootstrap Pagination class
7123 * @cfg {String} size xs | sm | md | lg
7124 * @cfg {Boolean} inverse false | true
7127 * Create a new Pagination
7128 * @param {Object} config The config object
7131 Roo.bootstrap.Pagination = function(config){
7132 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7135 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7141 getAutoCreate : function(){
7147 cfg.cls += ' inverse';
7153 cfg.cls += " " + this.cls;
7171 * @class Roo.bootstrap.PaginationItem
7172 * @extends Roo.bootstrap.Component
7173 * Bootstrap PaginationItem class
7174 * @cfg {String} html text
7175 * @cfg {String} href the link
7176 * @cfg {Boolean} preventDefault (true | false) default true
7177 * @cfg {Boolean} active (true | false) default false
7178 * @cfg {Boolean} disabled default false
7182 * Create a new PaginationItem
7183 * @param {Object} config The config object
7187 Roo.bootstrap.PaginationItem = function(config){
7188 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7193 * The raw click event for the entire grid.
7194 * @param {Roo.EventObject} e
7200 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7204 preventDefault: true,
7209 getAutoCreate : function(){
7215 href : this.href ? this.href : '#',
7216 html : this.html ? this.html : ''
7226 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7230 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7236 initEvents: function() {
7238 this.el.on('click', this.onClick, this);
7241 onClick : function(e)
7243 Roo.log('PaginationItem on click ');
7244 if(this.preventDefault){
7252 this.fireEvent('click', this, e);
7268 * @class Roo.bootstrap.Slider
7269 * @extends Roo.bootstrap.Component
7270 * Bootstrap Slider class
7273 * Create a new Slider
7274 * @param {Object} config The config object
7277 Roo.bootstrap.Slider = function(config){
7278 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7281 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7283 getAutoCreate : function(){
7287 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7291 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7303 * Ext JS Library 1.1.1
7304 * Copyright(c) 2006-2007, Ext JS, LLC.
7306 * Originally Released Under LGPL - original licence link has changed is not relivant.
7309 * <script type="text/javascript">
7312 * @extends Roo.dd.DDProxy
7313 * @class Roo.grid.SplitDragZone
7314 * Support for Column Header resizing
7316 * @param {Object} config
7319 // This is a support class used internally by the Grid components
7320 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7322 this.view = grid.getView();
7323 this.proxy = this.view.resizeProxy;
7324 Roo.grid.SplitDragZone.superclass.constructor.call(
7327 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7329 dragElId : Roo.id(this.proxy.dom),
7334 this.setHandleElId(Roo.id(hd));
7335 if (hd2 !== false) {
7336 this.setOuterHandleElId(Roo.id(hd2));
7339 this.scroll = false;
7341 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7342 fly: Roo.Element.fly,
7344 b4StartDrag : function(x, y){
7345 this.view.headersDisabled = true;
7346 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7347 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7349 this.proxy.setHeight(h);
7351 // for old system colWidth really stored the actual width?
7352 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7353 // which in reality did not work.. - it worked only for fixed sizes
7354 // for resizable we need to use actual sizes.
7355 var w = this.cm.getColumnWidth(this.cellIndex);
7356 if (!this.view.mainWrap) {
7358 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7363 // this was w-this.grid.minColumnWidth;
7364 // doesnt really make sense? - w = thie curren width or the rendered one?
7365 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7366 this.resetConstraints();
7367 this.setXConstraint(minw, 1000);
7368 this.setYConstraint(0, 0);
7369 this.minX = x - minw;
7370 this.maxX = x + 1000;
7372 if (!this.view.mainWrap) { // this is Bootstrap code..
7373 this.getDragEl().style.display='block';
7376 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7380 handleMouseDown : function(e){
7381 ev = Roo.EventObject.setEvent(e);
7382 var t = this.fly(ev.getTarget());
7383 if(t.hasClass("x-grid-split")){
7384 this.cellIndex = this.view.getCellIndex(t.dom);
7386 this.cm = this.grid.colModel;
7387 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7388 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7393 endDrag : function(e){
7394 this.view.headersDisabled = false;
7395 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7396 var diff = endX - this.startPos;
7398 var w = this.cm.getColumnWidth(this.cellIndex);
7399 if (!this.view.mainWrap) {
7402 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7405 autoOffset : function(){
7410 * Ext JS Library 1.1.1
7411 * Copyright(c) 2006-2007, Ext JS, LLC.
7413 * Originally Released Under LGPL - original licence link has changed is not relivant.
7416 * <script type="text/javascript">
7420 * @class Roo.grid.AbstractSelectionModel
7421 * @extends Roo.util.Observable
7423 * Abstract base class for grid SelectionModels. It provides the interface that should be
7424 * implemented by descendant classes. This class should not be directly instantiated.
7427 Roo.grid.AbstractSelectionModel = function(){
7428 this.locked = false;
7429 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7432 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7433 /** @ignore Called by the grid automatically. Do not call directly. */
7434 init : function(grid){
7440 * Locks the selections.
7447 * Unlocks the selections.
7449 unlock : function(){
7450 this.locked = false;
7454 * Returns true if the selections are locked.
7457 isLocked : function(){
7462 * Ext JS Library 1.1.1
7463 * Copyright(c) 2006-2007, Ext JS, LLC.
7465 * Originally Released Under LGPL - original licence link has changed is not relivant.
7468 * <script type="text/javascript">
7471 * @extends Roo.grid.AbstractSelectionModel
7472 * @class Roo.grid.RowSelectionModel
7473 * The default SelectionModel used by {@link Roo.grid.Grid}.
7474 * It supports multiple selections and keyboard selection/navigation.
7476 * @param {Object} config
7478 Roo.grid.RowSelectionModel = function(config){
7479 Roo.apply(this, config);
7480 this.selections = new Roo.util.MixedCollection(false, function(o){
7485 this.lastActive = false;
7489 * @event selectionchange
7490 * Fires when the selection changes
7491 * @param {SelectionModel} this
7493 "selectionchange" : true,
7495 * @event afterselectionchange
7496 * Fires after the selection changes (eg. by key press or clicking)
7497 * @param {SelectionModel} this
7499 "afterselectionchange" : true,
7501 * @event beforerowselect
7502 * Fires when a row is selected being selected, return false to cancel.
7503 * @param {SelectionModel} this
7504 * @param {Number} rowIndex The selected index
7505 * @param {Boolean} keepExisting False if other selections will be cleared
7507 "beforerowselect" : true,
7510 * Fires when a row is selected.
7511 * @param {SelectionModel} this
7512 * @param {Number} rowIndex The selected index
7513 * @param {Roo.data.Record} r The record
7517 * @event rowdeselect
7518 * Fires when a row is deselected.
7519 * @param {SelectionModel} this
7520 * @param {Number} rowIndex The selected index
7522 "rowdeselect" : true
7524 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7525 this.locked = false;
7528 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7530 * @cfg {Boolean} singleSelect
7531 * True to allow selection of only one row at a time (defaults to false)
7533 singleSelect : false,
7536 initEvents : function(){
7538 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7539 this.grid.on("mousedown", this.handleMouseDown, this);
7540 }else{ // allow click to work like normal
7541 this.grid.on("rowclick", this.handleDragableRowClick, this);
7543 // bootstrap does not have a view..
7544 var view = this.grid.view ? this.grid.view : this.grid;
7545 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7548 this.selectPrevious(e.shiftKey);
7549 }else if(this.last !== false && this.lastActive !== false){
7550 var last = this.last;
7551 this.selectRange(this.last, this.lastActive-1);
7552 view.focusRow(this.lastActive);
7557 this.selectFirstRow();
7559 this.fireEvent("afterselectionchange", this);
7561 "down" : function(e){
7563 this.selectNext(e.shiftKey);
7564 }else if(this.last !== false && this.lastActive !== false){
7565 var last = this.last;
7566 this.selectRange(this.last, this.lastActive+1);
7567 view.focusRow(this.lastActive);
7572 this.selectFirstRow();
7574 this.fireEvent("afterselectionchange", this);
7580 view.on("refresh", this.onRefresh, this);
7581 view.on("rowupdated", this.onRowUpdated, this);
7582 view.on("rowremoved", this.onRemove, this);
7586 onRefresh : function(){
7587 var ds = this.grid.ds, i, v = this.grid.view;
7588 var s = this.selections;
7590 if((i = ds.indexOfId(r.id)) != -1){
7592 s.add(ds.getAt(i)); // updating the selection relate data
7600 onRemove : function(v, index, r){
7601 this.selections.remove(r);
7605 onRowUpdated : function(v, index, r){
7606 if(this.isSelected(r)){
7607 v.onRowSelect(index);
7613 * @param {Array} records The records to select
7614 * @param {Boolean} keepExisting (optional) True to keep existing selections
7616 selectRecords : function(records, keepExisting){
7618 this.clearSelections();
7620 var ds = this.grid.ds;
7621 for(var i = 0, len = records.length; i < len; i++){
7622 this.selectRow(ds.indexOf(records[i]), true);
7627 * Gets the number of selected rows.
7630 getCount : function(){
7631 return this.selections.length;
7635 * Selects the first row in the grid.
7637 selectFirstRow : function(){
7642 * Select the last row.
7643 * @param {Boolean} keepExisting (optional) True to keep existing selections
7645 selectLastRow : function(keepExisting){
7646 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7650 * Selects the row immediately following the last selected row.
7651 * @param {Boolean} keepExisting (optional) True to keep existing selections
7653 selectNext : function(keepExisting){
7654 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7655 this.selectRow(this.last+1, keepExisting);
7656 var view = this.grid.view ? this.grid.view : this.grid;
7657 view.focusRow(this.last);
7662 * Selects the row that precedes the last selected row.
7663 * @param {Boolean} keepExisting (optional) True to keep existing selections
7665 selectPrevious : function(keepExisting){
7667 this.selectRow(this.last-1, keepExisting);
7668 var view = this.grid.view ? this.grid.view : this.grid;
7669 view.focusRow(this.last);
7674 * Returns the selected records
7675 * @return {Array} Array of selected records
7677 getSelections : function(){
7678 return [].concat(this.selections.items);
7682 * Returns the first selected record.
7685 getSelected : function(){
7686 return this.selections.itemAt(0);
7691 * Clears all selections.
7693 clearSelections : function(fast){
7698 var ds = this.grid.ds;
7699 var s = this.selections;
7701 this.deselectRow(ds.indexOfId(r.id));
7705 this.selections.clear();
7714 selectAll : function(){
7718 this.selections.clear();
7719 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7720 this.selectRow(i, true);
7725 * Returns True if there is a selection.
7728 hasSelection : function(){
7729 return this.selections.length > 0;
7733 * Returns True if the specified row is selected.
7734 * @param {Number/Record} record The record or index of the record to check
7737 isSelected : function(index){
7738 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7739 return (r && this.selections.key(r.id) ? true : false);
7743 * Returns True if the specified record id is selected.
7744 * @param {String} id The id of record to check
7747 isIdSelected : function(id){
7748 return (this.selections.key(id) ? true : false);
7752 handleMouseDown : function(e, t)
7754 var view = this.grid.view ? this.grid.view : this.grid;
7756 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7759 if(e.shiftKey && this.last !== false){
7760 var last = this.last;
7761 this.selectRange(last, rowIndex, e.ctrlKey);
7762 this.last = last; // reset the last
7763 view.focusRow(rowIndex);
7765 var isSelected = this.isSelected(rowIndex);
7766 if(e.button !== 0 && isSelected){
7767 view.focusRow(rowIndex);
7768 }else if(e.ctrlKey && isSelected){
7769 this.deselectRow(rowIndex);
7770 }else if(!isSelected){
7771 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7772 view.focusRow(rowIndex);
7775 this.fireEvent("afterselectionchange", this);
7778 handleDragableRowClick : function(grid, rowIndex, e)
7780 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7781 this.selectRow(rowIndex, false);
7782 var view = this.grid.view ? this.grid.view : this.grid;
7783 view.focusRow(rowIndex);
7784 this.fireEvent("afterselectionchange", this);
7789 * Selects multiple rows.
7790 * @param {Array} rows Array of the indexes of the row to select
7791 * @param {Boolean} keepExisting (optional) True to keep existing selections
7793 selectRows : function(rows, keepExisting){
7795 this.clearSelections();
7797 for(var i = 0, len = rows.length; i < len; i++){
7798 this.selectRow(rows[i], true);
7803 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7804 * @param {Number} startRow The index of the first row in the range
7805 * @param {Number} endRow The index of the last row in the range
7806 * @param {Boolean} keepExisting (optional) True to retain existing selections
7808 selectRange : function(startRow, endRow, keepExisting){
7813 this.clearSelections();
7815 if(startRow <= endRow){
7816 for(var i = startRow; i <= endRow; i++){
7817 this.selectRow(i, true);
7820 for(var i = startRow; i >= endRow; i--){
7821 this.selectRow(i, true);
7827 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7828 * @param {Number} startRow The index of the first row in the range
7829 * @param {Number} endRow The index of the last row in the range
7831 deselectRange : function(startRow, endRow, preventViewNotify){
7835 for(var i = startRow; i <= endRow; i++){
7836 this.deselectRow(i, preventViewNotify);
7842 * @param {Number} row The index of the row to select
7843 * @param {Boolean} keepExisting (optional) True to keep existing selections
7845 selectRow : function(index, keepExisting, preventViewNotify){
7846 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7849 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7850 if(!keepExisting || this.singleSelect){
7851 this.clearSelections();
7853 var r = this.grid.ds.getAt(index);
7854 this.selections.add(r);
7855 this.last = this.lastActive = index;
7856 if(!preventViewNotify){
7857 var view = this.grid.view ? this.grid.view : this.grid;
7858 view.onRowSelect(index);
7860 this.fireEvent("rowselect", this, index, r);
7861 this.fireEvent("selectionchange", this);
7867 * @param {Number} row The index of the row to deselect
7869 deselectRow : function(index, preventViewNotify){
7873 if(this.last == index){
7876 if(this.lastActive == index){
7877 this.lastActive = false;
7879 var r = this.grid.ds.getAt(index);
7880 this.selections.remove(r);
7881 if(!preventViewNotify){
7882 var view = this.grid.view ? this.grid.view : this.grid;
7883 view.onRowDeselect(index);
7885 this.fireEvent("rowdeselect", this, index);
7886 this.fireEvent("selectionchange", this);
7890 restoreLast : function(){
7892 this.last = this._last;
7897 acceptsNav : function(row, col, cm){
7898 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7902 onEditorKey : function(field, e){
7903 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7908 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7910 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7912 }else if(k == e.ENTER && !e.ctrlKey){
7916 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7918 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7920 }else if(k == e.ESC){
7924 g.startEditing(newCell[0], newCell[1]);
7929 * Ext JS Library 1.1.1
7930 * Copyright(c) 2006-2007, Ext JS, LLC.
7932 * Originally Released Under LGPL - original licence link has changed is not relivant.
7935 * <script type="text/javascript">
7940 * @class Roo.grid.ColumnModel
7941 * @extends Roo.util.Observable
7942 * This is the default implementation of a ColumnModel used by the Grid. It defines
7943 * the columns in the grid.
7946 var colModel = new Roo.grid.ColumnModel([
7947 {header: "Ticker", width: 60, sortable: true, locked: true},
7948 {header: "Company Name", width: 150, sortable: true},
7949 {header: "Market Cap.", width: 100, sortable: true},
7950 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7951 {header: "Employees", width: 100, sortable: true, resizable: false}
7956 * The config options listed for this class are options which may appear in each
7957 * individual column definition.
7958 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7960 * @param {Object} config An Array of column config objects. See this class's
7961 * config objects for details.
7963 Roo.grid.ColumnModel = function(config){
7965 * The config passed into the constructor
7967 this.config = []; //config;
7970 // if no id, create one
7971 // if the column does not have a dataIndex mapping,
7972 // map it to the order it is in the config
7973 for(var i = 0, len = config.length; i < len; i++){
7974 this.addColumn(config[i]);
7979 * The width of columns which have no width specified (defaults to 100)
7982 this.defaultWidth = 100;
7985 * Default sortable of columns which have no sortable specified (defaults to false)
7988 this.defaultSortable = false;
7992 * @event widthchange
7993 * Fires when the width of a column changes.
7994 * @param {ColumnModel} this
7995 * @param {Number} columnIndex The column index
7996 * @param {Number} newWidth The new width
7998 "widthchange": true,
8000 * @event headerchange
8001 * Fires when the text of a header changes.
8002 * @param {ColumnModel} this
8003 * @param {Number} columnIndex The column index
8004 * @param {Number} newText The new header text
8006 "headerchange": true,
8008 * @event hiddenchange
8009 * Fires when a column is hidden or "unhidden".
8010 * @param {ColumnModel} this
8011 * @param {Number} columnIndex The column index
8012 * @param {Boolean} hidden true if hidden, false otherwise
8014 "hiddenchange": true,
8016 * @event columnmoved
8017 * Fires when a column is moved.
8018 * @param {ColumnModel} this
8019 * @param {Number} oldIndex
8020 * @param {Number} newIndex
8022 "columnmoved" : true,
8024 * @event columlockchange
8025 * Fires when a column's locked state is changed
8026 * @param {ColumnModel} this
8027 * @param {Number} colIndex
8028 * @param {Boolean} locked true if locked
8030 "columnlockchange" : true
8032 Roo.grid.ColumnModel.superclass.constructor.call(this);
8034 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8036 * @cfg {String} header The header text to display in the Grid view.
8039 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8042 * @cfg {String} smHeader Header at Bootsrap Small width
8045 * @cfg {String} mdHeader Header at Bootsrap Medium width
8048 * @cfg {String} lgHeader Header at Bootsrap Large width
8051 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8054 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8055 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8056 * specified, the column's index is used as an index into the Record's data Array.
8059 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8060 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8063 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8064 * Defaults to the value of the {@link #defaultSortable} property.
8065 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8068 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8071 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8074 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8077 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8080 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8081 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8082 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8083 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8086 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8089 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8092 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8095 * @cfg {String} cursor (Optional)
8098 * @cfg {String} tooltip (Optional)
8101 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8104 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8107 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8110 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8113 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8116 * Returns the id of the column at the specified index.
8117 * @param {Number} index The column index
8118 * @return {String} the id
8120 getColumnId : function(index){
8121 return this.config[index].id;
8125 * Returns the column for a specified id.
8126 * @param {String} id The column id
8127 * @return {Object} the column
8129 getColumnById : function(id){
8130 return this.lookup[id];
8135 * Returns the column Object for a specified dataIndex.
8136 * @param {String} dataIndex The column dataIndex
8137 * @return {Object|Boolean} the column or false if not found
8139 getColumnByDataIndex: function(dataIndex){
8140 var index = this.findColumnIndex(dataIndex);
8141 return index > -1 ? this.config[index] : false;
8145 * Returns the index for a specified column id.
8146 * @param {String} id The column id
8147 * @return {Number} the index, or -1 if not found
8149 getIndexById : function(id){
8150 for(var i = 0, len = this.config.length; i < len; i++){
8151 if(this.config[i].id == id){
8159 * Returns the index for a specified column dataIndex.
8160 * @param {String} dataIndex The column dataIndex
8161 * @return {Number} the index, or -1 if not found
8164 findColumnIndex : function(dataIndex){
8165 for(var i = 0, len = this.config.length; i < len; i++){
8166 if(this.config[i].dataIndex == dataIndex){
8174 moveColumn : function(oldIndex, newIndex){
8175 var c = this.config[oldIndex];
8176 this.config.splice(oldIndex, 1);
8177 this.config.splice(newIndex, 0, c);
8178 this.dataMap = null;
8179 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8182 isLocked : function(colIndex){
8183 return this.config[colIndex].locked === true;
8186 setLocked : function(colIndex, value, suppressEvent){
8187 if(this.isLocked(colIndex) == value){
8190 this.config[colIndex].locked = value;
8192 this.fireEvent("columnlockchange", this, colIndex, value);
8196 getTotalLockedWidth : function(){
8198 for(var i = 0; i < this.config.length; i++){
8199 if(this.isLocked(i) && !this.isHidden(i)){
8200 this.totalWidth += this.getColumnWidth(i);
8206 getLockedCount : function(){
8207 for(var i = 0, len = this.config.length; i < len; i++){
8208 if(!this.isLocked(i)){
8213 return this.config.length;
8217 * Returns the number of columns.
8220 getColumnCount : function(visibleOnly){
8221 if(visibleOnly === true){
8223 for(var i = 0, len = this.config.length; i < len; i++){
8224 if(!this.isHidden(i)){
8230 return this.config.length;
8234 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8235 * @param {Function} fn
8236 * @param {Object} scope (optional)
8237 * @return {Array} result
8239 getColumnsBy : function(fn, scope){
8241 for(var i = 0, len = this.config.length; i < len; i++){
8242 var c = this.config[i];
8243 if(fn.call(scope||this, c, i) === true){
8251 * Returns true if the specified column is sortable.
8252 * @param {Number} col The column index
8255 isSortable : function(col){
8256 if(typeof this.config[col].sortable == "undefined"){
8257 return this.defaultSortable;
8259 return this.config[col].sortable;
8263 * Returns the rendering (formatting) function defined for the column.
8264 * @param {Number} col The column index.
8265 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8267 getRenderer : function(col){
8268 if(!this.config[col].renderer){
8269 return Roo.grid.ColumnModel.defaultRenderer;
8271 return this.config[col].renderer;
8275 * Sets the rendering (formatting) function for a column.
8276 * @param {Number} col The column index
8277 * @param {Function} fn The function to use to process the cell's raw data
8278 * to return HTML markup for the grid view. The render function is called with
8279 * the following parameters:<ul>
8280 * <li>Data value.</li>
8281 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8282 * <li>css A CSS style string to apply to the table cell.</li>
8283 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8284 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8285 * <li>Row index</li>
8286 * <li>Column index</li>
8287 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8289 setRenderer : function(col, fn){
8290 this.config[col].renderer = fn;
8294 * Returns the width for the specified column.
8295 * @param {Number} col The column index
8296 * @param (optional) {String} gridSize bootstrap width size.
8299 getColumnWidth : function(col, gridSize)
8301 var cfg = this.config[col];
8303 if (typeof(gridSize) == 'undefined') {
8304 return cfg.width * 1 || this.defaultWidth;
8306 if (gridSize === false) { // if we set it..
8307 return cfg.width || false;
8309 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8311 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8312 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8315 return cfg[ sizes[i] ];
8322 * Sets the width for a column.
8323 * @param {Number} col The column index
8324 * @param {Number} width The new width
8326 setColumnWidth : function(col, width, suppressEvent){
8327 this.config[col].width = width;
8328 this.totalWidth = null;
8330 this.fireEvent("widthchange", this, col, width);
8335 * Returns the total width of all columns.
8336 * @param {Boolean} includeHidden True to include hidden column widths
8339 getTotalWidth : function(includeHidden){
8340 if(!this.totalWidth){
8341 this.totalWidth = 0;
8342 for(var i = 0, len = this.config.length; i < len; i++){
8343 if(includeHidden || !this.isHidden(i)){
8344 this.totalWidth += this.getColumnWidth(i);
8348 return this.totalWidth;
8352 * Returns the header for the specified column.
8353 * @param {Number} col The column index
8356 getColumnHeader : function(col){
8357 return this.config[col].header;
8361 * Sets the header for a column.
8362 * @param {Number} col The column index
8363 * @param {String} header The new header
8365 setColumnHeader : function(col, header){
8366 this.config[col].header = header;
8367 this.fireEvent("headerchange", this, col, header);
8371 * Returns the tooltip for the specified column.
8372 * @param {Number} col The column index
8375 getColumnTooltip : function(col){
8376 return this.config[col].tooltip;
8379 * Sets the tooltip for a column.
8380 * @param {Number} col The column index
8381 * @param {String} tooltip The new tooltip
8383 setColumnTooltip : function(col, tooltip){
8384 this.config[col].tooltip = tooltip;
8388 * Returns the dataIndex for the specified column.
8389 * @param {Number} col The column index
8392 getDataIndex : function(col){
8393 return this.config[col].dataIndex;
8397 * Sets the dataIndex for a column.
8398 * @param {Number} col The column index
8399 * @param {Number} dataIndex The new dataIndex
8401 setDataIndex : function(col, dataIndex){
8402 this.config[col].dataIndex = dataIndex;
8408 * Returns true if the cell is editable.
8409 * @param {Number} colIndex The column index
8410 * @param {Number} rowIndex The row index - this is nto actually used..?
8413 isCellEditable : function(colIndex, rowIndex){
8414 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8418 * Returns the editor defined for the cell/column.
8419 * return false or null to disable editing.
8420 * @param {Number} colIndex The column index
8421 * @param {Number} rowIndex The row index
8424 getCellEditor : function(colIndex, rowIndex){
8425 return this.config[colIndex].editor;
8429 * Sets if a column is editable.
8430 * @param {Number} col The column index
8431 * @param {Boolean} editable True if the column is editable
8433 setEditable : function(col, editable){
8434 this.config[col].editable = editable;
8439 * Returns true if the column is hidden.
8440 * @param {Number} colIndex The column index
8443 isHidden : function(colIndex){
8444 return this.config[colIndex].hidden;
8449 * Returns true if the column width cannot be changed
8451 isFixed : function(colIndex){
8452 return this.config[colIndex].fixed;
8456 * Returns true if the column can be resized
8459 isResizable : function(colIndex){
8460 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8463 * Sets if a column is hidden.
8464 * @param {Number} colIndex The column index
8465 * @param {Boolean} hidden True if the column is hidden
8467 setHidden : function(colIndex, hidden){
8468 this.config[colIndex].hidden = hidden;
8469 this.totalWidth = null;
8470 this.fireEvent("hiddenchange", this, colIndex, hidden);
8474 * Sets the editor for a column.
8475 * @param {Number} col The column index
8476 * @param {Object} editor The editor object
8478 setEditor : function(col, editor){
8479 this.config[col].editor = editor;
8482 * Add a column (experimental...) - defaults to adding to the end..
8483 * @param {Object} config
8485 addColumn : function(c)
8488 var i = this.config.length;
8491 if(typeof c.dataIndex == "undefined"){
8494 if(typeof c.renderer == "string"){
8495 c.renderer = Roo.util.Format[c.renderer];
8497 if(typeof c.id == "undefined"){
8500 if(c.editor && c.editor.xtype){
8501 c.editor = Roo.factory(c.editor, Roo.grid);
8503 if(c.editor && c.editor.isFormField){
8504 c.editor = new Roo.grid.GridEditor(c.editor);
8506 this.lookup[c.id] = c;
8511 Roo.grid.ColumnModel.defaultRenderer = function(value)
8513 if(typeof value == "object") {
8516 if(typeof value == "string" && value.length < 1){
8520 return String.format("{0}", value);
8523 // Alias for backwards compatibility
8524 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8527 * Ext JS Library 1.1.1
8528 * Copyright(c) 2006-2007, Ext JS, LLC.
8530 * Originally Released Under LGPL - original licence link has changed is not relivant.
8533 * <script type="text/javascript">
8537 * @class Roo.LoadMask
8538 * A simple utility class for generically masking elements while loading data. If the element being masked has
8539 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8540 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8541 * element's UpdateManager load indicator and will be destroyed after the initial load.
8543 * Create a new LoadMask
8544 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8545 * @param {Object} config The config object
8547 Roo.LoadMask = function(el, config){
8548 this.el = Roo.get(el);
8549 Roo.apply(this, config);
8551 this.store.on('beforeload', this.onBeforeLoad, this);
8552 this.store.on('load', this.onLoad, this);
8553 this.store.on('loadexception', this.onLoadException, this);
8554 this.removeMask = false;
8556 var um = this.el.getUpdateManager();
8557 um.showLoadIndicator = false; // disable the default indicator
8558 um.on('beforeupdate', this.onBeforeLoad, this);
8559 um.on('update', this.onLoad, this);
8560 um.on('failure', this.onLoad, this);
8561 this.removeMask = true;
8565 Roo.LoadMask.prototype = {
8567 * @cfg {Boolean} removeMask
8568 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8569 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8574 * The text to display in a centered loading message box (defaults to 'Loading...')
8578 * @cfg {String} msgCls
8579 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8581 msgCls : 'x-mask-loading',
8584 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8590 * Disables the mask to prevent it from being displayed
8592 disable : function(){
8593 this.disabled = true;
8597 * Enables the mask so that it can be displayed
8599 enable : function(){
8600 this.disabled = false;
8603 onLoadException : function()
8607 if (typeof(arguments[3]) != 'undefined') {
8608 Roo.MessageBox.alert("Error loading",arguments[3]);
8612 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8613 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8620 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8625 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8629 onBeforeLoad : function(){
8631 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8636 destroy : function(){
8638 this.store.un('beforeload', this.onBeforeLoad, this);
8639 this.store.un('load', this.onLoad, this);
8640 this.store.un('loadexception', this.onLoadException, this);
8642 var um = this.el.getUpdateManager();
8643 um.un('beforeupdate', this.onBeforeLoad, this);
8644 um.un('update', this.onLoad, this);
8645 um.un('failure', this.onLoad, this);
8649 * @class Roo.bootstrap.Table
8651 * @extends Roo.bootstrap.Component
8652 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8653 * Similar to Roo.grid.Grid
8655 var table = Roo.factory({
8657 xns : Roo.bootstrap,
8658 autoSizeColumns: true,
8665 sortInfo : { direction : 'ASC', field: 'name' },
8667 xtype : 'HttpProxy',
8670 url : 'https://example.com/some.data.url.json'
8673 xtype : 'JsonReader',
8675 fields : [ 'id', 'name', whatever' ],
8682 xtype : 'ColumnModel',
8686 dataIndex : 'is_in_group',
8689 renderer : function(v, x , r) {
8691 return String.format("{0}", v)
8697 xtype : 'RowSelectionModel',
8698 xns : Roo.bootstrap.Table
8699 // you can add listeners to catch selection change here....
8705 grid.render(Roo.get("some-div"));
8708 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8713 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8714 * @cfg {Roo.data.Store} store The data store to use
8715 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8717 * @cfg {String} cls table class
8720 * @cfg {boolean} striped Should the rows be alternative striped
8721 * @cfg {boolean} bordered Add borders to the table
8722 * @cfg {boolean} hover Add hover highlighting
8723 * @cfg {boolean} condensed Format condensed
8724 * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
8725 * also adds table-responsive (see bootstrap docs for details)
8726 * @cfg {Boolean} loadMask (true|false) default false
8727 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8728 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8729 * @cfg {Boolean} rowSelection (true|false) default false
8730 * @cfg {Boolean} cellSelection (true|false) default false
8731 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8732 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8733 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8734 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8735 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8736 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8739 * Create a new Table
8740 * @param {Object} config The config object
8743 Roo.bootstrap.Table = function(config)
8745 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8748 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8749 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8750 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8751 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8753 this.view = this; // compat with grid.
8755 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8757 this.sm.grid = this;
8758 this.selModel = Roo.factory(this.sm, Roo.grid);
8759 this.sm = this.selModel;
8760 this.sm.xmodule = this.xmodule || false;
8763 if (this.cm && typeof(this.cm.config) == 'undefined') {
8764 this.colModel = new Roo.grid.ColumnModel(this.cm);
8765 this.cm = this.colModel;
8766 this.cm.xmodule = this.xmodule || false;
8769 this.store= Roo.factory(this.store, Roo.data);
8770 this.ds = this.store;
8771 this.ds.xmodule = this.xmodule || false;
8774 if (this.footer && this.store) {
8775 this.footer.dataSource = this.ds;
8776 this.footer = Roo.factory(this.footer);
8783 * Fires when a cell is clicked
8784 * @param {Roo.bootstrap.Table} this
8785 * @param {Roo.Element} el
8786 * @param {Number} rowIndex
8787 * @param {Number} columnIndex
8788 * @param {Roo.EventObject} e
8792 * @event celldblclick
8793 * Fires when a cell is double clicked
8794 * @param {Roo.bootstrap.Table} this
8795 * @param {Roo.Element} el
8796 * @param {Number} rowIndex
8797 * @param {Number} columnIndex
8798 * @param {Roo.EventObject} e
8800 "celldblclick" : true,
8803 * Fires when a row is clicked
8804 * @param {Roo.bootstrap.Table} this
8805 * @param {Roo.Element} el
8806 * @param {Number} rowIndex
8807 * @param {Roo.EventObject} e
8811 * @event rowdblclick
8812 * Fires when a row is double clicked
8813 * @param {Roo.bootstrap.Table} this
8814 * @param {Roo.Element} el
8815 * @param {Number} rowIndex
8816 * @param {Roo.EventObject} e
8818 "rowdblclick" : true,
8821 * Fires when a mouseover occur
8822 * @param {Roo.bootstrap.Table} this
8823 * @param {Roo.Element} el
8824 * @param {Number} rowIndex
8825 * @param {Number} columnIndex
8826 * @param {Roo.EventObject} e
8831 * Fires when a mouseout occur
8832 * @param {Roo.bootstrap.Table} this
8833 * @param {Roo.Element} el
8834 * @param {Number} rowIndex
8835 * @param {Number} columnIndex
8836 * @param {Roo.EventObject} e
8841 * Fires when a row is rendered, so you can change add a style to it.
8842 * @param {Roo.bootstrap.Table} this
8843 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8847 * @event rowsrendered
8848 * Fires when all the rows have been rendered
8849 * @param {Roo.bootstrap.Table} this
8851 'rowsrendered' : true,
8853 * @event contextmenu
8854 * The raw contextmenu event for the entire grid.
8855 * @param {Roo.EventObject} e
8857 "contextmenu" : true,
8859 * @event rowcontextmenu
8860 * Fires when a row is right clicked
8861 * @param {Roo.bootstrap.Table} this
8862 * @param {Number} rowIndex
8863 * @param {Roo.EventObject} e
8865 "rowcontextmenu" : true,
8867 * @event cellcontextmenu
8868 * Fires when a cell is right clicked
8869 * @param {Roo.bootstrap.Table} this
8870 * @param {Number} rowIndex
8871 * @param {Number} cellIndex
8872 * @param {Roo.EventObject} e
8874 "cellcontextmenu" : true,
8876 * @event headercontextmenu
8877 * Fires when a header is right clicked
8878 * @param {Roo.bootstrap.Table} this
8879 * @param {Number} columnIndex
8880 * @param {Roo.EventObject} e
8882 "headercontextmenu" : true,
8885 * The raw mousedown event for the entire grid.
8886 * @param {Roo.EventObject} e
8893 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8909 enableColumnResize: true,
8911 rowSelection : false,
8912 cellSelection : false,
8915 minColumnWidth : 50,
8917 // Roo.Element - the tbody
8918 bodyEl: false, // <tbody> Roo.Element - thead element
8919 headEl: false, // <thead> Roo.Element - thead element
8920 resizeProxy : false, // proxy element for dragging?
8924 container: false, // used by gridpanel...
8930 auto_hide_footer : false,
8932 view: false, // actually points to this..
8934 getAutoCreate : function()
8936 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8943 // this get's auto added by panel.Grid
8944 if (this.scrollBody) {
8945 cfg.cls += ' table-body-fixed';
8948 cfg.cls += ' table-striped';
8952 cfg.cls += ' table-hover';
8954 if (this.bordered) {
8955 cfg.cls += ' table-bordered';
8957 if (this.condensed) {
8958 cfg.cls += ' table-condensed';
8961 if (this.responsive) {
8962 cfg.cls += ' table-responsive';
8966 cfg.cls+= ' ' +this.cls;
8972 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8975 if(this.store || this.cm){
8976 if(this.headerShow){
8977 cfg.cn.push(this.renderHeader());
8980 cfg.cn.push(this.renderBody());
8982 if(this.footerShow){
8983 cfg.cn.push(this.renderFooter());
8985 // where does this come from?
8986 //cfg.cls+= ' TableGrid';
8989 return { cn : [ cfg ] };
8992 initEvents : function()
8994 if(!this.store || !this.cm){
8997 if (this.selModel) {
8998 this.selModel.initEvents();
9002 //Roo.log('initEvents with ds!!!!');
9004 this.bodyEl = this.el.select('tbody', true).first();
9005 this.headEl = this.el.select('thead', true).first();
9006 this.mainFoot = this.el.select('tfoot', true).first();
9011 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9012 e.on('click', this.sort, this);
9016 // why is this done????? = it breaks dialogs??
9017 //this.parent().el.setStyle('position', 'relative');
9021 this.footer.parentId = this.id;
9022 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9025 this.el.select('tfoot tr td').first().addClass('hide');
9030 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9033 this.store.on('load', this.onLoad, this);
9034 this.store.on('beforeload', this.onBeforeLoad, this);
9035 this.store.on('update', this.onUpdate, this);
9036 this.store.on('add', this.onAdd, this);
9037 this.store.on("clear", this.clear, this);
9039 this.el.on("contextmenu", this.onContextMenu, this);
9042 this.cm.on("headerchange", this.onHeaderChange, this);
9043 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9045 //?? does bodyEl get replaced on render?
9046 this.bodyEl.on("click", this.onClick, this);
9047 this.bodyEl.on("dblclick", this.onDblClick, this);
9048 this.bodyEl.on('scroll', this.onBodyScroll, this);
9050 // guessing mainbody will work - this relays usually caught by selmodel at present.
9051 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9054 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9057 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9058 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9063 // Compatibility with grid - we implement all the view features at present.
9064 getView : function()
9069 initCSS : function()
9073 var cm = this.cm, styles = [];
9074 this.CSS.removeStyleSheet(this.id + '-cssrules');
9075 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9076 // we can honour xs/sm/md/xl as widths...
9077 // we first have to decide what widht we are currently at...
9078 var sz = Roo.getGridSize();
9082 var cols = []; // visable cols.
9084 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9085 var w = cm.getColumnWidth(i, false);
9087 cols.push( { rel : false, abs : 0 });
9091 cols.push( { rel : false, abs : w });
9093 last = i; // not really..
9096 var w = cm.getColumnWidth(i, sz);
9101 cols.push( { rel : w, abs : false });
9104 var avail = this.bodyEl.dom.clientWidth - total_abs;
9106 var unitWidth = Math.floor(avail / total);
9107 var rem = avail - (unitWidth * total);
9109 var hidden, width, pos = 0 , splithide , left;
9110 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9112 hidden = 'display:none;';
9114 width = 'width:0px;';
9116 if(!cm.isHidden(i)){
9120 // we can honour xs/sm/md/xl ?
9121 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9123 hidden = 'display:none;';
9125 // width should return a small number...
9127 w+=rem; // add the remaining with..
9130 left = "left:" + (pos -4) + "px;";
9131 width = "width:" + w+ "px;";
9134 if (this.responsive) {
9137 hidden = cm.isHidden(i) ? 'display:none;' : '';
9138 splithide = 'display: none;';
9141 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9144 splithide = 'display:none;';
9147 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9148 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9153 //Roo.log(styles.join(''));
9154 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9160 onContextMenu : function(e, t)
9162 this.processEvent("contextmenu", e);
9165 processEvent : function(name, e)
9167 if (name != 'touchstart' ) {
9168 this.fireEvent(name, e);
9171 var t = e.getTarget();
9173 var cell = Roo.get(t);
9179 if(cell.findParent('tfoot', false, true)){
9183 if(cell.findParent('thead', false, true)){
9185 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9186 cell = Roo.get(t).findParent('th', false, true);
9188 Roo.log("failed to find th in thead?");
9189 Roo.log(e.getTarget());
9194 var cellIndex = cell.dom.cellIndex;
9196 var ename = name == 'touchstart' ? 'click' : name;
9197 this.fireEvent("header" + ename, this, cellIndex, e);
9202 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9203 cell = Roo.get(t).findParent('td', false, true);
9205 Roo.log("failed to find th in tbody?");
9206 Roo.log(e.getTarget());
9211 var row = cell.findParent('tr', false, true);
9212 var cellIndex = cell.dom.cellIndex;
9213 var rowIndex = row.dom.rowIndex - 1;
9217 this.fireEvent("row" + name, this, rowIndex, e);
9221 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9227 onMouseover : function(e, el)
9229 var cell = Roo.get(el);
9235 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9236 cell = cell.findParent('td', false, true);
9239 var row = cell.findParent('tr', false, true);
9240 var cellIndex = cell.dom.cellIndex;
9241 var rowIndex = row.dom.rowIndex - 1; // start from 0
9243 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9247 onMouseout : function(e, el)
9249 var cell = Roo.get(el);
9255 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9256 cell = cell.findParent('td', false, true);
9259 var row = cell.findParent('tr', false, true);
9260 var cellIndex = cell.dom.cellIndex;
9261 var rowIndex = row.dom.rowIndex - 1; // start from 0
9263 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9267 onClick : function(e, el)
9269 var cell = Roo.get(el);
9271 if(!cell || (!this.cellSelection && !this.rowSelection)){
9275 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9276 cell = cell.findParent('td', false, true);
9279 if(!cell || typeof(cell) == 'undefined'){
9283 var row = cell.findParent('tr', false, true);
9285 if(!row || typeof(row) == 'undefined'){
9289 var cellIndex = cell.dom.cellIndex;
9290 var rowIndex = this.getRowIndex(row);
9292 // why??? - should these not be based on SelectionModel?
9293 //if(this.cellSelection){
9294 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9297 //if(this.rowSelection){
9298 this.fireEvent('rowclick', this, row, rowIndex, e);
9303 onDblClick : function(e,el)
9305 var cell = Roo.get(el);
9307 if(!cell || (!this.cellSelection && !this.rowSelection)){
9311 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9312 cell = cell.findParent('td', false, true);
9315 if(!cell || typeof(cell) == 'undefined'){
9319 var row = cell.findParent('tr', false, true);
9321 if(!row || typeof(row) == 'undefined'){
9325 var cellIndex = cell.dom.cellIndex;
9326 var rowIndex = this.getRowIndex(row);
9328 if(this.cellSelection){
9329 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9332 if(this.rowSelection){
9333 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9336 findRowIndex : function(el)
9338 var cell = Roo.get(el);
9342 var row = cell.findParent('tr', false, true);
9344 if(!row || typeof(row) == 'undefined'){
9347 return this.getRowIndex(row);
9349 sort : function(e,el)
9351 var col = Roo.get(el);
9353 if(!col.hasClass('sortable')){
9357 var sort = col.attr('sort');
9360 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9364 this.store.sortInfo = {field : sort, direction : dir};
9367 Roo.log("calling footer first");
9368 this.footer.onClick('first');
9371 this.store.load({ params : { start : 0 } });
9375 renderHeader : function()
9383 this.totalWidth = 0;
9385 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9387 var config = cm.config[i];
9391 cls : 'x-hcol-' + i,
9394 html: cm.getColumnHeader(i)
9397 var tooltip = cm.getColumnTooltip(i);
9399 c.tooltip = tooltip;
9405 if(typeof(config.sortable) != 'undefined' && config.sortable){
9406 c.cls += ' sortable';
9407 c.html = '<i class="fa"></i>' + c.html;
9410 // could use BS4 hidden-..-down
9412 if(typeof(config.lgHeader) != 'undefined'){
9413 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9416 if(typeof(config.mdHeader) != 'undefined'){
9417 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9420 if(typeof(config.smHeader) != 'undefined'){
9421 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9424 if(typeof(config.xsHeader) != 'undefined'){
9425 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9432 if(typeof(config.tooltip) != 'undefined'){
9433 c.tooltip = config.tooltip;
9436 if(typeof(config.colspan) != 'undefined'){
9437 c.colspan = config.colspan;
9440 // hidden is handled by CSS now
9442 if(typeof(config.dataIndex) != 'undefined'){
9443 c.sort = config.dataIndex;
9448 if(typeof(config.align) != 'undefined' && config.align.length){
9449 c.style += ' text-align:' + config.align + ';';
9452 /* width is done in CSS
9453 *if(typeof(config.width) != 'undefined'){
9454 c.style += ' width:' + config.width + 'px;';
9455 this.totalWidth += config.width;
9457 this.totalWidth += 100; // assume minimum of 100 per column?
9461 if(typeof(config.cls) != 'undefined'){
9462 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9464 // this is the bit that doesnt reall work at all...
9466 if (this.responsive) {
9469 ['xs','sm','md','lg'].map(function(size){
9471 if(typeof(config[size]) == 'undefined'){
9475 if (!config[size]) { // 0 = hidden
9476 // BS 4 '0' is treated as hide that column and below.
9477 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9481 c.cls += ' col-' + size + '-' + config[size] + (
9482 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9490 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9501 renderBody : function()
9511 colspan : this.cm.getColumnCount()
9521 renderFooter : function()
9531 colspan : this.cm.getColumnCount()
9545 // Roo.log('ds onload');
9550 var ds = this.store;
9552 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9553 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9554 if (_this.store.sortInfo) {
9556 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9557 e.select('i', true).addClass(['fa-arrow-up']);
9560 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9561 e.select('i', true).addClass(['fa-arrow-down']);
9566 var tbody = this.bodyEl;
9568 if(ds.getCount() > 0){
9569 ds.data.each(function(d,rowIndex){
9570 var row = this.renderRow(cm, ds, rowIndex);
9572 tbody.createChild(row);
9576 if(row.cellObjects.length){
9577 Roo.each(row.cellObjects, function(r){
9578 _this.renderCellObject(r);
9585 var tfoot = this.el.select('tfoot', true).first();
9587 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9589 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9591 var total = this.ds.getTotalCount();
9593 if(this.footer.pageSize < total){
9594 this.mainFoot.show();
9598 Roo.each(this.el.select('tbody td', true).elements, function(e){
9599 e.on('mouseover', _this.onMouseover, _this);
9602 Roo.each(this.el.select('tbody td', true).elements, function(e){
9603 e.on('mouseout', _this.onMouseout, _this);
9605 this.fireEvent('rowsrendered', this);
9609 this.initCSS(); /// resize cols
9615 onUpdate : function(ds,record)
9617 this.refreshRow(record);
9621 onRemove : function(ds, record, index, isUpdate){
9622 if(isUpdate !== true){
9623 this.fireEvent("beforerowremoved", this, index, record);
9625 var bt = this.bodyEl.dom;
9627 var rows = this.el.select('tbody > tr', true).elements;
9629 if(typeof(rows[index]) != 'undefined'){
9630 bt.removeChild(rows[index].dom);
9633 // if(bt.rows[index]){
9634 // bt.removeChild(bt.rows[index]);
9637 if(isUpdate !== true){
9638 //this.stripeRows(index);
9639 //this.syncRowHeights(index, index);
9641 this.fireEvent("rowremoved", this, index, record);
9645 onAdd : function(ds, records, rowIndex)
9647 //Roo.log('on Add called');
9648 // - note this does not handle multiple adding very well..
9649 var bt = this.bodyEl.dom;
9650 for (var i =0 ; i < records.length;i++) {
9651 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9652 //Roo.log(records[i]);
9653 //Roo.log(this.store.getAt(rowIndex+i));
9654 this.insertRow(this.store, rowIndex + i, false);
9661 refreshRow : function(record){
9662 var ds = this.store, index;
9663 if(typeof record == 'number'){
9665 record = ds.getAt(index);
9667 index = ds.indexOf(record);
9669 return; // should not happen - but seems to
9672 this.insertRow(ds, index, true);
9674 this.onRemove(ds, record, index+1, true);
9676 //this.syncRowHeights(index, index);
9678 this.fireEvent("rowupdated", this, index, record);
9680 // private - called by RowSelection
9681 onRowSelect : function(rowIndex){
9682 var row = this.getRowDom(rowIndex);
9683 row.addClass(['bg-info','info']);
9685 // private - called by RowSelection
9686 onRowDeselect : function(rowIndex)
9691 var row = this.getRowDom(rowIndex);
9692 row.removeClass(['bg-info','info']);
9695 * Focuses the specified row.
9696 * @param {Number} row The row index
9698 focusRow : function(row)
9700 //Roo.log('GridView.focusRow');
9701 var x = this.bodyEl.dom.scrollLeft;
9702 this.focusCell(row, 0, false);
9703 this.bodyEl.dom.scrollLeft = x;
9707 * Focuses the specified cell.
9708 * @param {Number} row The row index
9709 * @param {Number} col The column index
9710 * @param {Boolean} hscroll false to disable horizontal scrolling
9712 focusCell : function(row, col, hscroll)
9714 //Roo.log('GridView.focusCell');
9715 var el = this.ensureVisible(row, col, hscroll);
9716 // not sure what focusEL achives = it's a <a> pos relative
9717 //this.focusEl.alignTo(el, "tl-tl");
9719 // this.focusEl.focus();
9721 // this.focusEl.focus.defer(1, this.focusEl);
9726 * Scrolls the specified cell into view
9727 * @param {Number} row The row index
9728 * @param {Number} col The column index
9729 * @param {Boolean} hscroll false to disable horizontal scrolling
9731 ensureVisible : function(row, col, hscroll)
9733 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9734 //return null; //disable for testing.
9735 if(typeof row != "number"){
9738 if(row < 0 && row >= this.ds.getCount()){
9741 col = (col !== undefined ? col : 0);
9743 while(cm.isHidden(col)){
9747 var el = this.getCellDom(row, col);
9751 var c = this.bodyEl.dom;
9753 var ctop = parseInt(el.offsetTop, 10);
9754 var cleft = parseInt(el.offsetLeft, 10);
9755 var cbot = ctop + el.offsetHeight;
9756 var cright = cleft + el.offsetWidth;
9758 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9759 var ch = 0; //?? header is not withing the area?
9760 var stop = parseInt(c.scrollTop, 10);
9761 var sleft = parseInt(c.scrollLeft, 10);
9762 var sbot = stop + ch;
9763 var sright = sleft + c.clientWidth;
9765 Roo.log('GridView.ensureVisible:' +
9767 ' c.clientHeight:' + c.clientHeight +
9768 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9777 //Roo.log("set scrolltop to ctop DISABLE?");
9778 }else if(cbot > sbot){
9779 //Roo.log("set scrolltop to cbot-ch");
9780 c.scrollTop = cbot-ch;
9783 if(hscroll !== false){
9785 c.scrollLeft = cleft;
9786 }else if(cright > sright){
9787 c.scrollLeft = cright-c.clientWidth;
9795 insertRow : function(dm, rowIndex, isUpdate){
9798 this.fireEvent("beforerowsinserted", this, rowIndex);
9800 //var s = this.getScrollState();
9801 var row = this.renderRow(this.cm, this.store, rowIndex);
9802 // insert before rowIndex..
9803 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9807 if(row.cellObjects.length){
9808 Roo.each(row.cellObjects, function(r){
9809 _this.renderCellObject(r);
9814 this.fireEvent("rowsinserted", this, rowIndex);
9815 //this.syncRowHeights(firstRow, lastRow);
9816 //this.stripeRows(firstRow);
9823 getRowDom : function(rowIndex)
9825 var rows = this.el.select('tbody > tr', true).elements;
9827 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9830 getCellDom : function(rowIndex, colIndex)
9832 var row = this.getRowDom(rowIndex);
9833 if (row === false) {
9836 var cols = row.select('td', true).elements;
9837 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9841 // returns the object tree for a tr..
9844 renderRow : function(cm, ds, rowIndex)
9846 var d = ds.getAt(rowIndex);
9850 cls : 'x-row-' + rowIndex,
9854 var cellObjects = [];
9856 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9857 var config = cm.config[i];
9859 var renderer = cm.getRenderer(i);
9863 if(typeof(renderer) !== 'undefined'){
9864 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9866 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9867 // and are rendered into the cells after the row is rendered - using the id for the element.
9869 if(typeof(value) === 'object'){
9879 rowIndex : rowIndex,
9884 this.fireEvent('rowclass', this, rowcfg);
9888 // this might end up displaying HTML?
9889 // this is too messy... - better to only do it on columsn you know are going to be too long
9890 //tooltip : (typeof(value) === 'object') ? '' : value,
9891 cls : rowcfg.rowClass + ' x-col-' + i,
9893 html: (typeof(value) === 'object') ? '' : value
9900 if(typeof(config.colspan) != 'undefined'){
9901 td.colspan = config.colspan;
9906 if(typeof(config.align) != 'undefined' && config.align.length){
9907 td.style += ' text-align:' + config.align + ';';
9909 if(typeof(config.valign) != 'undefined' && config.valign.length){
9910 td.style += ' vertical-align:' + config.valign + ';';
9913 if(typeof(config.width) != 'undefined'){
9914 td.style += ' width:' + config.width + 'px;';
9918 if(typeof(config.cursor) != 'undefined'){
9919 td.style += ' cursor:' + config.cursor + ';';
9922 if(typeof(config.cls) != 'undefined'){
9923 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9925 if (this.responsive) {
9926 ['xs','sm','md','lg'].map(function(size){
9928 if(typeof(config[size]) == 'undefined'){
9934 if (!config[size]) { // 0 = hidden
9935 // BS 4 '0' is treated as hide that column and below.
9936 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9940 td.cls += ' col-' + size + '-' + config[size] + (
9941 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9951 row.cellObjects = cellObjects;
9959 onBeforeLoad : function()
9968 this.el.select('tbody', true).first().dom.innerHTML = '';
9971 * Show or hide a row.
9972 * @param {Number} rowIndex to show or hide
9973 * @param {Boolean} state hide
9975 setRowVisibility : function(rowIndex, state)
9977 var bt = this.bodyEl.dom;
9979 var rows = this.el.select('tbody > tr', true).elements;
9981 if(typeof(rows[rowIndex]) == 'undefined'){
9984 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9989 getSelectionModel : function(){
9991 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9993 return this.selModel;
9996 * Render the Roo.bootstrap object from renderder
9998 renderCellObject : function(r)
10002 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10004 var t = r.cfg.render(r.container);
10007 Roo.each(r.cfg.cn, function(c){
10009 container: t.getChildContainer(),
10012 _this.renderCellObject(child);
10017 * get the Row Index from a dom element.
10018 * @param {Roo.Element} row The row to look for
10019 * @returns {Number} the row
10021 getRowIndex : function(row)
10025 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10036 * get the header TH element for columnIndex
10037 * @param {Number} columnIndex
10038 * @returns {Roo.Element}
10040 getHeaderIndex: function(colIndex)
10042 var cols = this.headEl.select('th', true).elements;
10043 return cols[colIndex];
10046 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10047 * @param {domElement} cell to look for
10048 * @returns {Number} the column
10050 getCellIndex : function(cell)
10052 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10054 return parseInt(id[1], 10);
10059 * Returns the grid's underlying element = used by panel.Grid
10060 * @return {Element} The element
10062 getGridEl : function(){
10066 * Forces a resize - used by panel.Grid
10067 * @return {Element} The element
10069 autoSize : function()
10071 //var ctr = Roo.get(this.container.dom.parentElement);
10072 var ctr = Roo.get(this.el.dom);
10074 var thd = this.getGridEl().select('thead',true).first();
10075 var tbd = this.getGridEl().select('tbody', true).first();
10076 var tfd = this.getGridEl().select('tfoot', true).first();
10078 var cw = ctr.getWidth();
10079 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10083 tbd.setWidth(ctr.getWidth());
10084 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10085 // this needs fixing for various usage - currently only hydra job advers I think..
10087 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10089 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10092 cw = Math.max(cw, this.totalWidth);
10093 this.getGridEl().select('tbody tr',true).setWidth(cw);
10096 // resize 'expandable coloumn?
10098 return; // we doe not have a view in this design..
10101 onBodyScroll: function()
10103 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10105 this.headEl.setStyle({
10106 'position' : 'relative',
10107 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10113 var scrollHeight = this.bodyEl.dom.scrollHeight;
10115 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10117 var height = this.bodyEl.getHeight();
10119 if(scrollHeight - height == scrollTop) {
10121 var total = this.ds.getTotalCount();
10123 if(this.footer.cursor + this.footer.pageSize < total){
10125 this.footer.ds.load({
10127 start : this.footer.cursor + this.footer.pageSize,
10128 limit : this.footer.pageSize
10137 onColumnSplitterMoved : function(i, diff)
10139 this.userResized = true;
10141 var cm = this.colModel;
10143 var w = this.getHeaderIndex(i).getWidth() + diff;
10146 cm.setColumnWidth(i, w, true);
10148 //var cid = cm.getColumnId(i); << not used in this version?
10149 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10151 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10152 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10153 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10155 //this.updateSplitters();
10156 //this.layout(); << ??
10157 this.fireEvent("columnresize", i, w);
10159 onHeaderChange : function()
10161 var header = this.renderHeader();
10162 var table = this.el.select('table', true).first();
10164 this.headEl.remove();
10165 this.headEl = table.createChild(header, this.bodyEl, false);
10167 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10168 e.on('click', this.sort, this);
10171 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10172 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10177 onHiddenChange : function(colModel, colIndex, hidden)
10180 this.cm.setHidden()
10181 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10182 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10184 this.CSS.updateRule(thSelector, "display", "");
10185 this.CSS.updateRule(tdSelector, "display", "");
10188 this.CSS.updateRule(thSelector, "display", "none");
10189 this.CSS.updateRule(tdSelector, "display", "none");
10192 // onload calls initCSS()
10193 this.onHeaderChange();
10197 setColumnWidth: function(col_index, width)
10199 // width = "md-2 xs-2..."
10200 if(!this.colModel.config[col_index]) {
10204 var w = width.split(" ");
10206 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10208 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10211 for(var j = 0; j < w.length; j++) {
10217 var size_cls = w[j].split("-");
10219 if(!Number.isInteger(size_cls[1] * 1)) {
10223 if(!this.colModel.config[col_index][size_cls[0]]) {
10227 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10231 h_row[0].classList.replace(
10232 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10233 "col-"+size_cls[0]+"-"+size_cls[1]
10236 for(var i = 0; i < rows.length; i++) {
10238 var size_cls = w[j].split("-");
10240 if(!Number.isInteger(size_cls[1] * 1)) {
10244 if(!this.colModel.config[col_index][size_cls[0]]) {
10248 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10252 rows[i].classList.replace(
10253 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10254 "col-"+size_cls[0]+"-"+size_cls[1]
10258 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10263 // currently only used to find the split on drag..
10264 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10269 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10270 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10279 * @class Roo.bootstrap.TableCell
10280 * @extends Roo.bootstrap.Component
10281 * Bootstrap TableCell class
10282 * @cfg {String} html cell contain text
10283 * @cfg {String} cls cell class
10284 * @cfg {String} tag cell tag (td|th) default td
10285 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10286 * @cfg {String} align Aligns the content in a cell
10287 * @cfg {String} axis Categorizes cells
10288 * @cfg {String} bgcolor Specifies the background color of a cell
10289 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10290 * @cfg {Number} colspan Specifies the number of columns a cell should span
10291 * @cfg {String} headers Specifies one or more header cells a cell is related to
10292 * @cfg {Number} height Sets the height of a cell
10293 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10294 * @cfg {Number} rowspan Sets the number of rows a cell should span
10295 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10296 * @cfg {String} valign Vertical aligns the content in a cell
10297 * @cfg {Number} width Specifies the width of a cell
10300 * Create a new TableCell
10301 * @param {Object} config The config object
10304 Roo.bootstrap.TableCell = function(config){
10305 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10308 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10328 getAutoCreate : function(){
10329 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10336 cfg.tag = this.tag;
10349 cfg.align=this.align
10354 if (this.bgcolor) {
10355 cfg.bgcolor=this.bgcolor
10357 if (this.charoff) {
10358 cfg.charoff=this.charoff
10360 if (this.colspan) {
10361 cfg.colspan=this.colspan
10363 if (this.headers) {
10364 cfg.headers=this.headers
10367 cfg.height=this.height
10370 cfg.nowrap=this.nowrap
10372 if (this.rowspan) {
10373 cfg.rowspan=this.rowspan
10376 cfg.scope=this.scope
10379 cfg.valign=this.valign
10382 cfg.width=this.width
10401 * @class Roo.bootstrap.TableRow
10402 * @extends Roo.bootstrap.Component
10403 * Bootstrap TableRow class
10404 * @cfg {String} cls row class
10405 * @cfg {String} align Aligns the content in a table row
10406 * @cfg {String} bgcolor Specifies a background color for a table row
10407 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10408 * @cfg {String} valign Vertical aligns the content in a table row
10411 * Create a new TableRow
10412 * @param {Object} config The config object
10415 Roo.bootstrap.TableRow = function(config){
10416 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10419 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10427 getAutoCreate : function(){
10428 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10435 cfg.cls = this.cls;
10438 cfg.align = this.align;
10441 cfg.bgcolor = this.bgcolor;
10444 cfg.charoff = this.charoff;
10447 cfg.valign = this.valign;
10465 * @class Roo.bootstrap.TableBody
10466 * @extends Roo.bootstrap.Component
10467 * Bootstrap TableBody class
10468 * @cfg {String} cls element class
10469 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10470 * @cfg {String} align Aligns the content inside the element
10471 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10472 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10475 * Create a new TableBody
10476 * @param {Object} config The config object
10479 Roo.bootstrap.TableBody = function(config){
10480 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10483 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10491 getAutoCreate : function(){
10492 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10502 cfg.tag = this.tag;
10506 cfg.align = this.align;
10509 cfg.charoff = this.charoff;
10512 cfg.valign = this.valign;
10519 // initEvents : function()
10522 // if(!this.store){
10526 // this.store = Roo.factory(this.store, Roo.data);
10527 // this.store.on('load', this.onLoad, this);
10529 // this.store.load();
10533 // onLoad: function ()
10535 // this.fireEvent('load', this);
10545 * Ext JS Library 1.1.1
10546 * Copyright(c) 2006-2007, Ext JS, LLC.
10548 * Originally Released Under LGPL - original licence link has changed is not relivant.
10551 * <script type="text/javascript">
10554 // as we use this in bootstrap.
10555 Roo.namespace('Roo.form');
10557 * @class Roo.form.Action
10558 * Internal Class used to handle form actions
10560 * @param {Roo.form.BasicForm} el The form element or its id
10561 * @param {Object} config Configuration options
10566 // define the action interface
10567 Roo.form.Action = function(form, options){
10569 this.options = options || {};
10572 * Client Validation Failed
10575 Roo.form.Action.CLIENT_INVALID = 'client';
10577 * Server Validation Failed
10580 Roo.form.Action.SERVER_INVALID = 'server';
10582 * Connect to Server Failed
10585 Roo.form.Action.CONNECT_FAILURE = 'connect';
10587 * Reading Data from Server Failed
10590 Roo.form.Action.LOAD_FAILURE = 'load';
10592 Roo.form.Action.prototype = {
10594 failureType : undefined,
10595 response : undefined,
10596 result : undefined,
10598 // interface method
10599 run : function(options){
10603 // interface method
10604 success : function(response){
10608 // interface method
10609 handleResponse : function(response){
10613 // default connection failure
10614 failure : function(response){
10616 this.response = response;
10617 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10618 this.form.afterAction(this, false);
10621 processResponse : function(response){
10622 this.response = response;
10623 if(!response.responseText){
10626 this.result = this.handleResponse(response);
10627 return this.result;
10630 // utility functions used internally
10631 getUrl : function(appendParams){
10632 var url = this.options.url || this.form.url || this.form.el.dom.action;
10634 var p = this.getParams();
10636 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10642 getMethod : function(){
10643 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10646 getParams : function(){
10647 var bp = this.form.baseParams;
10648 var p = this.options.params;
10650 if(typeof p == "object"){
10651 p = Roo.urlEncode(Roo.applyIf(p, bp));
10652 }else if(typeof p == 'string' && bp){
10653 p += '&' + Roo.urlEncode(bp);
10656 p = Roo.urlEncode(bp);
10661 createCallback : function(){
10663 success: this.success,
10664 failure: this.failure,
10666 timeout: (this.form.timeout*1000),
10667 upload: this.form.fileUpload ? this.success : undefined
10672 Roo.form.Action.Submit = function(form, options){
10673 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10676 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10679 haveProgress : false,
10680 uploadComplete : false,
10682 // uploadProgress indicator.
10683 uploadProgress : function()
10685 if (!this.form.progressUrl) {
10689 if (!this.haveProgress) {
10690 Roo.MessageBox.progress("Uploading", "Uploading");
10692 if (this.uploadComplete) {
10693 Roo.MessageBox.hide();
10697 this.haveProgress = true;
10699 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10701 var c = new Roo.data.Connection();
10703 url : this.form.progressUrl,
10708 success : function(req){
10709 //console.log(data);
10713 rdata = Roo.decode(req.responseText)
10715 Roo.log("Invalid data from server..");
10719 if (!rdata || !rdata.success) {
10721 Roo.MessageBox.alert(Roo.encode(rdata));
10724 var data = rdata.data;
10726 if (this.uploadComplete) {
10727 Roo.MessageBox.hide();
10732 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10733 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10736 this.uploadProgress.defer(2000,this);
10739 failure: function(data) {
10740 Roo.log('progress url failed ');
10751 // run get Values on the form, so it syncs any secondary forms.
10752 this.form.getValues();
10754 var o = this.options;
10755 var method = this.getMethod();
10756 var isPost = method == 'POST';
10757 if(o.clientValidation === false || this.form.isValid()){
10759 if (this.form.progressUrl) {
10760 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10761 (new Date() * 1) + '' + Math.random());
10766 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10767 form:this.form.el.dom,
10768 url:this.getUrl(!isPost),
10770 params:isPost ? this.getParams() : null,
10771 isUpload: this.form.fileUpload,
10772 formData : this.form.formData
10775 this.uploadProgress();
10777 }else if (o.clientValidation !== false){ // client validation failed
10778 this.failureType = Roo.form.Action.CLIENT_INVALID;
10779 this.form.afterAction(this, false);
10783 success : function(response)
10785 this.uploadComplete= true;
10786 if (this.haveProgress) {
10787 Roo.MessageBox.hide();
10791 var result = this.processResponse(response);
10792 if(result === true || result.success){
10793 this.form.afterAction(this, true);
10797 this.form.markInvalid(result.errors);
10798 this.failureType = Roo.form.Action.SERVER_INVALID;
10800 this.form.afterAction(this, false);
10802 failure : function(response)
10804 this.uploadComplete= true;
10805 if (this.haveProgress) {
10806 Roo.MessageBox.hide();
10809 this.response = response;
10810 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10811 this.form.afterAction(this, false);
10814 handleResponse : function(response){
10815 if(this.form.errorReader){
10816 var rs = this.form.errorReader.read(response);
10819 for(var i = 0, len = rs.records.length; i < len; i++) {
10820 var r = rs.records[i];
10821 errors[i] = r.data;
10824 if(errors.length < 1){
10828 success : rs.success,
10834 ret = Roo.decode(response.responseText);
10838 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10848 Roo.form.Action.Load = function(form, options){
10849 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10850 this.reader = this.form.reader;
10853 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10858 Roo.Ajax.request(Roo.apply(
10859 this.createCallback(), {
10860 method:this.getMethod(),
10861 url:this.getUrl(false),
10862 params:this.getParams()
10866 success : function(response){
10868 var result = this.processResponse(response);
10869 if(result === true || !result.success || !result.data){
10870 this.failureType = Roo.form.Action.LOAD_FAILURE;
10871 this.form.afterAction(this, false);
10874 this.form.clearInvalid();
10875 this.form.setValues(result.data);
10876 this.form.afterAction(this, true);
10879 handleResponse : function(response){
10880 if(this.form.reader){
10881 var rs = this.form.reader.read(response);
10882 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10884 success : rs.success,
10888 return Roo.decode(response.responseText);
10892 Roo.form.Action.ACTION_TYPES = {
10893 'load' : Roo.form.Action.Load,
10894 'submit' : Roo.form.Action.Submit
10903 * @class Roo.bootstrap.Form
10904 * @extends Roo.bootstrap.Component
10905 * Bootstrap Form class
10906 * @cfg {String} method GET | POST (default POST)
10907 * @cfg {String} labelAlign top | left (default top)
10908 * @cfg {String} align left | right - for navbars
10909 * @cfg {Boolean} loadMask load mask when submit (default true)
10913 * Create a new Form
10914 * @param {Object} config The config object
10918 Roo.bootstrap.Form = function(config){
10920 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10922 Roo.bootstrap.Form.popover.apply();
10926 * @event clientvalidation
10927 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10928 * @param {Form} this
10929 * @param {Boolean} valid true if the form has passed client-side validation
10931 clientvalidation: true,
10933 * @event beforeaction
10934 * Fires before any action is performed. Return false to cancel the action.
10935 * @param {Form} this
10936 * @param {Action} action The action to be performed
10938 beforeaction: true,
10940 * @event actionfailed
10941 * Fires when an action fails.
10942 * @param {Form} this
10943 * @param {Action} action The action that failed
10945 actionfailed : true,
10947 * @event actioncomplete
10948 * Fires when an action is completed.
10949 * @param {Form} this
10950 * @param {Action} action The action that completed
10952 actioncomplete : true
10956 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10959 * @cfg {String} method
10960 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10964 * @cfg {String} url
10965 * The URL to use for form actions if one isn't supplied in the action options.
10968 * @cfg {Boolean} fileUpload
10969 * Set to true if this form is a file upload.
10973 * @cfg {Object} baseParams
10974 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10978 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10982 * @cfg {Sting} align (left|right) for navbar forms
10987 activeAction : null,
10990 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10991 * element by passing it or its id or mask the form itself by passing in true.
10994 waitMsgTarget : false,
10999 * @cfg {Boolean} errorMask (true|false) default false
11004 * @cfg {Number} maskOffset Default 100
11009 * @cfg {Boolean} maskBody
11013 getAutoCreate : function(){
11017 method : this.method || 'POST',
11018 id : this.id || Roo.id(),
11021 if (this.parent().xtype.match(/^Nav/)) {
11022 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11026 if (this.labelAlign == 'left' ) {
11027 cfg.cls += ' form-horizontal';
11033 initEvents : function()
11035 this.el.on('submit', this.onSubmit, this);
11036 // this was added as random key presses on the form where triggering form submit.
11037 this.el.on('keypress', function(e) {
11038 if (e.getCharCode() != 13) {
11041 // we might need to allow it for textareas.. and some other items.
11042 // check e.getTarget().
11044 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11048 Roo.log("keypress blocked");
11050 e.preventDefault();
11056 onSubmit : function(e){
11061 * Returns true if client-side validation on the form is successful.
11064 isValid : function(){
11065 var items = this.getItems();
11067 var target = false;
11069 items.each(function(f){
11075 Roo.log('invalid field: ' + f.name);
11079 if(!target && f.el.isVisible(true)){
11085 if(this.errorMask && !valid){
11086 Roo.bootstrap.Form.popover.mask(this, target);
11093 * Returns true if any fields in this form have changed since their original load.
11096 isDirty : function(){
11098 var items = this.getItems();
11099 items.each(function(f){
11109 * Performs a predefined action (submit or load) or custom actions you define on this form.
11110 * @param {String} actionName The name of the action type
11111 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11112 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11113 * accept other config options):
11115 Property Type Description
11116 ---------------- --------------- ----------------------------------------------------------------------------------
11117 url String The url for the action (defaults to the form's url)
11118 method String The form method to use (defaults to the form's method, or POST if not defined)
11119 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11120 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11121 validate the form on the client (defaults to false)
11123 * @return {BasicForm} this
11125 doAction : function(action, options){
11126 if(typeof action == 'string'){
11127 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11129 if(this.fireEvent('beforeaction', this, action) !== false){
11130 this.beforeAction(action);
11131 action.run.defer(100, action);
11137 beforeAction : function(action){
11138 var o = action.options;
11143 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11145 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11148 // not really supported yet.. ??
11150 //if(this.waitMsgTarget === true){
11151 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11152 //}else if(this.waitMsgTarget){
11153 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11154 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11156 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11162 afterAction : function(action, success){
11163 this.activeAction = null;
11164 var o = action.options;
11169 Roo.get(document.body).unmask();
11175 //if(this.waitMsgTarget === true){
11176 // this.el.unmask();
11177 //}else if(this.waitMsgTarget){
11178 // this.waitMsgTarget.unmask();
11180 // Roo.MessageBox.updateProgress(1);
11181 // Roo.MessageBox.hide();
11188 Roo.callback(o.success, o.scope, [this, action]);
11189 this.fireEvent('actioncomplete', this, action);
11193 // failure condition..
11194 // we have a scenario where updates need confirming.
11195 // eg. if a locking scenario exists..
11196 // we look for { errors : { needs_confirm : true }} in the response.
11198 (typeof(action.result) != 'undefined') &&
11199 (typeof(action.result.errors) != 'undefined') &&
11200 (typeof(action.result.errors.needs_confirm) != 'undefined')
11203 Roo.log("not supported yet");
11206 Roo.MessageBox.confirm(
11207 "Change requires confirmation",
11208 action.result.errorMsg,
11213 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11223 Roo.callback(o.failure, o.scope, [this, action]);
11224 // show an error message if no failed handler is set..
11225 if (!this.hasListener('actionfailed')) {
11226 Roo.log("need to add dialog support");
11228 Roo.MessageBox.alert("Error",
11229 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11230 action.result.errorMsg :
11231 "Saving Failed, please check your entries or try again"
11236 this.fireEvent('actionfailed', this, action);
11241 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11242 * @param {String} id The value to search for
11245 findField : function(id){
11246 var items = this.getItems();
11247 var field = items.get(id);
11249 items.each(function(f){
11250 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11257 return field || null;
11260 * Mark fields in this form invalid in bulk.
11261 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11262 * @return {BasicForm} this
11264 markInvalid : function(errors){
11265 if(errors instanceof Array){
11266 for(var i = 0, len = errors.length; i < len; i++){
11267 var fieldError = errors[i];
11268 var f = this.findField(fieldError.id);
11270 f.markInvalid(fieldError.msg);
11276 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11277 field.markInvalid(errors[id]);
11281 //Roo.each(this.childForms || [], function (f) {
11282 // f.markInvalid(errors);
11289 * Set values for fields in this form in bulk.
11290 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11291 * @return {BasicForm} this
11293 setValues : function(values){
11294 if(values instanceof Array){ // array of objects
11295 for(var i = 0, len = values.length; i < len; i++){
11297 var f = this.findField(v.id);
11299 f.setValue(v.value);
11300 if(this.trackResetOnLoad){
11301 f.originalValue = f.getValue();
11305 }else{ // object hash
11308 if(typeof values[id] != 'function' && (field = this.findField(id))){
11310 if (field.setFromData &&
11311 field.valueField &&
11312 field.displayField &&
11313 // combos' with local stores can
11314 // be queried via setValue()
11315 // to set their value..
11316 (field.store && !field.store.isLocal)
11320 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11321 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11322 field.setFromData(sd);
11324 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11326 field.setFromData(values);
11329 field.setValue(values[id]);
11333 if(this.trackResetOnLoad){
11334 field.originalValue = field.getValue();
11340 //Roo.each(this.childForms || [], function (f) {
11341 // f.setValues(values);
11348 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11349 * they are returned as an array.
11350 * @param {Boolean} asString
11353 getValues : function(asString){
11354 //if (this.childForms) {
11355 // copy values from the child forms
11356 // Roo.each(this.childForms, function (f) {
11357 // this.setValues(f.getValues());
11363 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11364 if(asString === true){
11367 return Roo.urlDecode(fs);
11371 * Returns the fields in this form as an object with key/value pairs.
11372 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11375 getFieldValues : function(with_hidden)
11377 var items = this.getItems();
11379 items.each(function(f){
11381 if (!f.getName()) {
11385 var v = f.getValue();
11387 if (f.inputType =='radio') {
11388 if (typeof(ret[f.getName()]) == 'undefined') {
11389 ret[f.getName()] = ''; // empty..
11392 if (!f.el.dom.checked) {
11396 v = f.el.dom.value;
11400 if(f.xtype == 'MoneyField'){
11401 ret[f.currencyName] = f.getCurrency();
11404 // not sure if this supported any more..
11405 if ((typeof(v) == 'object') && f.getRawValue) {
11406 v = f.getRawValue() ; // dates..
11408 // combo boxes where name != hiddenName...
11409 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11410 ret[f.name] = f.getRawValue();
11412 ret[f.getName()] = v;
11419 * Clears all invalid messages in this form.
11420 * @return {BasicForm} this
11422 clearInvalid : function(){
11423 var items = this.getItems();
11425 items.each(function(f){
11433 * Resets this form.
11434 * @return {BasicForm} this
11436 reset : function(){
11437 var items = this.getItems();
11438 items.each(function(f){
11442 Roo.each(this.childForms || [], function (f) {
11450 getItems : function()
11452 var r=new Roo.util.MixedCollection(false, function(o){
11453 return o.id || (o.id = Roo.id());
11455 var iter = function(el) {
11462 Roo.each(el.items,function(e) {
11471 hideFields : function(items)
11473 Roo.each(items, function(i){
11475 var f = this.findField(i);
11486 showFields : function(items)
11488 Roo.each(items, function(i){
11490 var f = this.findField(i);
11503 Roo.apply(Roo.bootstrap.Form, {
11519 intervalID : false,
11525 if(this.isApplied){
11530 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11531 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11532 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11533 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11536 this.maskEl.top.enableDisplayMode("block");
11537 this.maskEl.left.enableDisplayMode("block");
11538 this.maskEl.bottom.enableDisplayMode("block");
11539 this.maskEl.right.enableDisplayMode("block");
11541 this.toolTip = new Roo.bootstrap.Tooltip({
11542 cls : 'roo-form-error-popover',
11544 'left' : ['r-l', [-2,0], 'right'],
11545 'right' : ['l-r', [2,0], 'left'],
11546 'bottom' : ['tl-bl', [0,2], 'top'],
11547 'top' : [ 'bl-tl', [0,-2], 'bottom']
11551 this.toolTip.render(Roo.get(document.body));
11553 this.toolTip.el.enableDisplayMode("block");
11555 Roo.get(document.body).on('click', function(){
11559 Roo.get(document.body).on('touchstart', function(){
11563 this.isApplied = true
11566 mask : function(form, target)
11570 this.target = target;
11572 if(!this.form.errorMask || !target.el){
11576 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11578 Roo.log(scrollable);
11580 var ot = this.target.el.calcOffsetsTo(scrollable);
11582 var scrollTo = ot[1] - this.form.maskOffset;
11584 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11586 scrollable.scrollTo('top', scrollTo);
11588 var box = this.target.el.getBox();
11590 var zIndex = Roo.bootstrap.Modal.zIndex++;
11593 this.maskEl.top.setStyle('position', 'absolute');
11594 this.maskEl.top.setStyle('z-index', zIndex);
11595 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11596 this.maskEl.top.setLeft(0);
11597 this.maskEl.top.setTop(0);
11598 this.maskEl.top.show();
11600 this.maskEl.left.setStyle('position', 'absolute');
11601 this.maskEl.left.setStyle('z-index', zIndex);
11602 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11603 this.maskEl.left.setLeft(0);
11604 this.maskEl.left.setTop(box.y - this.padding);
11605 this.maskEl.left.show();
11607 this.maskEl.bottom.setStyle('position', 'absolute');
11608 this.maskEl.bottom.setStyle('z-index', zIndex);
11609 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11610 this.maskEl.bottom.setLeft(0);
11611 this.maskEl.bottom.setTop(box.bottom + this.padding);
11612 this.maskEl.bottom.show();
11614 this.maskEl.right.setStyle('position', 'absolute');
11615 this.maskEl.right.setStyle('z-index', zIndex);
11616 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11617 this.maskEl.right.setLeft(box.right + this.padding);
11618 this.maskEl.right.setTop(box.y - this.padding);
11619 this.maskEl.right.show();
11621 this.toolTip.bindEl = this.target.el;
11623 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11625 var tip = this.target.blankText;
11627 if(this.target.getValue() !== '' ) {
11629 if (this.target.invalidText.length) {
11630 tip = this.target.invalidText;
11631 } else if (this.target.regexText.length){
11632 tip = this.target.regexText;
11636 this.toolTip.show(tip);
11638 this.intervalID = window.setInterval(function() {
11639 Roo.bootstrap.Form.popover.unmask();
11642 window.onwheel = function(){ return false;};
11644 (function(){ this.isMasked = true; }).defer(500, this);
11648 unmask : function()
11650 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11654 this.maskEl.top.setStyle('position', 'absolute');
11655 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11656 this.maskEl.top.hide();
11658 this.maskEl.left.setStyle('position', 'absolute');
11659 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11660 this.maskEl.left.hide();
11662 this.maskEl.bottom.setStyle('position', 'absolute');
11663 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11664 this.maskEl.bottom.hide();
11666 this.maskEl.right.setStyle('position', 'absolute');
11667 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11668 this.maskEl.right.hide();
11670 this.toolTip.hide();
11672 this.toolTip.el.hide();
11674 window.onwheel = function(){ return true;};
11676 if(this.intervalID){
11677 window.clearInterval(this.intervalID);
11678 this.intervalID = false;
11681 this.isMasked = false;
11691 * Ext JS Library 1.1.1
11692 * Copyright(c) 2006-2007, Ext JS, LLC.
11694 * Originally Released Under LGPL - original licence link has changed is not relivant.
11697 * <script type="text/javascript">
11700 * @class Roo.form.VTypes
11701 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11704 Roo.form.VTypes = function(){
11705 // closure these in so they are only created once.
11706 var alpha = /^[a-zA-Z_]+$/;
11707 var alphanum = /^[a-zA-Z0-9_]+$/;
11708 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11709 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11711 // All these messages and functions are configurable
11714 * The function used to validate email addresses
11715 * @param {String} value The email address
11717 'email' : function(v){
11718 return email.test(v);
11721 * The error text to display when the email validation function returns false
11724 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11726 * The keystroke filter mask to be applied on email input
11729 'emailMask' : /[a-z0-9_\.\-@]/i,
11732 * The function used to validate URLs
11733 * @param {String} value The URL
11735 'url' : function(v){
11736 return url.test(v);
11739 * The error text to display when the url validation function returns false
11742 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11745 * The function used to validate alpha values
11746 * @param {String} value The value
11748 'alpha' : function(v){
11749 return alpha.test(v);
11752 * The error text to display when the alpha validation function returns false
11755 'alphaText' : 'This field should only contain letters and _',
11757 * The keystroke filter mask to be applied on alpha input
11760 'alphaMask' : /[a-z_]/i,
11763 * The function used to validate alphanumeric values
11764 * @param {String} value The value
11766 'alphanum' : function(v){
11767 return alphanum.test(v);
11770 * The error text to display when the alphanumeric validation function returns false
11773 'alphanumText' : 'This field should only contain letters, numbers and _',
11775 * The keystroke filter mask to be applied on alphanumeric input
11778 'alphanumMask' : /[a-z0-9_]/i
11788 * @class Roo.bootstrap.Input
11789 * @extends Roo.bootstrap.Component
11790 * Bootstrap Input class
11791 * @cfg {Boolean} disabled is it disabled
11792 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11793 * @cfg {String} name name of the input
11794 * @cfg {string} fieldLabel - the label associated
11795 * @cfg {string} placeholder - placeholder to put in text.
11796 * @cfg {string} before - input group add on before
11797 * @cfg {string} after - input group add on after
11798 * @cfg {string} size - (lg|sm) or leave empty..
11799 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11800 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11801 * @cfg {Number} md colspan out of 12 for computer-sized screens
11802 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11803 * @cfg {string} value default value of the input
11804 * @cfg {Number} labelWidth set the width of label
11805 * @cfg {Number} labellg set the width of label (1-12)
11806 * @cfg {Number} labelmd set the width of label (1-12)
11807 * @cfg {Number} labelsm set the width of label (1-12)
11808 * @cfg {Number} labelxs set the width of label (1-12)
11809 * @cfg {String} labelAlign (top|left)
11810 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11811 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11812 * @cfg {String} indicatorpos (left|right) default left
11813 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11814 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11815 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11817 * @cfg {String} align (left|center|right) Default left
11818 * @cfg {Boolean} forceFeedback (true|false) Default false
11821 * Create a new Input
11822 * @param {Object} config The config object
11825 Roo.bootstrap.Input = function(config){
11827 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11832 * Fires when this field receives input focus.
11833 * @param {Roo.form.Field} this
11838 * Fires when this field loses input focus.
11839 * @param {Roo.form.Field} this
11843 * @event specialkey
11844 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11845 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11846 * @param {Roo.form.Field} this
11847 * @param {Roo.EventObject} e The event object
11852 * Fires just before the field blurs if the field value has changed.
11853 * @param {Roo.form.Field} this
11854 * @param {Mixed} newValue The new value
11855 * @param {Mixed} oldValue The original value
11860 * Fires after the field has been marked as invalid.
11861 * @param {Roo.form.Field} this
11862 * @param {String} msg The validation message
11867 * Fires after the field has been validated with no errors.
11868 * @param {Roo.form.Field} this
11873 * Fires after the key up
11874 * @param {Roo.form.Field} this
11875 * @param {Roo.EventObject} e The event Object
11880 * Fires after the user pastes into input
11881 * @param {Roo.form.Field} this
11882 * @param {Roo.EventObject} e The event Object
11888 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11890 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11891 automatic validation (defaults to "keyup").
11893 validationEvent : "keyup",
11895 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11897 validateOnBlur : true,
11899 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11901 validationDelay : 250,
11903 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11905 focusClass : "x-form-focus", // not needed???
11909 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11911 invalidClass : "has-warning",
11914 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11916 validClass : "has-success",
11919 * @cfg {Boolean} hasFeedback (true|false) default true
11921 hasFeedback : true,
11924 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11926 invalidFeedbackClass : "glyphicon-warning-sign",
11929 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11931 validFeedbackClass : "glyphicon-ok",
11934 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11936 selectOnFocus : false,
11939 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11943 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11948 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11950 disableKeyFilter : false,
11953 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11957 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11961 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11963 blankText : "Please complete this mandatory field",
11966 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11970 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11972 maxLength : Number.MAX_VALUE,
11974 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11976 minLengthText : "The minimum length for this field is {0}",
11978 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11980 maxLengthText : "The maximum length for this field is {0}",
11984 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11985 * If available, this function will be called only after the basic validators all return true, and will be passed the
11986 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11990 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11991 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11992 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11996 * @cfg {String} regexText -- Depricated - use Invalid Text
12001 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12007 autocomplete: false,
12011 inputType : 'text',
12014 placeholder: false,
12019 preventMark: false,
12020 isFormField : true,
12023 labelAlign : false,
12026 formatedValue : false,
12027 forceFeedback : false,
12029 indicatorpos : 'left',
12039 parentLabelAlign : function()
12042 while (parent.parent()) {
12043 parent = parent.parent();
12044 if (typeof(parent.labelAlign) !='undefined') {
12045 return parent.labelAlign;
12052 getAutoCreate : function()
12054 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12060 if(this.inputType != 'hidden'){
12061 cfg.cls = 'form-group' //input-group
12067 type : this.inputType,
12068 value : this.value,
12069 cls : 'form-control',
12070 placeholder : this.placeholder || '',
12071 autocomplete : this.autocomplete || 'new-password'
12073 if (this.inputType == 'file') {
12074 input.style = 'overflow:hidden'; // why not in CSS?
12077 if(this.capture.length){
12078 input.capture = this.capture;
12081 if(this.accept.length){
12082 input.accept = this.accept + "/*";
12086 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12089 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12090 input.maxLength = this.maxLength;
12093 if (this.disabled) {
12094 input.disabled=true;
12097 if (this.readOnly) {
12098 input.readonly=true;
12102 input.name = this.name;
12106 input.cls += ' input-' + this.size;
12110 ['xs','sm','md','lg'].map(function(size){
12111 if (settings[size]) {
12112 cfg.cls += ' col-' + size + '-' + settings[size];
12116 var inputblock = input;
12120 cls: 'glyphicon form-control-feedback'
12123 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12126 cls : 'has-feedback',
12134 if (this.before || this.after) {
12137 cls : 'input-group',
12141 if (this.before && typeof(this.before) == 'string') {
12143 inputblock.cn.push({
12145 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12149 if (this.before && typeof(this.before) == 'object') {
12150 this.before = Roo.factory(this.before);
12152 inputblock.cn.push({
12154 cls : 'roo-input-before input-group-prepend input-group-' +
12155 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12159 inputblock.cn.push(input);
12161 if (this.after && typeof(this.after) == 'string') {
12162 inputblock.cn.push({
12164 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12168 if (this.after && typeof(this.after) == 'object') {
12169 this.after = Roo.factory(this.after);
12171 inputblock.cn.push({
12173 cls : 'roo-input-after input-group-append input-group-' +
12174 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12178 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12179 inputblock.cls += ' has-feedback';
12180 inputblock.cn.push(feedback);
12185 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12186 tooltip : 'This field is required'
12188 if (this.allowBlank ) {
12189 indicator.style = this.allowBlank ? ' display:none' : '';
12191 if (align ==='left' && this.fieldLabel.length) {
12193 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12200 cls : 'control-label col-form-label',
12201 html : this.fieldLabel
12212 var labelCfg = cfg.cn[1];
12213 var contentCfg = cfg.cn[2];
12215 if(this.indicatorpos == 'right'){
12220 cls : 'control-label col-form-label',
12224 html : this.fieldLabel
12238 labelCfg = cfg.cn[0];
12239 contentCfg = cfg.cn[1];
12243 if(this.labelWidth > 12){
12244 labelCfg.style = "width: " + this.labelWidth + 'px';
12247 if(this.labelWidth < 13 && this.labelmd == 0){
12248 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12251 if(this.labellg > 0){
12252 labelCfg.cls += ' col-lg-' + this.labellg;
12253 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12256 if(this.labelmd > 0){
12257 labelCfg.cls += ' col-md-' + this.labelmd;
12258 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12261 if(this.labelsm > 0){
12262 labelCfg.cls += ' col-sm-' + this.labelsm;
12263 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12266 if(this.labelxs > 0){
12267 labelCfg.cls += ' col-xs-' + this.labelxs;
12268 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12272 } else if ( this.fieldLabel.length) {
12279 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12280 tooltip : 'This field is required',
12281 style : this.allowBlank ? ' display:none' : ''
12285 //cls : 'input-group-addon',
12286 html : this.fieldLabel
12294 if(this.indicatorpos == 'right'){
12299 //cls : 'input-group-addon',
12300 html : this.fieldLabel
12305 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12306 tooltip : 'This field is required',
12307 style : this.allowBlank ? ' display:none' : ''
12327 if (this.parentType === 'Navbar' && this.parent().bar) {
12328 cfg.cls += ' navbar-form';
12331 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12332 // on BS4 we do this only if not form
12333 cfg.cls += ' navbar-form';
12341 * return the real input element.
12343 inputEl: function ()
12345 return this.el.select('input.form-control',true).first();
12348 tooltipEl : function()
12350 return this.inputEl();
12353 indicatorEl : function()
12355 if (Roo.bootstrap.version == 4) {
12356 return false; // not enabled in v4 yet.
12359 var indicator = this.el.select('i.roo-required-indicator',true).first();
12369 setDisabled : function(v)
12371 var i = this.inputEl().dom;
12373 i.removeAttribute('disabled');
12377 i.setAttribute('disabled','true');
12379 initEvents : function()
12382 this.inputEl().on("keydown" , this.fireKey, this);
12383 this.inputEl().on("focus", this.onFocus, this);
12384 this.inputEl().on("blur", this.onBlur, this);
12386 this.inputEl().relayEvent('keyup', this);
12387 this.inputEl().relayEvent('paste', this);
12389 this.indicator = this.indicatorEl();
12391 if(this.indicator){
12392 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12395 // reference to original value for reset
12396 this.originalValue = this.getValue();
12397 //Roo.form.TextField.superclass.initEvents.call(this);
12398 if(this.validationEvent == 'keyup'){
12399 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12400 this.inputEl().on('keyup', this.filterValidation, this);
12402 else if(this.validationEvent !== false){
12403 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12406 if(this.selectOnFocus){
12407 this.on("focus", this.preFocus, this);
12410 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12411 this.inputEl().on("keypress", this.filterKeys, this);
12413 this.inputEl().relayEvent('keypress', this);
12416 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12417 this.el.on("click", this.autoSize, this);
12420 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12421 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12424 if (typeof(this.before) == 'object') {
12425 this.before.render(this.el.select('.roo-input-before',true).first());
12427 if (typeof(this.after) == 'object') {
12428 this.after.render(this.el.select('.roo-input-after',true).first());
12431 this.inputEl().on('change', this.onChange, this);
12434 filterValidation : function(e){
12435 if(!e.isNavKeyPress()){
12436 this.validationTask.delay(this.validationDelay);
12440 * Validates the field value
12441 * @return {Boolean} True if the value is valid, else false
12443 validate : function(){
12444 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12445 if(this.disabled || this.validateValue(this.getRawValue())){
12450 this.markInvalid();
12456 * Validates a value according to the field's validation rules and marks the field as invalid
12457 * if the validation fails
12458 * @param {Mixed} value The value to validate
12459 * @return {Boolean} True if the value is valid, else false
12461 validateValue : function(value)
12463 if(this.getVisibilityEl().hasClass('hidden')){
12467 if(value.length < 1) { // if it's blank
12468 if(this.allowBlank){
12474 if(value.length < this.minLength){
12477 if(value.length > this.maxLength){
12481 var vt = Roo.form.VTypes;
12482 if(!vt[this.vtype](value, this)){
12486 if(typeof this.validator == "function"){
12487 var msg = this.validator(value);
12491 if (typeof(msg) == 'string') {
12492 this.invalidText = msg;
12496 if(this.regex && !this.regex.test(value)){
12504 fireKey : function(e){
12505 //Roo.log('field ' + e.getKey());
12506 if(e.isNavKeyPress()){
12507 this.fireEvent("specialkey", this, e);
12510 focus : function (selectText){
12512 this.inputEl().focus();
12513 if(selectText === true){
12514 this.inputEl().dom.select();
12520 onFocus : function(){
12521 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12522 // this.el.addClass(this.focusClass);
12524 if(!this.hasFocus){
12525 this.hasFocus = true;
12526 this.startValue = this.getValue();
12527 this.fireEvent("focus", this);
12531 beforeBlur : Roo.emptyFn,
12535 onBlur : function(){
12537 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12538 //this.el.removeClass(this.focusClass);
12540 this.hasFocus = false;
12541 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12544 var v = this.getValue();
12545 if(String(v) !== String(this.startValue)){
12546 this.fireEvent('change', this, v, this.startValue);
12548 this.fireEvent("blur", this);
12551 onChange : function(e)
12553 var v = this.getValue();
12554 if(String(v) !== String(this.startValue)){
12555 this.fireEvent('change', this, v, this.startValue);
12561 * Resets the current field value to the originally loaded value and clears any validation messages
12563 reset : function(){
12564 this.setValue(this.originalValue);
12568 * Returns the name of the field
12569 * @return {Mixed} name The name field
12571 getName: function(){
12575 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12576 * @return {Mixed} value The field value
12578 getValue : function(){
12580 var v = this.inputEl().getValue();
12585 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12586 * @return {Mixed} value The field value
12588 getRawValue : function(){
12589 var v = this.inputEl().getValue();
12595 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12596 * @param {Mixed} value The value to set
12598 setRawValue : function(v){
12599 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12602 selectText : function(start, end){
12603 var v = this.getRawValue();
12605 start = start === undefined ? 0 : start;
12606 end = end === undefined ? v.length : end;
12607 var d = this.inputEl().dom;
12608 if(d.setSelectionRange){
12609 d.setSelectionRange(start, end);
12610 }else if(d.createTextRange){
12611 var range = d.createTextRange();
12612 range.moveStart("character", start);
12613 range.moveEnd("character", v.length-end);
12620 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12621 * @param {Mixed} value The value to set
12623 setValue : function(v){
12626 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12632 processValue : function(value){
12633 if(this.stripCharsRe){
12634 var newValue = value.replace(this.stripCharsRe, '');
12635 if(newValue !== value){
12636 this.setRawValue(newValue);
12643 preFocus : function(){
12645 if(this.selectOnFocus){
12646 this.inputEl().dom.select();
12649 filterKeys : function(e){
12650 var k = e.getKey();
12651 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12654 var c = e.getCharCode(), cc = String.fromCharCode(c);
12655 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12658 if(!this.maskRe.test(cc)){
12663 * Clear any invalid styles/messages for this field
12665 clearInvalid : function(){
12667 if(!this.el || this.preventMark){ // not rendered
12672 this.el.removeClass([this.invalidClass, 'is-invalid']);
12674 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12676 var feedback = this.el.select('.form-control-feedback', true).first();
12679 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12684 if(this.indicator){
12685 this.indicator.removeClass('visible');
12686 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12689 this.fireEvent('valid', this);
12693 * Mark this field as valid
12695 markValid : function()
12697 if(!this.el || this.preventMark){ // not rendered...
12701 this.el.removeClass([this.invalidClass, this.validClass]);
12702 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12704 var feedback = this.el.select('.form-control-feedback', true).first();
12707 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12710 if(this.indicator){
12711 this.indicator.removeClass('visible');
12712 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12720 if(this.allowBlank && !this.getRawValue().length){
12723 if (Roo.bootstrap.version == 3) {
12724 this.el.addClass(this.validClass);
12726 this.inputEl().addClass('is-valid');
12729 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12731 var feedback = this.el.select('.form-control-feedback', true).first();
12734 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12735 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12740 this.fireEvent('valid', this);
12744 * Mark this field as invalid
12745 * @param {String} msg The validation message
12747 markInvalid : function(msg)
12749 if(!this.el || this.preventMark){ // not rendered
12753 this.el.removeClass([this.invalidClass, this.validClass]);
12754 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12756 var feedback = this.el.select('.form-control-feedback', true).first();
12759 this.el.select('.form-control-feedback', true).first().removeClass(
12760 [this.invalidFeedbackClass, this.validFeedbackClass]);
12767 if(this.allowBlank && !this.getRawValue().length){
12771 if(this.indicator){
12772 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12773 this.indicator.addClass('visible');
12775 if (Roo.bootstrap.version == 3) {
12776 this.el.addClass(this.invalidClass);
12778 this.inputEl().addClass('is-invalid');
12783 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12785 var feedback = this.el.select('.form-control-feedback', true).first();
12788 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12790 if(this.getValue().length || this.forceFeedback){
12791 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12798 this.fireEvent('invalid', this, msg);
12801 SafariOnKeyDown : function(event)
12803 // this is a workaround for a password hang bug on chrome/ webkit.
12804 if (this.inputEl().dom.type != 'password') {
12808 var isSelectAll = false;
12810 if(this.inputEl().dom.selectionEnd > 0){
12811 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12813 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12814 event.preventDefault();
12819 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12821 event.preventDefault();
12822 // this is very hacky as keydown always get's upper case.
12824 var cc = String.fromCharCode(event.getCharCode());
12825 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12829 adjustWidth : function(tag, w){
12830 tag = tag.toLowerCase();
12831 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12832 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12833 if(tag == 'input'){
12836 if(tag == 'textarea'){
12839 }else if(Roo.isOpera){
12840 if(tag == 'input'){
12843 if(tag == 'textarea'){
12851 setFieldLabel : function(v)
12853 if(!this.rendered){
12857 if(this.indicatorEl()){
12858 var ar = this.el.select('label > span',true);
12860 if (ar.elements.length) {
12861 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12862 this.fieldLabel = v;
12866 var br = this.el.select('label',true);
12868 if(br.elements.length) {
12869 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12870 this.fieldLabel = v;
12874 Roo.log('Cannot Found any of label > span || label in input');
12878 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12879 this.fieldLabel = v;
12894 * @class Roo.bootstrap.TextArea
12895 * @extends Roo.bootstrap.Input
12896 * Bootstrap TextArea class
12897 * @cfg {Number} cols Specifies the visible width of a text area
12898 * @cfg {Number} rows Specifies the visible number of lines in a text area
12899 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12900 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12901 * @cfg {string} html text
12904 * Create a new TextArea
12905 * @param {Object} config The config object
12908 Roo.bootstrap.TextArea = function(config){
12909 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12913 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12923 getAutoCreate : function(){
12925 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12931 if(this.inputType != 'hidden'){
12932 cfg.cls = 'form-group' //input-group
12940 value : this.value || '',
12941 html: this.html || '',
12942 cls : 'form-control',
12943 placeholder : this.placeholder || ''
12947 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12948 input.maxLength = this.maxLength;
12952 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12956 input.cols = this.cols;
12959 if (this.readOnly) {
12960 input.readonly = true;
12964 input.name = this.name;
12968 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12972 ['xs','sm','md','lg'].map(function(size){
12973 if (settings[size]) {
12974 cfg.cls += ' col-' + size + '-' + settings[size];
12978 var inputblock = input;
12980 if(this.hasFeedback && !this.allowBlank){
12984 cls: 'glyphicon form-control-feedback'
12988 cls : 'has-feedback',
12997 if (this.before || this.after) {
13000 cls : 'input-group',
13004 inputblock.cn.push({
13006 cls : 'input-group-addon',
13011 inputblock.cn.push(input);
13013 if(this.hasFeedback && !this.allowBlank){
13014 inputblock.cls += ' has-feedback';
13015 inputblock.cn.push(feedback);
13019 inputblock.cn.push({
13021 cls : 'input-group-addon',
13028 if (align ==='left' && this.fieldLabel.length) {
13033 cls : 'control-label',
13034 html : this.fieldLabel
13045 if(this.labelWidth > 12){
13046 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13049 if(this.labelWidth < 13 && this.labelmd == 0){
13050 this.labelmd = this.labelWidth;
13053 if(this.labellg > 0){
13054 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13055 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13058 if(this.labelmd > 0){
13059 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13060 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13063 if(this.labelsm > 0){
13064 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13065 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13068 if(this.labelxs > 0){
13069 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13070 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13073 } else if ( this.fieldLabel.length) {
13078 //cls : 'input-group-addon',
13079 html : this.fieldLabel
13097 if (this.disabled) {
13098 input.disabled=true;
13105 * return the real textarea element.
13107 inputEl: function ()
13109 return this.el.select('textarea.form-control',true).first();
13113 * Clear any invalid styles/messages for this field
13115 clearInvalid : function()
13118 if(!this.el || this.preventMark){ // not rendered
13122 var label = this.el.select('label', true).first();
13123 var icon = this.el.select('i.fa-star', true).first();
13128 this.el.removeClass( this.validClass);
13129 this.inputEl().removeClass('is-invalid');
13131 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13133 var feedback = this.el.select('.form-control-feedback', true).first();
13136 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13141 this.fireEvent('valid', this);
13145 * Mark this field as valid
13147 markValid : function()
13149 if(!this.el || this.preventMark){ // not rendered
13153 this.el.removeClass([this.invalidClass, this.validClass]);
13154 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13156 var feedback = this.el.select('.form-control-feedback', true).first();
13159 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13162 if(this.disabled || this.allowBlank){
13166 var label = this.el.select('label', true).first();
13167 var icon = this.el.select('i.fa-star', true).first();
13172 if (Roo.bootstrap.version == 3) {
13173 this.el.addClass(this.validClass);
13175 this.inputEl().addClass('is-valid');
13179 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13181 var feedback = this.el.select('.form-control-feedback', true).first();
13184 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13185 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13190 this.fireEvent('valid', this);
13194 * Mark this field as invalid
13195 * @param {String} msg The validation message
13197 markInvalid : function(msg)
13199 if(!this.el || this.preventMark){ // not rendered
13203 this.el.removeClass([this.invalidClass, this.validClass]);
13204 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13206 var feedback = this.el.select('.form-control-feedback', true).first();
13209 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13212 if(this.disabled || this.allowBlank){
13216 var label = this.el.select('label', true).first();
13217 var icon = this.el.select('i.fa-star', true).first();
13219 if(!this.getValue().length && label && !icon){
13220 this.el.createChild({
13222 cls : 'text-danger fa fa-lg fa-star',
13223 tooltip : 'This field is required',
13224 style : 'margin-right:5px;'
13228 if (Roo.bootstrap.version == 3) {
13229 this.el.addClass(this.invalidClass);
13231 this.inputEl().addClass('is-invalid');
13234 // fixme ... this may be depricated need to test..
13235 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13237 var feedback = this.el.select('.form-control-feedback', true).first();
13240 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13242 if(this.getValue().length || this.forceFeedback){
13243 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13250 this.fireEvent('invalid', this, msg);
13258 * trigger field - base class for combo..
13263 * @class Roo.bootstrap.TriggerField
13264 * @extends Roo.bootstrap.Input
13265 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13266 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13267 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13268 * for which you can provide a custom implementation. For example:
13270 var trigger = new Roo.bootstrap.TriggerField();
13271 trigger.onTriggerClick = myTriggerFn;
13272 trigger.applyTo('my-field');
13275 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13276 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13277 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13278 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13279 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13282 * Create a new TriggerField.
13283 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13284 * to the base TextField)
13286 Roo.bootstrap.TriggerField = function(config){
13287 this.mimicing = false;
13288 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13291 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13293 * @cfg {String} triggerClass A CSS class to apply to the trigger
13296 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13301 * @cfg {Boolean} removable (true|false) special filter default false
13305 /** @cfg {Boolean} grow @hide */
13306 /** @cfg {Number} growMin @hide */
13307 /** @cfg {Number} growMax @hide */
13313 autoSize: Roo.emptyFn,
13317 deferHeight : true,
13320 actionMode : 'wrap',
13325 getAutoCreate : function(){
13327 var align = this.labelAlign || this.parentLabelAlign();
13332 cls: 'form-group' //input-group
13339 type : this.inputType,
13340 cls : 'form-control',
13341 autocomplete: 'new-password',
13342 placeholder : this.placeholder || ''
13346 input.name = this.name;
13349 input.cls += ' input-' + this.size;
13352 if (this.disabled) {
13353 input.disabled=true;
13356 var inputblock = input;
13358 if(this.hasFeedback && !this.allowBlank){
13362 cls: 'glyphicon form-control-feedback'
13365 if(this.removable && !this.editable ){
13367 cls : 'has-feedback',
13373 cls : 'roo-combo-removable-btn close'
13380 cls : 'has-feedback',
13389 if(this.removable && !this.editable ){
13391 cls : 'roo-removable',
13397 cls : 'roo-combo-removable-btn close'
13404 if (this.before || this.after) {
13407 cls : 'input-group',
13411 inputblock.cn.push({
13413 cls : 'input-group-addon input-group-prepend input-group-text',
13418 inputblock.cn.push(input);
13420 if(this.hasFeedback && !this.allowBlank){
13421 inputblock.cls += ' has-feedback';
13422 inputblock.cn.push(feedback);
13426 inputblock.cn.push({
13428 cls : 'input-group-addon input-group-append input-group-text',
13437 var ibwrap = inputblock;
13442 cls: 'roo-select2-choices',
13446 cls: 'roo-select2-search-field',
13458 cls: 'roo-select2-container input-group',
13463 cls: 'form-hidden-field'
13469 if(!this.multiple && this.showToggleBtn){
13475 if (this.caret != false) {
13478 cls: 'fa fa-' + this.caret
13485 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13487 Roo.bootstrap.version == 3 ? caret : '',
13490 cls: 'combobox-clear',
13504 combobox.cls += ' roo-select2-container-multi';
13508 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13509 tooltip : 'This field is required'
13511 if (Roo.bootstrap.version == 4) {
13514 style : 'display:none'
13519 if (align ==='left' && this.fieldLabel.length) {
13521 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13528 cls : 'control-label',
13529 html : this.fieldLabel
13541 var labelCfg = cfg.cn[1];
13542 var contentCfg = cfg.cn[2];
13544 if(this.indicatorpos == 'right'){
13549 cls : 'control-label',
13553 html : this.fieldLabel
13567 labelCfg = cfg.cn[0];
13568 contentCfg = cfg.cn[1];
13571 if(this.labelWidth > 12){
13572 labelCfg.style = "width: " + this.labelWidth + 'px';
13575 if(this.labelWidth < 13 && this.labelmd == 0){
13576 this.labelmd = this.labelWidth;
13579 if(this.labellg > 0){
13580 labelCfg.cls += ' col-lg-' + this.labellg;
13581 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13584 if(this.labelmd > 0){
13585 labelCfg.cls += ' col-md-' + this.labelmd;
13586 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13589 if(this.labelsm > 0){
13590 labelCfg.cls += ' col-sm-' + this.labelsm;
13591 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13594 if(this.labelxs > 0){
13595 labelCfg.cls += ' col-xs-' + this.labelxs;
13596 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13599 } else if ( this.fieldLabel.length) {
13600 // Roo.log(" label");
13605 //cls : 'input-group-addon',
13606 html : this.fieldLabel
13614 if(this.indicatorpos == 'right'){
13622 html : this.fieldLabel
13636 // Roo.log(" no label && no align");
13643 ['xs','sm','md','lg'].map(function(size){
13644 if (settings[size]) {
13645 cfg.cls += ' col-' + size + '-' + settings[size];
13656 onResize : function(w, h){
13657 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13658 // if(typeof w == 'number'){
13659 // var x = w - this.trigger.getWidth();
13660 // this.inputEl().setWidth(this.adjustWidth('input', x));
13661 // this.trigger.setStyle('left', x+'px');
13666 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13669 getResizeEl : function(){
13670 return this.inputEl();
13674 getPositionEl : function(){
13675 return this.inputEl();
13679 alignErrorIcon : function(){
13680 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13684 initEvents : function(){
13688 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13689 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13690 if(!this.multiple && this.showToggleBtn){
13691 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13692 if(this.hideTrigger){
13693 this.trigger.setDisplayed(false);
13695 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13699 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13702 if(this.removable && !this.editable && !this.tickable){
13703 var close = this.closeTriggerEl();
13706 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13707 close.on('click', this.removeBtnClick, this, close);
13711 //this.trigger.addClassOnOver('x-form-trigger-over');
13712 //this.trigger.addClassOnClick('x-form-trigger-click');
13715 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13719 closeTriggerEl : function()
13721 var close = this.el.select('.roo-combo-removable-btn', true).first();
13722 return close ? close : false;
13725 removeBtnClick : function(e, h, el)
13727 e.preventDefault();
13729 if(this.fireEvent("remove", this) !== false){
13731 this.fireEvent("afterremove", this)
13735 createList : function()
13737 this.list = Roo.get(document.body).createChild({
13738 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13739 cls: 'typeahead typeahead-long dropdown-menu shadow',
13740 style: 'display:none'
13743 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13748 initTrigger : function(){
13753 onDestroy : function(){
13755 this.trigger.removeAllListeners();
13756 // this.trigger.remove();
13759 // this.wrap.remove();
13761 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13765 onFocus : function(){
13766 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13768 if(!this.mimicing){
13769 this.wrap.addClass('x-trigger-wrap-focus');
13770 this.mimicing = true;
13771 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13772 if(this.monitorTab){
13773 this.el.on("keydown", this.checkTab, this);
13780 checkTab : function(e){
13781 if(e.getKey() == e.TAB){
13782 this.triggerBlur();
13787 onBlur : function(){
13792 mimicBlur : function(e, t){
13794 if(!this.wrap.contains(t) && this.validateBlur()){
13795 this.triggerBlur();
13801 triggerBlur : function(){
13802 this.mimicing = false;
13803 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13804 if(this.monitorTab){
13805 this.el.un("keydown", this.checkTab, this);
13807 //this.wrap.removeClass('x-trigger-wrap-focus');
13808 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13812 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13813 validateBlur : function(e, t){
13818 onDisable : function(){
13819 this.inputEl().dom.disabled = true;
13820 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13822 // this.wrap.addClass('x-item-disabled');
13827 onEnable : function(){
13828 this.inputEl().dom.disabled = false;
13829 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13831 // this.el.removeClass('x-item-disabled');
13836 onShow : function(){
13837 var ae = this.getActionEl();
13840 ae.dom.style.display = '';
13841 ae.dom.style.visibility = 'visible';
13847 onHide : function(){
13848 var ae = this.getActionEl();
13849 ae.dom.style.display = 'none';
13853 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13854 * by an implementing function.
13856 * @param {EventObject} e
13858 onTriggerClick : Roo.emptyFn
13866 * @class Roo.bootstrap.CardUploader
13867 * @extends Roo.bootstrap.Button
13868 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13869 * @cfg {Number} errorTimeout default 3000
13870 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13871 * @cfg {Array} html The button text.
13875 * Create a new CardUploader
13876 * @param {Object} config The config object
13879 Roo.bootstrap.CardUploader = function(config){
13883 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13886 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13894 * When a image is clicked on - and needs to display a slideshow or similar..
13895 * @param {Roo.bootstrap.Card} this
13896 * @param {Object} The image information data
13902 * When a the download link is clicked
13903 * @param {Roo.bootstrap.Card} this
13904 * @param {Object} The image information data contains
13911 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13914 errorTimeout : 3000,
13918 fileCollection : false,
13921 getAutoCreate : function()
13925 cls :'form-group' ,
13930 //cls : 'input-group-addon',
13931 html : this.fieldLabel
13939 value : this.value,
13940 cls : 'd-none form-control'
13945 multiple : 'multiple',
13947 cls : 'd-none roo-card-upload-selector'
13951 cls : 'roo-card-uploader-button-container w-100 mb-2'
13954 cls : 'card-columns roo-card-uploader-container'
13964 getChildContainer : function() /// what children are added to.
13966 return this.containerEl;
13969 getButtonContainer : function() /// what children are added to.
13971 return this.el.select(".roo-card-uploader-button-container").first();
13974 initEvents : function()
13977 Roo.bootstrap.Input.prototype.initEvents.call(this);
13981 xns: Roo.bootstrap,
13984 container_method : 'getButtonContainer' ,
13985 html : this.html, // fix changable?
13988 'click' : function(btn, e) {
13997 this.urlAPI = (window.createObjectURL && window) ||
13998 (window.URL && URL.revokeObjectURL && URL) ||
13999 (window.webkitURL && webkitURL);
14004 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14006 this.selectorEl.on('change', this.onFileSelected, this);
14009 this.images.forEach(function(img) {
14012 this.images = false;
14014 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14020 onClick : function(e)
14022 e.preventDefault();
14024 this.selectorEl.dom.click();
14028 onFileSelected : function(e)
14030 e.preventDefault();
14032 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14036 Roo.each(this.selectorEl.dom.files, function(file){
14037 this.addFile(file);
14046 addFile : function(file)
14049 if(typeof(file) === 'string'){
14050 throw "Add file by name?"; // should not happen
14054 if(!file || !this.urlAPI){
14064 var url = _this.urlAPI.createObjectURL( file);
14067 id : Roo.bootstrap.CardUploader.ID--,
14068 is_uploaded : false,
14072 mimetype : file.type,
14080 * addCard - add an Attachment to the uploader
14081 * @param data - the data about the image to upload
14085 title : "Title of file",
14086 is_uploaded : false,
14087 src : "http://.....",
14088 srcfile : { the File upload object },
14089 mimetype : file.type,
14092 .. any other data...
14098 addCard : function (data)
14100 // hidden input element?
14101 // if the file is not an image...
14102 //then we need to use something other that and header_image
14107 xns : Roo.bootstrap,
14108 xtype : 'CardFooter',
14111 xns : Roo.bootstrap,
14117 xns : Roo.bootstrap,
14119 html : String.format("<small>{0}</small>", data.title),
14120 cls : 'col-10 text-left',
14125 click : function() {
14127 t.fireEvent( "download", t, data );
14133 xns : Roo.bootstrap,
14135 style: 'max-height: 28px; ',
14141 click : function() {
14142 t.removeCard(data.id)
14154 var cn = this.addxtype(
14157 xns : Roo.bootstrap,
14160 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14161 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14162 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14167 initEvents : function() {
14168 Roo.bootstrap.Card.prototype.initEvents.call(this);
14170 this.imgEl = this.el.select('.card-img-top').first();
14172 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14173 this.imgEl.set({ 'pointer' : 'cursor' });
14176 this.getCardFooter().addClass('p-1');
14183 // dont' really need ot update items.
14184 // this.items.push(cn);
14185 this.fileCollection.add(cn);
14187 if (!data.srcfile) {
14188 this.updateInput();
14193 var reader = new FileReader();
14194 reader.addEventListener("load", function() {
14195 data.srcdata = reader.result;
14198 reader.readAsDataURL(data.srcfile);
14203 removeCard : function(id)
14206 var card = this.fileCollection.get(id);
14207 card.data.is_deleted = 1;
14208 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14209 //this.fileCollection.remove(card);
14210 //this.items = this.items.filter(function(e) { return e != card });
14211 // dont' really need ot update items.
14212 card.el.dom.parentNode.removeChild(card.el.dom);
14213 this.updateInput();
14219 this.fileCollection.each(function(card) {
14220 if (card.el.dom && card.el.dom.parentNode) {
14221 card.el.dom.parentNode.removeChild(card.el.dom);
14224 this.fileCollection.clear();
14225 this.updateInput();
14228 updateInput : function()
14231 this.fileCollection.each(function(e) {
14235 this.inputEl().dom.value = JSON.stringify(data);
14245 Roo.bootstrap.CardUploader.ID = -1;/*
14247 * Ext JS Library 1.1.1
14248 * Copyright(c) 2006-2007, Ext JS, LLC.
14250 * Originally Released Under LGPL - original licence link has changed is not relivant.
14253 * <script type="text/javascript">
14258 * @class Roo.data.SortTypes
14260 * Defines the default sorting (casting?) comparison functions used when sorting data.
14262 Roo.data.SortTypes = {
14264 * Default sort that does nothing
14265 * @param {Mixed} s The value being converted
14266 * @return {Mixed} The comparison value
14268 none : function(s){
14273 * The regular expression used to strip tags
14277 stripTagsRE : /<\/?[^>]+>/gi,
14280 * Strips all HTML tags to sort on text only
14281 * @param {Mixed} s The value being converted
14282 * @return {String} The comparison value
14284 asText : function(s){
14285 return String(s).replace(this.stripTagsRE, "");
14289 * Strips all HTML tags to sort on text only - Case insensitive
14290 * @param {Mixed} s The value being converted
14291 * @return {String} The comparison value
14293 asUCText : function(s){
14294 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14298 * Case insensitive string
14299 * @param {Mixed} s The value being converted
14300 * @return {String} The comparison value
14302 asUCString : function(s) {
14303 return String(s).toUpperCase();
14308 * @param {Mixed} s The value being converted
14309 * @return {Number} The comparison value
14311 asDate : function(s) {
14315 if(s instanceof Date){
14316 return s.getTime();
14318 return Date.parse(String(s));
14323 * @param {Mixed} s The value being converted
14324 * @return {Float} The comparison value
14326 asFloat : function(s) {
14327 var val = parseFloat(String(s).replace(/,/g, ""));
14336 * @param {Mixed} s The value being converted
14337 * @return {Number} The comparison value
14339 asInt : function(s) {
14340 var val = parseInt(String(s).replace(/,/g, ""));
14348 * Ext JS Library 1.1.1
14349 * Copyright(c) 2006-2007, Ext JS, LLC.
14351 * Originally Released Under LGPL - original licence link has changed is not relivant.
14354 * <script type="text/javascript">
14358 * @class Roo.data.Record
14359 * Instances of this class encapsulate both record <em>definition</em> information, and record
14360 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14361 * to access Records cached in an {@link Roo.data.Store} object.<br>
14363 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14364 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14367 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14369 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14370 * {@link #create}. The parameters are the same.
14371 * @param {Array} data An associative Array of data values keyed by the field name.
14372 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14373 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14374 * not specified an integer id is generated.
14376 Roo.data.Record = function(data, id){
14377 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14382 * Generate a constructor for a specific record layout.
14383 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14384 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14385 * Each field definition object may contain the following properties: <ul>
14386 * <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,
14387 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14388 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14389 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14390 * is being used, then this is a string containing the javascript expression to reference the data relative to
14391 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14392 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14393 * this may be omitted.</p></li>
14394 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14395 * <ul><li>auto (Default, implies no conversion)</li>
14400 * <li>date</li></ul></p></li>
14401 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14402 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14403 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14404 * by the Reader into an object that will be stored in the Record. It is passed the
14405 * following parameters:<ul>
14406 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14408 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14410 * <br>usage:<br><pre><code>
14411 var TopicRecord = Roo.data.Record.create(
14412 {name: 'title', mapping: 'topic_title'},
14413 {name: 'author', mapping: 'username'},
14414 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14415 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14416 {name: 'lastPoster', mapping: 'user2'},
14417 {name: 'excerpt', mapping: 'post_text'}
14420 var myNewRecord = new TopicRecord({
14421 title: 'Do my job please',
14424 lastPost: new Date(),
14425 lastPoster: 'Animal',
14426 excerpt: 'No way dude!'
14428 myStore.add(myNewRecord);
14433 Roo.data.Record.create = function(o){
14434 var f = function(){
14435 f.superclass.constructor.apply(this, arguments);
14437 Roo.extend(f, Roo.data.Record);
14438 var p = f.prototype;
14439 p.fields = new Roo.util.MixedCollection(false, function(field){
14442 for(var i = 0, len = o.length; i < len; i++){
14443 p.fields.add(new Roo.data.Field(o[i]));
14445 f.getField = function(name){
14446 return p.fields.get(name);
14451 Roo.data.Record.AUTO_ID = 1000;
14452 Roo.data.Record.EDIT = 'edit';
14453 Roo.data.Record.REJECT = 'reject';
14454 Roo.data.Record.COMMIT = 'commit';
14456 Roo.data.Record.prototype = {
14458 * Readonly flag - true if this record has been modified.
14467 join : function(store){
14468 this.store = store;
14472 * Set the named field to the specified value.
14473 * @param {String} name The name of the field to set.
14474 * @param {Object} value The value to set the field to.
14476 set : function(name, value){
14477 if(this.data[name] == value){
14481 if(!this.modified){
14482 this.modified = {};
14484 if(typeof this.modified[name] == 'undefined'){
14485 this.modified[name] = this.data[name];
14487 this.data[name] = value;
14488 if(!this.editing && this.store){
14489 this.store.afterEdit(this);
14494 * Get the value of the named field.
14495 * @param {String} name The name of the field to get the value of.
14496 * @return {Object} The value of the field.
14498 get : function(name){
14499 return this.data[name];
14503 beginEdit : function(){
14504 this.editing = true;
14505 this.modified = {};
14509 cancelEdit : function(){
14510 this.editing = false;
14511 delete this.modified;
14515 endEdit : function(){
14516 this.editing = false;
14517 if(this.dirty && this.store){
14518 this.store.afterEdit(this);
14523 * Usually called by the {@link Roo.data.Store} which owns the Record.
14524 * Rejects all changes made to the Record since either creation, or the last commit operation.
14525 * Modified fields are reverted to their original values.
14527 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14528 * of reject operations.
14530 reject : function(){
14531 var m = this.modified;
14533 if(typeof m[n] != "function"){
14534 this.data[n] = m[n];
14537 this.dirty = false;
14538 delete this.modified;
14539 this.editing = false;
14541 this.store.afterReject(this);
14546 * Usually called by the {@link Roo.data.Store} which owns the Record.
14547 * Commits all changes made to the Record since either creation, or the last commit operation.
14549 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14550 * of commit operations.
14552 commit : function(){
14553 this.dirty = false;
14554 delete this.modified;
14555 this.editing = false;
14557 this.store.afterCommit(this);
14562 hasError : function(){
14563 return this.error != null;
14567 clearError : function(){
14572 * Creates a copy of this record.
14573 * @param {String} id (optional) A new record id if you don't want to use this record's id
14576 copy : function(newId) {
14577 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14581 * Ext JS Library 1.1.1
14582 * Copyright(c) 2006-2007, Ext JS, LLC.
14584 * Originally Released Under LGPL - original licence link has changed is not relivant.
14587 * <script type="text/javascript">
14593 * @class Roo.data.Store
14594 * @extends Roo.util.Observable
14595 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14596 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14598 * 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
14599 * has no knowledge of the format of the data returned by the Proxy.<br>
14601 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14602 * instances from the data object. These records are cached and made available through accessor functions.
14604 * Creates a new Store.
14605 * @param {Object} config A config object containing the objects needed for the Store to access data,
14606 * and read the data into Records.
14608 Roo.data.Store = function(config){
14609 this.data = new Roo.util.MixedCollection(false);
14610 this.data.getKey = function(o){
14613 this.baseParams = {};
14615 this.paramNames = {
14620 "multisort" : "_multisort"
14623 if(config && config.data){
14624 this.inlineData = config.data;
14625 delete config.data;
14628 Roo.apply(this, config);
14630 if(this.reader){ // reader passed
14631 this.reader = Roo.factory(this.reader, Roo.data);
14632 this.reader.xmodule = this.xmodule || false;
14633 if(!this.recordType){
14634 this.recordType = this.reader.recordType;
14636 if(this.reader.onMetaChange){
14637 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14641 if(this.recordType){
14642 this.fields = this.recordType.prototype.fields;
14644 this.modified = [];
14648 * @event datachanged
14649 * Fires when the data cache has changed, and a widget which is using this Store
14650 * as a Record cache should refresh its view.
14651 * @param {Store} this
14653 datachanged : true,
14655 * @event metachange
14656 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14657 * @param {Store} this
14658 * @param {Object} meta The JSON metadata
14663 * Fires when Records have been added to the Store
14664 * @param {Store} this
14665 * @param {Roo.data.Record[]} records The array of Records added
14666 * @param {Number} index The index at which the record(s) were added
14671 * Fires when a Record has been removed from the Store
14672 * @param {Store} this
14673 * @param {Roo.data.Record} record The Record that was removed
14674 * @param {Number} index The index at which the record was removed
14679 * Fires when a Record has been updated
14680 * @param {Store} this
14681 * @param {Roo.data.Record} record The Record that was updated
14682 * @param {String} operation The update operation being performed. Value may be one of:
14684 Roo.data.Record.EDIT
14685 Roo.data.Record.REJECT
14686 Roo.data.Record.COMMIT
14692 * Fires when the data cache has been cleared.
14693 * @param {Store} this
14697 * @event beforeload
14698 * Fires before a request is made for a new data object. If the beforeload handler returns false
14699 * the load action will be canceled.
14700 * @param {Store} this
14701 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14705 * @event beforeloadadd
14706 * Fires after a new set of Records has been loaded.
14707 * @param {Store} this
14708 * @param {Roo.data.Record[]} records The Records that were loaded
14709 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14711 beforeloadadd : true,
14714 * Fires after a new set of Records has been loaded, before they are added to the store.
14715 * @param {Store} this
14716 * @param {Roo.data.Record[]} records The Records that were loaded
14717 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14718 * @params {Object} return from reader
14722 * @event loadexception
14723 * Fires if an exception occurs in the Proxy during loading.
14724 * Called with the signature of the Proxy's "loadexception" event.
14725 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14728 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14729 * @param {Object} load options
14730 * @param {Object} jsonData from your request (normally this contains the Exception)
14732 loadexception : true
14736 this.proxy = Roo.factory(this.proxy, Roo.data);
14737 this.proxy.xmodule = this.xmodule || false;
14738 this.relayEvents(this.proxy, ["loadexception"]);
14740 this.sortToggle = {};
14741 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14743 Roo.data.Store.superclass.constructor.call(this);
14745 if(this.inlineData){
14746 this.loadData(this.inlineData);
14747 delete this.inlineData;
14751 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14753 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14754 * without a remote query - used by combo/forms at present.
14758 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14761 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14764 * @cfg {Roo.data.Reader} reader [required] The Reader object which processes the data object and returns
14765 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14768 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14769 * on any HTTP request
14772 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14775 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14779 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14780 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14782 remoteSort : false,
14785 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14786 * loaded or when a record is removed. (defaults to false).
14788 pruneModifiedRecords : false,
14791 lastOptions : null,
14794 * Add Records to the Store and fires the add event.
14795 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14797 add : function(records){
14798 records = [].concat(records);
14799 for(var i = 0, len = records.length; i < len; i++){
14800 records[i].join(this);
14802 var index = this.data.length;
14803 this.data.addAll(records);
14804 this.fireEvent("add", this, records, index);
14808 * Remove a Record from the Store and fires the remove event.
14809 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14811 remove : function(record){
14812 var index = this.data.indexOf(record);
14813 this.data.removeAt(index);
14815 if(this.pruneModifiedRecords){
14816 this.modified.remove(record);
14818 this.fireEvent("remove", this, record, index);
14822 * Remove all Records from the Store and fires the clear event.
14824 removeAll : function(){
14826 if(this.pruneModifiedRecords){
14827 this.modified = [];
14829 this.fireEvent("clear", this);
14833 * Inserts Records to the Store at the given index and fires the add event.
14834 * @param {Number} index The start index at which to insert the passed Records.
14835 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14837 insert : function(index, records){
14838 records = [].concat(records);
14839 for(var i = 0, len = records.length; i < len; i++){
14840 this.data.insert(index, records[i]);
14841 records[i].join(this);
14843 this.fireEvent("add", this, records, index);
14847 * Get the index within the cache of the passed Record.
14848 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14849 * @return {Number} The index of the passed Record. Returns -1 if not found.
14851 indexOf : function(record){
14852 return this.data.indexOf(record);
14856 * Get the index within the cache of the Record with the passed id.
14857 * @param {String} id The id of the Record to find.
14858 * @return {Number} The index of the Record. Returns -1 if not found.
14860 indexOfId : function(id){
14861 return this.data.indexOfKey(id);
14865 * Get the Record with the specified id.
14866 * @param {String} id The id of the Record to find.
14867 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14869 getById : function(id){
14870 return this.data.key(id);
14874 * Get the Record at the specified index.
14875 * @param {Number} index The index of the Record to find.
14876 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14878 getAt : function(index){
14879 return this.data.itemAt(index);
14883 * Returns a range of Records between specified indices.
14884 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14885 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14886 * @return {Roo.data.Record[]} An array of Records
14888 getRange : function(start, end){
14889 return this.data.getRange(start, end);
14893 storeOptions : function(o){
14894 o = Roo.apply({}, o);
14897 this.lastOptions = o;
14901 * Loads the Record cache from the configured Proxy using the configured Reader.
14903 * If using remote paging, then the first load call must specify the <em>start</em>
14904 * and <em>limit</em> properties in the options.params property to establish the initial
14905 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14907 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14908 * and this call will return before the new data has been loaded. Perform any post-processing
14909 * in a callback function, or in a "load" event handler.</strong>
14911 * @param {Object} options An object containing properties which control loading options:<ul>
14912 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14913 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14914 * passed the following arguments:<ul>
14915 * <li>r : Roo.data.Record[]</li>
14916 * <li>options: Options object from the load call</li>
14917 * <li>success: Boolean success indicator</li></ul></li>
14918 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14919 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14922 load : function(options){
14923 options = options || {};
14924 if(this.fireEvent("beforeload", this, options) !== false){
14925 this.storeOptions(options);
14926 var p = Roo.apply(options.params || {}, this.baseParams);
14927 // if meta was not loaded from remote source.. try requesting it.
14928 if (!this.reader.metaFromRemote) {
14929 p._requestMeta = 1;
14931 if(this.sortInfo && this.remoteSort){
14932 var pn = this.paramNames;
14933 p[pn["sort"]] = this.sortInfo.field;
14934 p[pn["dir"]] = this.sortInfo.direction;
14936 if (this.multiSort) {
14937 var pn = this.paramNames;
14938 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14941 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14946 * Reloads the Record cache from the configured Proxy using the configured Reader and
14947 * the options from the last load operation performed.
14948 * @param {Object} options (optional) An object containing properties which may override the options
14949 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14950 * the most recently used options are reused).
14952 reload : function(options){
14953 this.load(Roo.applyIf(options||{}, this.lastOptions));
14957 // Called as a callback by the Reader during a load operation.
14958 loadRecords : function(o, options, success){
14959 if(!o || success === false){
14960 if(success !== false){
14961 this.fireEvent("load", this, [], options, o);
14963 if(options.callback){
14964 options.callback.call(options.scope || this, [], options, false);
14968 // if data returned failure - throw an exception.
14969 if (o.success === false) {
14970 // show a message if no listener is registered.
14971 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14972 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14974 // loadmask wil be hooked into this..
14975 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14978 var r = o.records, t = o.totalRecords || r.length;
14980 this.fireEvent("beforeloadadd", this, r, options, o);
14982 if(!options || options.add !== true){
14983 if(this.pruneModifiedRecords){
14984 this.modified = [];
14986 for(var i = 0, len = r.length; i < len; i++){
14990 this.data = this.snapshot;
14991 delete this.snapshot;
14994 this.data.addAll(r);
14995 this.totalLength = t;
14997 this.fireEvent("datachanged", this);
14999 this.totalLength = Math.max(t, this.data.length+r.length);
15003 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15005 var e = new Roo.data.Record({});
15007 e.set(this.parent.displayField, this.parent.emptyTitle);
15008 e.set(this.parent.valueField, '');
15013 this.fireEvent("load", this, r, options, o);
15014 if(options.callback){
15015 options.callback.call(options.scope || this, r, options, true);
15021 * Loads data from a passed data block. A Reader which understands the format of the data
15022 * must have been configured in the constructor.
15023 * @param {Object} data The data block from which to read the Records. The format of the data expected
15024 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15025 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15027 loadData : function(o, append){
15028 var r = this.reader.readRecords(o);
15029 this.loadRecords(r, {add: append}, true);
15033 * using 'cn' the nested child reader read the child array into it's child stores.
15034 * @param {Object} rec The record with a 'children array
15036 loadDataFromChildren : function(rec)
15038 this.loadData(this.reader.toLoadData(rec));
15043 * Gets the number of cached records.
15045 * <em>If using paging, this may not be the total size of the dataset. If the data object
15046 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15047 * the data set size</em>
15049 getCount : function(){
15050 return this.data.length || 0;
15054 * Gets the total number of records in the dataset as returned by the server.
15056 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15057 * the dataset size</em>
15059 getTotalCount : function(){
15060 return this.totalLength || 0;
15064 * Returns the sort state of the Store as an object with two properties:
15066 field {String} The name of the field by which the Records are sorted
15067 direction {String} The sort order, "ASC" or "DESC"
15070 getSortState : function(){
15071 return this.sortInfo;
15075 applySort : function(){
15076 if(this.sortInfo && !this.remoteSort){
15077 var s = this.sortInfo, f = s.field;
15078 var st = this.fields.get(f).sortType;
15079 var fn = function(r1, r2){
15080 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15081 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15083 this.data.sort(s.direction, fn);
15084 if(this.snapshot && this.snapshot != this.data){
15085 this.snapshot.sort(s.direction, fn);
15091 * Sets the default sort column and order to be used by the next load operation.
15092 * @param {String} fieldName The name of the field to sort by.
15093 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15095 setDefaultSort : function(field, dir){
15096 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15100 * Sort the Records.
15101 * If remote sorting is used, the sort is performed on the server, and the cache is
15102 * reloaded. If local sorting is used, the cache is sorted internally.
15103 * @param {String} fieldName The name of the field to sort by.
15104 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15106 sort : function(fieldName, dir){
15107 var f = this.fields.get(fieldName);
15109 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15111 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15112 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15117 this.sortToggle[f.name] = dir;
15118 this.sortInfo = {field: f.name, direction: dir};
15119 if(!this.remoteSort){
15121 this.fireEvent("datachanged", this);
15123 this.load(this.lastOptions);
15128 * Calls the specified function for each of the Records in the cache.
15129 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15130 * Returning <em>false</em> aborts and exits the iteration.
15131 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15133 each : function(fn, scope){
15134 this.data.each(fn, scope);
15138 * Gets all records modified since the last commit. Modified records are persisted across load operations
15139 * (e.g., during paging).
15140 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15142 getModifiedRecords : function(){
15143 return this.modified;
15147 createFilterFn : function(property, value, anyMatch){
15148 if(!value.exec){ // not a regex
15149 value = String(value);
15150 if(value.length == 0){
15153 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15155 return function(r){
15156 return value.test(r.data[property]);
15161 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15162 * @param {String} property A field on your records
15163 * @param {Number} start The record index to start at (defaults to 0)
15164 * @param {Number} end The last record index to include (defaults to length - 1)
15165 * @return {Number} The sum
15167 sum : function(property, start, end){
15168 var rs = this.data.items, v = 0;
15169 start = start || 0;
15170 end = (end || end === 0) ? end : rs.length-1;
15172 for(var i = start; i <= end; i++){
15173 v += (rs[i].data[property] || 0);
15179 * Filter the records by a specified property.
15180 * @param {String} field A field on your records
15181 * @param {String/RegExp} value Either a string that the field
15182 * should start with or a RegExp to test against the field
15183 * @param {Boolean} anyMatch True to match any part not just the beginning
15185 filter : function(property, value, anyMatch){
15186 var fn = this.createFilterFn(property, value, anyMatch);
15187 return fn ? this.filterBy(fn) : this.clearFilter();
15191 * Filter by a function. The specified function will be called with each
15192 * record in this data source. If the function returns true the record is included,
15193 * otherwise it is filtered.
15194 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15195 * @param {Object} scope (optional) The scope of the function (defaults to this)
15197 filterBy : function(fn, scope){
15198 this.snapshot = this.snapshot || this.data;
15199 this.data = this.queryBy(fn, scope||this);
15200 this.fireEvent("datachanged", this);
15204 * Query the records by a specified property.
15205 * @param {String} field A field on your records
15206 * @param {String/RegExp} value Either a string that the field
15207 * should start with or a RegExp to test against the field
15208 * @param {Boolean} anyMatch True to match any part not just the beginning
15209 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15211 query : function(property, value, anyMatch){
15212 var fn = this.createFilterFn(property, value, anyMatch);
15213 return fn ? this.queryBy(fn) : this.data.clone();
15217 * Query by a function. The specified function will be called with each
15218 * record in this data source. If the function returns true the record is included
15220 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15221 * @param {Object} scope (optional) The scope of the function (defaults to this)
15222 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15224 queryBy : function(fn, scope){
15225 var data = this.snapshot || this.data;
15226 return data.filterBy(fn, scope||this);
15230 * Collects unique values for a particular dataIndex from this store.
15231 * @param {String} dataIndex The property to collect
15232 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15233 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15234 * @return {Array} An array of the unique values
15236 collect : function(dataIndex, allowNull, bypassFilter){
15237 var d = (bypassFilter === true && this.snapshot) ?
15238 this.snapshot.items : this.data.items;
15239 var v, sv, r = [], l = {};
15240 for(var i = 0, len = d.length; i < len; i++){
15241 v = d[i].data[dataIndex];
15243 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15252 * Revert to a view of the Record cache with no filtering applied.
15253 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15255 clearFilter : function(suppressEvent){
15256 if(this.snapshot && this.snapshot != this.data){
15257 this.data = this.snapshot;
15258 delete this.snapshot;
15259 if(suppressEvent !== true){
15260 this.fireEvent("datachanged", this);
15266 afterEdit : function(record){
15267 if(this.modified.indexOf(record) == -1){
15268 this.modified.push(record);
15270 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15274 afterReject : function(record){
15275 this.modified.remove(record);
15276 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15280 afterCommit : function(record){
15281 this.modified.remove(record);
15282 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15286 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15287 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15289 commitChanges : function(){
15290 var m = this.modified.slice(0);
15291 this.modified = [];
15292 for(var i = 0, len = m.length; i < len; i++){
15298 * Cancel outstanding changes on all changed records.
15300 rejectChanges : function(){
15301 var m = this.modified.slice(0);
15302 this.modified = [];
15303 for(var i = 0, len = m.length; i < len; i++){
15308 onMetaChange : function(meta, rtype, o){
15309 this.recordType = rtype;
15310 this.fields = rtype.prototype.fields;
15311 delete this.snapshot;
15312 this.sortInfo = meta.sortInfo || this.sortInfo;
15313 this.modified = [];
15314 this.fireEvent('metachange', this, this.reader.meta);
15317 moveIndex : function(data, type)
15319 var index = this.indexOf(data);
15321 var newIndex = index + type;
15325 this.insert(newIndex, data);
15330 * Ext JS Library 1.1.1
15331 * Copyright(c) 2006-2007, Ext JS, LLC.
15333 * Originally Released Under LGPL - original licence link has changed is not relivant.
15336 * <script type="text/javascript">
15340 * @class Roo.data.SimpleStore
15341 * @extends Roo.data.Store
15342 * Small helper class to make creating Stores from Array data easier.
15343 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15344 * @cfg {Array} fields An array of field definition objects, or field name strings.
15345 * @cfg {Object} an existing reader (eg. copied from another store)
15346 * @cfg {Array} data The multi-dimensional array of data
15347 * @cfg {Roo.data.DataProxy} proxy [not-required]
15348 * @cfg {Roo.data.Reader} reader [not-required]
15350 * @param {Object} config
15352 Roo.data.SimpleStore = function(config)
15354 Roo.data.SimpleStore.superclass.constructor.call(this, {
15356 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15359 Roo.data.Record.create(config.fields)
15361 proxy : new Roo.data.MemoryProxy(config.data)
15365 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15367 * Ext JS Library 1.1.1
15368 * Copyright(c) 2006-2007, Ext JS, LLC.
15370 * Originally Released Under LGPL - original licence link has changed is not relivant.
15373 * <script type="text/javascript">
15378 * @extends Roo.data.Store
15379 * @class Roo.data.JsonStore
15380 * Small helper class to make creating Stores for JSON data easier. <br/>
15382 var store = new Roo.data.JsonStore({
15383 url: 'get-images.php',
15385 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15388 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15389 * JsonReader and HttpProxy (unless inline data is provided).</b>
15390 * @cfg {Array} fields An array of field definition objects, or field name strings.
15392 * @param {Object} config
15394 Roo.data.JsonStore = function(c){
15395 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15396 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15397 reader: new Roo.data.JsonReader(c, c.fields)
15400 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15402 * Ext JS Library 1.1.1
15403 * Copyright(c) 2006-2007, Ext JS, LLC.
15405 * Originally Released Under LGPL - original licence link has changed is not relivant.
15408 * <script type="text/javascript">
15412 Roo.data.Field = function(config){
15413 if(typeof config == "string"){
15414 config = {name: config};
15416 Roo.apply(this, config);
15419 this.type = "auto";
15422 var st = Roo.data.SortTypes;
15423 // named sortTypes are supported, here we look them up
15424 if(typeof this.sortType == "string"){
15425 this.sortType = st[this.sortType];
15428 // set default sortType for strings and dates
15429 if(!this.sortType){
15432 this.sortType = st.asUCString;
15435 this.sortType = st.asDate;
15438 this.sortType = st.none;
15443 var stripRe = /[\$,%]/g;
15445 // prebuilt conversion function for this field, instead of
15446 // switching every time we're reading a value
15448 var cv, dateFormat = this.dateFormat;
15453 cv = function(v){ return v; };
15456 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15460 return v !== undefined && v !== null && v !== '' ?
15461 parseInt(String(v).replace(stripRe, ""), 10) : '';
15466 return v !== undefined && v !== null && v !== '' ?
15467 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15472 cv = function(v){ return v === true || v === "true" || v == 1; };
15479 if(v instanceof Date){
15483 if(dateFormat == "timestamp"){
15484 return new Date(v*1000);
15486 return Date.parseDate(v, dateFormat);
15488 var parsed = Date.parse(v);
15489 return parsed ? new Date(parsed) : null;
15498 Roo.data.Field.prototype = {
15506 * Ext JS Library 1.1.1
15507 * Copyright(c) 2006-2007, Ext JS, LLC.
15509 * Originally Released Under LGPL - original licence link has changed is not relivant.
15512 * <script type="text/javascript">
15515 // Base class for reading structured data from a data source. This class is intended to be
15516 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15519 * @class Roo.data.DataReader
15520 * Base class for reading structured data from a data source. This class is intended to be
15521 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15524 Roo.data.DataReader = function(meta, recordType){
15528 this.recordType = recordType instanceof Array ?
15529 Roo.data.Record.create(recordType) : recordType;
15532 Roo.data.DataReader.prototype = {
15535 readerType : 'Data',
15537 * Create an empty record
15538 * @param {Object} data (optional) - overlay some values
15539 * @return {Roo.data.Record} record created.
15541 newRow : function(d) {
15543 this.recordType.prototype.fields.each(function(c) {
15545 case 'int' : da[c.name] = 0; break;
15546 case 'date' : da[c.name] = new Date(); break;
15547 case 'float' : da[c.name] = 0.0; break;
15548 case 'boolean' : da[c.name] = false; break;
15549 default : da[c.name] = ""; break;
15553 return new this.recordType(Roo.apply(da, d));
15559 * Ext JS Library 1.1.1
15560 * Copyright(c) 2006-2007, Ext JS, LLC.
15562 * Originally Released Under LGPL - original licence link has changed is not relivant.
15565 * <script type="text/javascript">
15569 * @class Roo.data.DataProxy
15570 * @extends Roo.data.Observable
15572 * This class is an abstract base class for implementations which provide retrieval of
15573 * unformatted data objects.<br>
15575 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15576 * (of the appropriate type which knows how to parse the data object) to provide a block of
15577 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15579 * Custom implementations must implement the load method as described in
15580 * {@link Roo.data.HttpProxy#load}.
15582 Roo.data.DataProxy = function(){
15585 * @event beforeload
15586 * Fires before a network request is made to retrieve a data object.
15587 * @param {Object} This DataProxy object.
15588 * @param {Object} params The params parameter to the load function.
15593 * Fires before the load method's callback is called.
15594 * @param {Object} This DataProxy object.
15595 * @param {Object} o The data object.
15596 * @param {Object} arg The callback argument object passed to the load function.
15600 * @event loadexception
15601 * Fires if an Exception occurs during data retrieval.
15602 * @param {Object} This DataProxy object.
15603 * @param {Object} o The data object.
15604 * @param {Object} arg The callback argument object passed to the load function.
15605 * @param {Object} e The Exception.
15607 loadexception : true
15609 Roo.data.DataProxy.superclass.constructor.call(this);
15612 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15615 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15619 * Ext JS Library 1.1.1
15620 * Copyright(c) 2006-2007, Ext JS, LLC.
15622 * Originally Released Under LGPL - original licence link has changed is not relivant.
15625 * <script type="text/javascript">
15628 * @class Roo.data.MemoryProxy
15629 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15630 * to the Reader when its load method is called.
15632 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15634 Roo.data.MemoryProxy = function(data){
15638 Roo.data.MemoryProxy.superclass.constructor.call(this);
15642 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15645 * Load data from the requested source (in this case an in-memory
15646 * data object passed to the constructor), read the data object into
15647 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15648 * process that block using the passed callback.
15649 * @param {Object} params This parameter is not used by the MemoryProxy class.
15650 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15651 * object into a block of Roo.data.Records.
15652 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15653 * The function must be passed <ul>
15654 * <li>The Record block object</li>
15655 * <li>The "arg" argument from the load function</li>
15656 * <li>A boolean success indicator</li>
15658 * @param {Object} scope The scope in which to call the callback
15659 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15661 load : function(params, reader, callback, scope, arg){
15662 params = params || {};
15665 result = reader.readRecords(params.data ? params.data :this.data);
15667 this.fireEvent("loadexception", this, arg, null, e);
15668 callback.call(scope, null, arg, false);
15671 callback.call(scope, result, arg, true);
15675 update : function(params, records){
15680 * Ext JS Library 1.1.1
15681 * Copyright(c) 2006-2007, Ext JS, LLC.
15683 * Originally Released Under LGPL - original licence link has changed is not relivant.
15686 * <script type="text/javascript">
15689 * @class Roo.data.HttpProxy
15690 * @extends Roo.data.DataProxy
15691 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15692 * configured to reference a certain URL.<br><br>
15694 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15695 * from which the running page was served.<br><br>
15697 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15699 * Be aware that to enable the browser to parse an XML document, the server must set
15700 * the Content-Type header in the HTTP response to "text/xml".
15702 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15703 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15704 * will be used to make the request.
15706 Roo.data.HttpProxy = function(conn){
15707 Roo.data.HttpProxy.superclass.constructor.call(this);
15708 // is conn a conn config or a real conn?
15710 this.useAjax = !conn || !conn.events;
15714 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15715 // thse are take from connection...
15718 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15721 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15722 * extra parameters to each request made by this object. (defaults to undefined)
15725 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15726 * to each request made by this object. (defaults to undefined)
15729 * @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)
15732 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15735 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15741 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15745 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15746 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15747 * a finer-grained basis than the DataProxy events.
15749 getConnection : function(){
15750 return this.useAjax ? Roo.Ajax : this.conn;
15754 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15755 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15756 * process that block using the passed callback.
15757 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15758 * for the request to the remote server.
15759 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15760 * object into a block of Roo.data.Records.
15761 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15762 * The function must be passed <ul>
15763 * <li>The Record block object</li>
15764 * <li>The "arg" argument from the load function</li>
15765 * <li>A boolean success indicator</li>
15767 * @param {Object} scope The scope in which to call the callback
15768 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15770 load : function(params, reader, callback, scope, arg){
15771 if(this.fireEvent("beforeload", this, params) !== false){
15773 params : params || {},
15775 callback : callback,
15780 callback : this.loadResponse,
15784 Roo.applyIf(o, this.conn);
15785 if(this.activeRequest){
15786 Roo.Ajax.abort(this.activeRequest);
15788 this.activeRequest = Roo.Ajax.request(o);
15790 this.conn.request(o);
15793 callback.call(scope||this, null, arg, false);
15798 loadResponse : function(o, success, response){
15799 delete this.activeRequest;
15801 this.fireEvent("loadexception", this, o, response);
15802 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15807 result = o.reader.read(response);
15809 this.fireEvent("loadexception", this, o, response, e);
15810 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15814 this.fireEvent("load", this, o, o.request.arg);
15815 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15819 update : function(dataSet){
15824 updateResponse : function(dataSet){
15829 * Ext JS Library 1.1.1
15830 * Copyright(c) 2006-2007, Ext JS, LLC.
15832 * Originally Released Under LGPL - original licence link has changed is not relivant.
15835 * <script type="text/javascript">
15839 * @class Roo.data.ScriptTagProxy
15840 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15841 * other than the originating domain of the running page.<br><br>
15843 * <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
15844 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15846 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15847 * source code that is used as the source inside a <script> tag.<br><br>
15849 * In order for the browser to process the returned data, the server must wrap the data object
15850 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15851 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15852 * depending on whether the callback name was passed:
15855 boolean scriptTag = false;
15856 String cb = request.getParameter("callback");
15859 response.setContentType("text/javascript");
15861 response.setContentType("application/x-json");
15863 Writer out = response.getWriter();
15865 out.write(cb + "(");
15867 out.print(dataBlock.toJsonString());
15874 * @param {Object} config A configuration object.
15876 Roo.data.ScriptTagProxy = function(config){
15877 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15878 Roo.apply(this, config);
15879 this.head = document.getElementsByTagName("head")[0];
15882 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15884 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15886 * @cfg {String} url The URL from which to request the data object.
15889 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15893 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15894 * the server the name of the callback function set up by the load call to process the returned data object.
15895 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15896 * javascript output which calls this named function passing the data object as its only parameter.
15898 callbackParam : "callback",
15900 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15901 * name to the request.
15906 * Load data from the configured URL, read the data object into
15907 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15908 * process that block using the passed callback.
15909 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15910 * for the request to the remote server.
15911 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15912 * object into a block of Roo.data.Records.
15913 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15914 * The function must be passed <ul>
15915 * <li>The Record block object</li>
15916 * <li>The "arg" argument from the load function</li>
15917 * <li>A boolean success indicator</li>
15919 * @param {Object} scope The scope in which to call the callback
15920 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15922 load : function(params, reader, callback, scope, arg){
15923 if(this.fireEvent("beforeload", this, params) !== false){
15925 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15927 var url = this.url;
15928 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15930 url += "&_dc=" + (new Date().getTime());
15932 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15935 cb : "stcCallback"+transId,
15936 scriptId : "stcScript"+transId,
15940 callback : callback,
15946 window[trans.cb] = function(o){
15947 conn.handleResponse(o, trans);
15950 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15952 if(this.autoAbort !== false){
15956 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15958 var script = document.createElement("script");
15959 script.setAttribute("src", url);
15960 script.setAttribute("type", "text/javascript");
15961 script.setAttribute("id", trans.scriptId);
15962 this.head.appendChild(script);
15964 this.trans = trans;
15966 callback.call(scope||this, null, arg, false);
15971 isLoading : function(){
15972 return this.trans ? true : false;
15976 * Abort the current server request.
15978 abort : function(){
15979 if(this.isLoading()){
15980 this.destroyTrans(this.trans);
15985 destroyTrans : function(trans, isLoaded){
15986 this.head.removeChild(document.getElementById(trans.scriptId));
15987 clearTimeout(trans.timeoutId);
15989 window[trans.cb] = undefined;
15991 delete window[trans.cb];
15994 // if hasn't been loaded, wait for load to remove it to prevent script error
15995 window[trans.cb] = function(){
15996 window[trans.cb] = undefined;
15998 delete window[trans.cb];
16005 handleResponse : function(o, trans){
16006 this.trans = false;
16007 this.destroyTrans(trans, true);
16010 result = trans.reader.readRecords(o);
16012 this.fireEvent("loadexception", this, o, trans.arg, e);
16013 trans.callback.call(trans.scope||window, null, trans.arg, false);
16016 this.fireEvent("load", this, o, trans.arg);
16017 trans.callback.call(trans.scope||window, result, trans.arg, true);
16021 handleFailure : function(trans){
16022 this.trans = false;
16023 this.destroyTrans(trans, false);
16024 this.fireEvent("loadexception", this, null, trans.arg);
16025 trans.callback.call(trans.scope||window, null, trans.arg, false);
16029 * Ext JS Library 1.1.1
16030 * Copyright(c) 2006-2007, Ext JS, LLC.
16032 * Originally Released Under LGPL - original licence link has changed is not relivant.
16035 * <script type="text/javascript">
16039 * @class Roo.data.JsonReader
16040 * @extends Roo.data.DataReader
16041 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16042 * based on mappings in a provided Roo.data.Record constructor.
16044 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16045 * in the reply previously.
16050 var RecordDef = Roo.data.Record.create([
16051 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16052 {name: 'occupation'} // This field will use "occupation" as the mapping.
16054 var myReader = new Roo.data.JsonReader({
16055 totalProperty: "results", // The property which contains the total dataset size (optional)
16056 root: "rows", // The property which contains an Array of row objects
16057 id: "id" // The property within each row object that provides an ID for the record (optional)
16061 * This would consume a JSON file like this:
16063 { 'results': 2, 'rows': [
16064 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16065 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16068 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16069 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16070 * paged from the remote server.
16071 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16072 * @cfg {String} root name of the property which contains the Array of row objects.
16073 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16074 * @cfg {Array} fields Array of field definition objects
16076 * Create a new JsonReader
16077 * @param {Object} meta Metadata configuration options
16078 * @param {Object} recordType Either an Array of field definition objects,
16079 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16081 Roo.data.JsonReader = function(meta, recordType){
16084 // set some defaults:
16085 Roo.applyIf(meta, {
16086 totalProperty: 'total',
16087 successProperty : 'success',
16092 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16094 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16096 readerType : 'Json',
16099 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16100 * Used by Store query builder to append _requestMeta to params.
16103 metaFromRemote : false,
16105 * This method is only used by a DataProxy which has retrieved data from a remote server.
16106 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16107 * @return {Object} data A data block which is used by an Roo.data.Store object as
16108 * a cache of Roo.data.Records.
16110 read : function(response){
16111 var json = response.responseText;
16113 var o = /* eval:var:o */ eval("("+json+")");
16115 throw {message: "JsonReader.read: Json object not found"};
16121 this.metaFromRemote = true;
16122 this.meta = o.metaData;
16123 this.recordType = Roo.data.Record.create(o.metaData.fields);
16124 this.onMetaChange(this.meta, this.recordType, o);
16126 return this.readRecords(o);
16129 // private function a store will implement
16130 onMetaChange : function(meta, recordType, o){
16137 simpleAccess: function(obj, subsc) {
16144 getJsonAccessor: function(){
16146 return function(expr) {
16148 return(re.test(expr))
16149 ? new Function("obj", "return obj." + expr)
16154 return Roo.emptyFn;
16159 * Create a data block containing Roo.data.Records from an XML document.
16160 * @param {Object} o An object which contains an Array of row objects in the property specified
16161 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16162 * which contains the total size of the dataset.
16163 * @return {Object} data A data block which is used by an Roo.data.Store object as
16164 * a cache of Roo.data.Records.
16166 readRecords : function(o){
16168 * After any data loads, the raw JSON data is available for further custom processing.
16172 var s = this.meta, Record = this.recordType,
16173 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16175 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16177 if(s.totalProperty) {
16178 this.getTotal = this.getJsonAccessor(s.totalProperty);
16180 if(s.successProperty) {
16181 this.getSuccess = this.getJsonAccessor(s.successProperty);
16183 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16185 var g = this.getJsonAccessor(s.id);
16186 this.getId = function(rec) {
16188 return (r === undefined || r === "") ? null : r;
16191 this.getId = function(){return null;};
16194 for(var jj = 0; jj < fl; jj++){
16196 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16197 this.ef[jj] = this.getJsonAccessor(map);
16201 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16202 if(s.totalProperty){
16203 var vt = parseInt(this.getTotal(o), 10);
16208 if(s.successProperty){
16209 var vs = this.getSuccess(o);
16210 if(vs === false || vs === 'false'){
16215 for(var i = 0; i < c; i++){
16218 var id = this.getId(n);
16219 for(var j = 0; j < fl; j++){
16221 var v = this.ef[j](n);
16223 Roo.log('missing convert for ' + f.name);
16227 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16229 var record = new Record(values, id);
16231 records[i] = record;
16237 totalRecords : totalRecords
16240 // used when loading children.. @see loadDataFromChildren
16241 toLoadData: function(rec)
16243 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16244 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16245 return { data : data, total : data.length };
16250 * Ext JS Library 1.1.1
16251 * Copyright(c) 2006-2007, Ext JS, LLC.
16253 * Originally Released Under LGPL - original licence link has changed is not relivant.
16256 * <script type="text/javascript">
16260 * @class Roo.data.ArrayReader
16261 * @extends Roo.data.DataReader
16262 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16263 * Each element of that Array represents a row of data fields. The
16264 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16265 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16269 var RecordDef = Roo.data.Record.create([
16270 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16271 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16273 var myReader = new Roo.data.ArrayReader({
16274 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16278 * This would consume an Array like this:
16280 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16284 * Create a new JsonReader
16285 * @param {Object} meta Metadata configuration options.
16286 * @param {Object|Array} recordType Either an Array of field definition objects
16288 * @cfg {Array} fields Array of field definition objects
16289 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16290 * as specified to {@link Roo.data.Record#create},
16291 * or an {@link Roo.data.Record} object
16294 * created using {@link Roo.data.Record#create}.
16296 Roo.data.ArrayReader = function(meta, recordType)
16298 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16301 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16304 * Create a data block containing Roo.data.Records from an XML document.
16305 * @param {Object} o An Array of row objects which represents the dataset.
16306 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16307 * a cache of Roo.data.Records.
16309 readRecords : function(o)
16311 var sid = this.meta ? this.meta.id : null;
16312 var recordType = this.recordType, fields = recordType.prototype.fields;
16315 for(var i = 0; i < root.length; i++){
16318 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16319 for(var j = 0, jlen = fields.length; j < jlen; j++){
16320 var f = fields.items[j];
16321 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16322 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16324 values[f.name] = v;
16326 var record = new recordType(values, id);
16328 records[records.length] = record;
16332 totalRecords : records.length
16335 // used when loading children.. @see loadDataFromChildren
16336 toLoadData: function(rec)
16338 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16339 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16350 * @class Roo.bootstrap.ComboBox
16351 * @extends Roo.bootstrap.TriggerField
16352 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16353 * @cfg {Boolean} append (true|false) default false
16354 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16355 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16356 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16357 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16358 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16359 * @cfg {Boolean} animate default true
16360 * @cfg {Boolean} emptyResultText only for touch device
16361 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16362 * @cfg {String} emptyTitle default ''
16363 * @cfg {Number} width fixed with? experimental
16365 * Create a new ComboBox.
16366 * @param {Object} config Configuration options
16368 Roo.bootstrap.ComboBox = function(config){
16369 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16373 * Fires when the dropdown list is expanded
16374 * @param {Roo.bootstrap.ComboBox} combo This combo box
16379 * Fires when the dropdown list is collapsed
16380 * @param {Roo.bootstrap.ComboBox} combo This combo box
16384 * @event beforeselect
16385 * Fires before a list item is selected. Return false to cancel the selection.
16386 * @param {Roo.bootstrap.ComboBox} combo This combo box
16387 * @param {Roo.data.Record} record The data record returned from the underlying store
16388 * @param {Number} index The index of the selected item in the dropdown list
16390 'beforeselect' : true,
16393 * Fires when a list item is selected
16394 * @param {Roo.bootstrap.ComboBox} combo This combo box
16395 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16396 * @param {Number} index The index of the selected item in the dropdown list
16400 * @event beforequery
16401 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16402 * The event object passed has these properties:
16403 * @param {Roo.bootstrap.ComboBox} combo This combo box
16404 * @param {String} query The query
16405 * @param {Boolean} forceAll true to force "all" query
16406 * @param {Boolean} cancel true to cancel the query
16407 * @param {Object} e The query event object
16409 'beforequery': true,
16412 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16413 * @param {Roo.bootstrap.ComboBox} combo This combo box
16418 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16419 * @param {Roo.bootstrap.ComboBox} combo This combo box
16420 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16425 * Fires when the remove value from the combobox array
16426 * @param {Roo.bootstrap.ComboBox} combo This combo box
16430 * @event afterremove
16431 * Fires when the remove value from the combobox array
16432 * @param {Roo.bootstrap.ComboBox} combo This combo box
16434 'afterremove' : true,
16436 * @event specialfilter
16437 * Fires when specialfilter
16438 * @param {Roo.bootstrap.ComboBox} combo This combo box
16440 'specialfilter' : true,
16443 * Fires when tick the element
16444 * @param {Roo.bootstrap.ComboBox} combo This combo box
16448 * @event touchviewdisplay
16449 * Fires when touch view require special display (default is using displayField)
16450 * @param {Roo.bootstrap.ComboBox} combo This combo box
16451 * @param {Object} cfg set html .
16453 'touchviewdisplay' : true
16458 this.tickItems = [];
16460 this.selectedIndex = -1;
16461 if(this.mode == 'local'){
16462 if(config.queryDelay === undefined){
16463 this.queryDelay = 10;
16465 if(config.minChars === undefined){
16471 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16474 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16475 * rendering into an Roo.Editor, defaults to false)
16478 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16479 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16482 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16485 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16486 * the dropdown list (defaults to undefined, with no header element)
16490 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16494 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16496 listWidth: undefined,
16498 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16499 * mode = 'remote' or 'text' if mode = 'local')
16501 displayField: undefined,
16504 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16505 * mode = 'remote' or 'value' if mode = 'local').
16506 * Note: use of a valueField requires the user make a selection
16507 * in order for a value to be mapped.
16509 valueField: undefined,
16511 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16516 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16517 * field's data value (defaults to the underlying DOM element's name)
16519 hiddenName: undefined,
16521 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16525 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16527 selectedClass: 'active',
16530 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16534 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16535 * anchor positions (defaults to 'tl-bl')
16537 listAlign: 'tl-bl?',
16539 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16543 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16544 * query specified by the allQuery config option (defaults to 'query')
16546 triggerAction: 'query',
16548 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16549 * (defaults to 4, does not apply if editable = false)
16553 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16554 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16558 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16559 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16563 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16564 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16568 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16569 * when editable = true (defaults to false)
16571 selectOnFocus:false,
16573 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16575 queryParam: 'query',
16577 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16578 * when mode = 'remote' (defaults to 'Loading...')
16580 loadingText: 'Loading...',
16582 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16586 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16590 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16591 * traditional select (defaults to true)
16595 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16599 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16603 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16604 * listWidth has a higher value)
16608 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16609 * allow the user to set arbitrary text into the field (defaults to false)
16611 forceSelection:false,
16613 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16614 * if typeAhead = true (defaults to 250)
16616 typeAheadDelay : 250,
16618 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16619 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16621 valueNotFoundText : undefined,
16623 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16625 blockFocus : false,
16628 * @cfg {Boolean} disableClear Disable showing of clear button.
16630 disableClear : false,
16632 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16634 alwaysQuery : false,
16637 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16642 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16644 invalidClass : "has-warning",
16647 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16649 validClass : "has-success",
16652 * @cfg {Boolean} specialFilter (true|false) special filter default false
16654 specialFilter : false,
16657 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16659 mobileTouchView : true,
16662 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16664 useNativeIOS : false,
16667 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16669 mobile_restrict_height : false,
16671 ios_options : false,
16683 btnPosition : 'right',
16684 triggerList : true,
16685 showToggleBtn : true,
16687 emptyResultText: 'Empty',
16688 triggerText : 'Select',
16692 // element that contains real text value.. (when hidden is used..)
16694 getAutoCreate : function()
16699 * Render classic select for iso
16702 if(Roo.isIOS && this.useNativeIOS){
16703 cfg = this.getAutoCreateNativeIOS();
16711 if(Roo.isTouch && this.mobileTouchView){
16712 cfg = this.getAutoCreateTouchView();
16719 if(!this.tickable){
16720 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16725 * ComboBox with tickable selections
16728 var align = this.labelAlign || this.parentLabelAlign();
16731 cls : 'form-group roo-combobox-tickable' //input-group
16734 var btn_text_select = '';
16735 var btn_text_done = '';
16736 var btn_text_cancel = '';
16738 if (this.btn_text_show) {
16739 btn_text_select = 'Select';
16740 btn_text_done = 'Done';
16741 btn_text_cancel = 'Cancel';
16746 cls : 'tickable-buttons',
16751 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16752 //html : this.triggerText
16753 html: btn_text_select
16759 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16761 html: btn_text_done
16767 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16769 html: btn_text_cancel
16775 buttons.cn.unshift({
16777 cls: 'roo-select2-search-field-input'
16783 Roo.each(buttons.cn, function(c){
16785 c.cls += ' btn-' + _this.size;
16788 if (_this.disabled) {
16795 style : 'display: contents',
16800 cls: 'form-hidden-field'
16804 cls: 'roo-select2-choices',
16808 cls: 'roo-select2-search-field',
16819 cls: 'roo-select2-container input-group roo-select2-container-multi',
16825 // cls: 'typeahead typeahead-long dropdown-menu',
16826 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16831 if(this.hasFeedback && !this.allowBlank){
16835 cls: 'glyphicon form-control-feedback'
16838 combobox.cn.push(feedback);
16845 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16846 tooltip : 'This field is required'
16848 if (Roo.bootstrap.version == 4) {
16851 style : 'display:none'
16854 if (align ==='left' && this.fieldLabel.length) {
16856 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16863 cls : 'control-label col-form-label',
16864 html : this.fieldLabel
16876 var labelCfg = cfg.cn[1];
16877 var contentCfg = cfg.cn[2];
16880 if(this.indicatorpos == 'right'){
16886 cls : 'control-label col-form-label',
16890 html : this.fieldLabel
16906 labelCfg = cfg.cn[0];
16907 contentCfg = cfg.cn[1];
16911 if(this.labelWidth > 12){
16912 labelCfg.style = "width: " + this.labelWidth + 'px';
16914 if(this.width * 1 > 0){
16915 contentCfg.style = "width: " + this.width + 'px';
16917 if(this.labelWidth < 13 && this.labelmd == 0){
16918 this.labelmd = this.labelWidth;
16921 if(this.labellg > 0){
16922 labelCfg.cls += ' col-lg-' + this.labellg;
16923 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16926 if(this.labelmd > 0){
16927 labelCfg.cls += ' col-md-' + this.labelmd;
16928 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16931 if(this.labelsm > 0){
16932 labelCfg.cls += ' col-sm-' + this.labelsm;
16933 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16936 if(this.labelxs > 0){
16937 labelCfg.cls += ' col-xs-' + this.labelxs;
16938 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16942 } else if ( this.fieldLabel.length) {
16943 // Roo.log(" label");
16948 //cls : 'input-group-addon',
16949 html : this.fieldLabel
16954 if(this.indicatorpos == 'right'){
16958 //cls : 'input-group-addon',
16959 html : this.fieldLabel
16969 // Roo.log(" no label && no align");
16976 ['xs','sm','md','lg'].map(function(size){
16977 if (settings[size]) {
16978 cfg.cls += ' col-' + size + '-' + settings[size];
16986 _initEventsCalled : false,
16989 initEvents: function()
16991 if (this._initEventsCalled) { // as we call render... prevent looping...
16994 this._initEventsCalled = true;
16997 throw "can not find store for combo";
17000 this.indicator = this.indicatorEl();
17002 this.store = Roo.factory(this.store, Roo.data);
17003 this.store.parent = this;
17005 // if we are building from html. then this element is so complex, that we can not really
17006 // use the rendered HTML.
17007 // so we have to trash and replace the previous code.
17008 if (Roo.XComponent.build_from_html) {
17009 // remove this element....
17010 var e = this.el.dom, k=0;
17011 while (e ) { e = e.previousSibling; ++k;}
17016 this.rendered = false;
17018 this.render(this.parent().getChildContainer(true), k);
17021 if(Roo.isIOS && this.useNativeIOS){
17022 this.initIOSView();
17030 if(Roo.isTouch && this.mobileTouchView){
17031 this.initTouchView();
17036 this.initTickableEvents();
17040 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17042 if(this.hiddenName){
17044 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17046 this.hiddenField.dom.value =
17047 this.hiddenValue !== undefined ? this.hiddenValue :
17048 this.value !== undefined ? this.value : '';
17050 // prevent input submission
17051 this.el.dom.removeAttribute('name');
17052 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17057 // this.el.dom.setAttribute('autocomplete', 'off');
17060 var cls = 'x-combo-list';
17062 //this.list = new Roo.Layer({
17063 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17069 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17070 _this.list.setWidth(lw);
17073 this.list.on('mouseover', this.onViewOver, this);
17074 this.list.on('mousemove', this.onViewMove, this);
17075 this.list.on('scroll', this.onViewScroll, this);
17078 this.list.swallowEvent('mousewheel');
17079 this.assetHeight = 0;
17082 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17083 this.assetHeight += this.header.getHeight();
17086 this.innerList = this.list.createChild({cls:cls+'-inner'});
17087 this.innerList.on('mouseover', this.onViewOver, this);
17088 this.innerList.on('mousemove', this.onViewMove, this);
17089 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17091 if(this.allowBlank && !this.pageSize && !this.disableClear){
17092 this.footer = this.list.createChild({cls:cls+'-ft'});
17093 this.pageTb = new Roo.Toolbar(this.footer);
17097 this.footer = this.list.createChild({cls:cls+'-ft'});
17098 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17099 {pageSize: this.pageSize});
17103 if (this.pageTb && this.allowBlank && !this.disableClear) {
17105 this.pageTb.add(new Roo.Toolbar.Fill(), {
17106 cls: 'x-btn-icon x-btn-clear',
17108 handler: function()
17111 _this.clearValue();
17112 _this.onSelect(false, -1);
17117 this.assetHeight += this.footer.getHeight();
17122 this.tpl = Roo.bootstrap.version == 4 ?
17123 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17124 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17127 this.view = new Roo.View(this.list, this.tpl, {
17128 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17130 //this.view.wrapEl.setDisplayed(false);
17131 this.view.on('click', this.onViewClick, this);
17134 this.store.on('beforeload', this.onBeforeLoad, this);
17135 this.store.on('load', this.onLoad, this);
17136 this.store.on('loadexception', this.onLoadException, this);
17138 if(this.resizable){
17139 this.resizer = new Roo.Resizable(this.list, {
17140 pinned:true, handles:'se'
17142 this.resizer.on('resize', function(r, w, h){
17143 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17144 this.listWidth = w;
17145 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17146 this.restrictHeight();
17148 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17151 if(!this.editable){
17152 this.editable = true;
17153 this.setEditable(false);
17158 if (typeof(this.events.add.listeners) != 'undefined') {
17160 this.addicon = this.wrap.createChild(
17161 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17163 this.addicon.on('click', function(e) {
17164 this.fireEvent('add', this);
17167 if (typeof(this.events.edit.listeners) != 'undefined') {
17169 this.editicon = this.wrap.createChild(
17170 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17171 if (this.addicon) {
17172 this.editicon.setStyle('margin-left', '40px');
17174 this.editicon.on('click', function(e) {
17176 // we fire even if inothing is selected..
17177 this.fireEvent('edit', this, this.lastData );
17183 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17184 "up" : function(e){
17185 this.inKeyMode = true;
17189 "down" : function(e){
17190 if(!this.isExpanded()){
17191 this.onTriggerClick();
17193 this.inKeyMode = true;
17198 "enter" : function(e){
17199 // this.onViewClick();
17203 if(this.fireEvent("specialkey", this, e)){
17204 this.onViewClick(false);
17210 "esc" : function(e){
17214 "tab" : function(e){
17217 if(this.fireEvent("specialkey", this, e)){
17218 this.onViewClick(false);
17226 doRelay : function(foo, bar, hname){
17227 if(hname == 'down' || this.scope.isExpanded()){
17228 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17237 this.queryDelay = Math.max(this.queryDelay || 10,
17238 this.mode == 'local' ? 10 : 250);
17241 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17243 if(this.typeAhead){
17244 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17246 if(this.editable !== false){
17247 this.inputEl().on("keyup", this.onKeyUp, this);
17249 if(this.forceSelection){
17250 this.inputEl().on('blur', this.doForce, this);
17254 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17255 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17259 initTickableEvents: function()
17263 if(this.hiddenName){
17265 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17267 this.hiddenField.dom.value =
17268 this.hiddenValue !== undefined ? this.hiddenValue :
17269 this.value !== undefined ? this.value : '';
17271 // prevent input submission
17272 this.el.dom.removeAttribute('name');
17273 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17278 // this.list = this.el.select('ul.dropdown-menu',true).first();
17280 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17281 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17282 if(this.triggerList){
17283 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17286 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17287 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17289 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17290 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17292 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17293 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17295 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17296 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17297 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17300 this.cancelBtn.hide();
17305 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17306 _this.list.setWidth(lw);
17309 this.list.on('mouseover', this.onViewOver, this);
17310 this.list.on('mousemove', this.onViewMove, this);
17312 this.list.on('scroll', this.onViewScroll, this);
17315 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17316 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17319 this.view = new Roo.View(this.list, this.tpl, {
17324 selectedClass: this.selectedClass
17327 //this.view.wrapEl.setDisplayed(false);
17328 this.view.on('click', this.onViewClick, this);
17332 this.store.on('beforeload', this.onBeforeLoad, this);
17333 this.store.on('load', this.onLoad, this);
17334 this.store.on('loadexception', this.onLoadException, this);
17337 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17338 "up" : function(e){
17339 this.inKeyMode = true;
17343 "down" : function(e){
17344 this.inKeyMode = true;
17348 "enter" : function(e){
17349 if(this.fireEvent("specialkey", this, e)){
17350 this.onViewClick(false);
17356 "esc" : function(e){
17357 this.onTickableFooterButtonClick(e, false, false);
17360 "tab" : function(e){
17361 this.fireEvent("specialkey", this, e);
17363 this.onTickableFooterButtonClick(e, false, false);
17370 doRelay : function(e, fn, key){
17371 if(this.scope.isExpanded()){
17372 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17381 this.queryDelay = Math.max(this.queryDelay || 10,
17382 this.mode == 'local' ? 10 : 250);
17385 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17387 if(this.typeAhead){
17388 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17391 if(this.editable !== false){
17392 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17395 this.indicator = this.indicatorEl();
17397 if(this.indicator){
17398 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17399 this.indicator.hide();
17404 onDestroy : function(){
17406 this.view.setStore(null);
17407 this.view.el.removeAllListeners();
17408 this.view.el.remove();
17409 this.view.purgeListeners();
17412 this.list.dom.innerHTML = '';
17416 this.store.un('beforeload', this.onBeforeLoad, this);
17417 this.store.un('load', this.onLoad, this);
17418 this.store.un('loadexception', this.onLoadException, this);
17420 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17424 fireKey : function(e){
17425 if(e.isNavKeyPress() && !this.list.isVisible()){
17426 this.fireEvent("specialkey", this, e);
17431 onResize: function(w, h)
17435 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17437 // if(typeof w != 'number'){
17438 // // we do not handle it!?!?
17441 // var tw = this.trigger.getWidth();
17442 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17443 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17445 // this.inputEl().setWidth( this.adjustWidth('input', x));
17447 // //this.trigger.setStyle('left', x+'px');
17449 // if(this.list && this.listWidth === undefined){
17450 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17451 // this.list.setWidth(lw);
17452 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17460 * Allow or prevent the user from directly editing the field text. If false is passed,
17461 * the user will only be able to select from the items defined in the dropdown list. This method
17462 * is the runtime equivalent of setting the 'editable' config option at config time.
17463 * @param {Boolean} value True to allow the user to directly edit the field text
17465 setEditable : function(value){
17466 if(value == this.editable){
17469 this.editable = value;
17471 this.inputEl().dom.setAttribute('readOnly', true);
17472 this.inputEl().on('mousedown', this.onTriggerClick, this);
17473 this.inputEl().addClass('x-combo-noedit');
17475 this.inputEl().dom.removeAttribute('readOnly');
17476 this.inputEl().un('mousedown', this.onTriggerClick, this);
17477 this.inputEl().removeClass('x-combo-noedit');
17483 onBeforeLoad : function(combo,opts){
17484 if(!this.hasFocus){
17488 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17490 this.restrictHeight();
17491 this.selectedIndex = -1;
17495 onLoad : function(){
17497 this.hasQuery = false;
17499 if(!this.hasFocus){
17503 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17504 this.loading.hide();
17507 if(this.store.getCount() > 0){
17510 this.restrictHeight();
17511 if(this.lastQuery == this.allQuery){
17512 if(this.editable && !this.tickable){
17513 this.inputEl().dom.select();
17517 !this.selectByValue(this.value, true) &&
17520 !this.store.lastOptions ||
17521 typeof(this.store.lastOptions.add) == 'undefined' ||
17522 this.store.lastOptions.add != true
17525 this.select(0, true);
17528 if(this.autoFocus){
17531 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17532 this.taTask.delay(this.typeAheadDelay);
17536 this.onEmptyResults();
17542 onLoadException : function()
17544 this.hasQuery = false;
17546 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17547 this.loading.hide();
17550 if(this.tickable && this.editable){
17555 // only causes errors at present
17556 //Roo.log(this.store.reader.jsonData);
17557 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17559 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17565 onTypeAhead : function(){
17566 if(this.store.getCount() > 0){
17567 var r = this.store.getAt(0);
17568 var newValue = r.data[this.displayField];
17569 var len = newValue.length;
17570 var selStart = this.getRawValue().length;
17572 if(selStart != len){
17573 this.setRawValue(newValue);
17574 this.selectText(selStart, newValue.length);
17580 onSelect : function(record, index){
17582 if(this.fireEvent('beforeselect', this, record, index) !== false){
17584 this.setFromData(index > -1 ? record.data : false);
17587 this.fireEvent('select', this, record, index);
17592 * Returns the currently selected field value or empty string if no value is set.
17593 * @return {String} value The selected value
17595 getValue : function()
17597 if(Roo.isIOS && this.useNativeIOS){
17598 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17602 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17605 if(this.valueField){
17606 return typeof this.value != 'undefined' ? this.value : '';
17608 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17612 getRawValue : function()
17614 if(Roo.isIOS && this.useNativeIOS){
17615 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17618 var v = this.inputEl().getValue();
17624 * Clears any text/value currently set in the field
17626 clearValue : function(){
17628 if(this.hiddenField){
17629 this.hiddenField.dom.value = '';
17632 this.setRawValue('');
17633 this.lastSelectionText = '';
17634 this.lastData = false;
17636 var close = this.closeTriggerEl();
17647 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17648 * will be displayed in the field. If the value does not match the data value of an existing item,
17649 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17650 * Otherwise the field will be blank (although the value will still be set).
17651 * @param {String} value The value to match
17653 setValue : function(v)
17655 if(Roo.isIOS && this.useNativeIOS){
17656 this.setIOSValue(v);
17666 if(this.valueField){
17667 var r = this.findRecord(this.valueField, v);
17669 text = r.data[this.displayField];
17670 }else if(this.valueNotFoundText !== undefined){
17671 text = this.valueNotFoundText;
17674 this.lastSelectionText = text;
17675 if(this.hiddenField){
17676 this.hiddenField.dom.value = v;
17678 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17681 var close = this.closeTriggerEl();
17684 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17690 * @property {Object} the last set data for the element
17695 * Sets the value of the field based on a object which is related to the record format for the store.
17696 * @param {Object} value the value to set as. or false on reset?
17698 setFromData : function(o){
17705 var dv = ''; // display value
17706 var vv = ''; // value value..
17708 if (this.displayField) {
17709 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17711 // this is an error condition!!!
17712 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17715 if(this.valueField){
17716 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17719 var close = this.closeTriggerEl();
17722 if(dv.length || vv * 1 > 0){
17724 this.blockFocus=true;
17730 if(this.hiddenField){
17731 this.hiddenField.dom.value = vv;
17733 this.lastSelectionText = dv;
17734 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17738 // no hidden field.. - we store the value in 'value', but still display
17739 // display field!!!!
17740 this.lastSelectionText = dv;
17741 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17748 reset : function(){
17749 // overridden so that last data is reset..
17756 this.setValue(this.originalValue);
17757 //this.clearInvalid();
17758 this.lastData = false;
17760 this.view.clearSelections();
17766 findRecord : function(prop, value){
17768 if(this.store.getCount() > 0){
17769 this.store.each(function(r){
17770 if(r.data[prop] == value){
17780 getName: function()
17782 // returns hidden if it's set..
17783 if (!this.rendered) {return ''};
17784 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17788 onViewMove : function(e, t){
17789 this.inKeyMode = false;
17793 onViewOver : function(e, t){
17794 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17797 var item = this.view.findItemFromChild(t);
17800 var index = this.view.indexOf(item);
17801 this.select(index, false);
17806 onViewClick : function(view, doFocus, el, e)
17808 var index = this.view.getSelectedIndexes()[0];
17810 var r = this.store.getAt(index);
17814 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17821 Roo.each(this.tickItems, function(v,k){
17823 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17825 _this.tickItems.splice(k, 1);
17827 if(typeof(e) == 'undefined' && view == false){
17828 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17840 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17841 this.tickItems.push(r.data);
17844 if(typeof(e) == 'undefined' && view == false){
17845 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17852 this.onSelect(r, index);
17854 if(doFocus !== false && !this.blockFocus){
17855 this.inputEl().focus();
17860 restrictHeight : function(){
17861 //this.innerList.dom.style.height = '';
17862 //var inner = this.innerList.dom;
17863 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17864 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17865 //this.list.beginUpdate();
17866 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17867 this.list.alignTo(this.inputEl(), this.listAlign);
17868 this.list.alignTo(this.inputEl(), this.listAlign);
17869 //this.list.endUpdate();
17873 onEmptyResults : function(){
17875 if(this.tickable && this.editable){
17876 this.hasFocus = false;
17877 this.restrictHeight();
17885 * Returns true if the dropdown list is expanded, else false.
17887 isExpanded : function(){
17888 return this.list.isVisible();
17892 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17893 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17894 * @param {String} value The data value of the item to select
17895 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17896 * selected item if it is not currently in view (defaults to true)
17897 * @return {Boolean} True if the value matched an item in the list, else false
17899 selectByValue : function(v, scrollIntoView){
17900 if(v !== undefined && v !== null){
17901 var r = this.findRecord(this.valueField || this.displayField, v);
17903 this.select(this.store.indexOf(r), scrollIntoView);
17911 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17912 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17913 * @param {Number} index The zero-based index of the list item to select
17914 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17915 * selected item if it is not currently in view (defaults to true)
17917 select : function(index, scrollIntoView){
17918 this.selectedIndex = index;
17919 this.view.select(index);
17920 if(scrollIntoView !== false){
17921 var el = this.view.getNode(index);
17923 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17926 this.list.scrollChildIntoView(el, false);
17932 selectNext : function(){
17933 var ct = this.store.getCount();
17935 if(this.selectedIndex == -1){
17937 }else if(this.selectedIndex < ct-1){
17938 this.select(this.selectedIndex+1);
17944 selectPrev : function(){
17945 var ct = this.store.getCount();
17947 if(this.selectedIndex == -1){
17949 }else if(this.selectedIndex != 0){
17950 this.select(this.selectedIndex-1);
17956 onKeyUp : function(e){
17957 if(this.editable !== false && !e.isSpecialKey()){
17958 this.lastKey = e.getKey();
17959 this.dqTask.delay(this.queryDelay);
17964 validateBlur : function(){
17965 return !this.list || !this.list.isVisible();
17969 initQuery : function(){
17971 var v = this.getRawValue();
17973 if(this.tickable && this.editable){
17974 v = this.tickableInputEl().getValue();
17981 doForce : function(){
17982 if(this.inputEl().dom.value.length > 0){
17983 this.inputEl().dom.value =
17984 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17990 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17991 * query allowing the query action to be canceled if needed.
17992 * @param {String} query The SQL query to execute
17993 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17994 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17995 * saved in the current store (defaults to false)
17997 doQuery : function(q, forceAll){
17999 if(q === undefined || q === null){
18004 forceAll: forceAll,
18008 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18013 forceAll = qe.forceAll;
18014 if(forceAll === true || (q.length >= this.minChars)){
18016 this.hasQuery = true;
18018 if(this.lastQuery != q || this.alwaysQuery){
18019 this.lastQuery = q;
18020 if(this.mode == 'local'){
18021 this.selectedIndex = -1;
18023 this.store.clearFilter();
18026 if(this.specialFilter){
18027 this.fireEvent('specialfilter', this);
18032 this.store.filter(this.displayField, q);
18035 this.store.fireEvent("datachanged", this.store);
18042 this.store.baseParams[this.queryParam] = q;
18044 var options = {params : this.getParams(q)};
18047 options.add = true;
18048 options.params.start = this.page * this.pageSize;
18051 this.store.load(options);
18054 * this code will make the page width larger, at the beginning, the list not align correctly,
18055 * we should expand the list on onLoad
18056 * so command out it
18061 this.selectedIndex = -1;
18066 this.loadNext = false;
18070 getParams : function(q){
18072 //p[this.queryParam] = q;
18076 p.limit = this.pageSize;
18082 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18084 collapse : function(){
18085 if(!this.isExpanded()){
18091 this.hasFocus = false;
18095 this.cancelBtn.hide();
18096 this.trigger.show();
18099 this.tickableInputEl().dom.value = '';
18100 this.tickableInputEl().blur();
18105 Roo.get(document).un('mousedown', this.collapseIf, this);
18106 Roo.get(document).un('mousewheel', this.collapseIf, this);
18107 if (!this.editable) {
18108 Roo.get(document).un('keydown', this.listKeyPress, this);
18110 this.fireEvent('collapse', this);
18116 collapseIf : function(e){
18117 var in_combo = e.within(this.el);
18118 var in_list = e.within(this.list);
18119 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18121 if (in_combo || in_list || is_list) {
18122 //e.stopPropagation();
18127 this.onTickableFooterButtonClick(e, false, false);
18135 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18137 expand : function(){
18139 if(this.isExpanded() || !this.hasFocus){
18143 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18144 this.list.setWidth(lw);
18150 this.restrictHeight();
18154 this.tickItems = Roo.apply([], this.item);
18157 this.cancelBtn.show();
18158 this.trigger.hide();
18161 this.tickableInputEl().focus();
18166 Roo.get(document).on('mousedown', this.collapseIf, this);
18167 Roo.get(document).on('mousewheel', this.collapseIf, this);
18168 if (!this.editable) {
18169 Roo.get(document).on('keydown', this.listKeyPress, this);
18172 this.fireEvent('expand', this);
18176 // Implements the default empty TriggerField.onTriggerClick function
18177 onTriggerClick : function(e)
18179 Roo.log('trigger click');
18181 if(this.disabled || !this.triggerList){
18186 this.loadNext = false;
18188 if(this.isExpanded()){
18190 if (!this.blockFocus) {
18191 this.inputEl().focus();
18195 this.hasFocus = true;
18196 if(this.triggerAction == 'all') {
18197 this.doQuery(this.allQuery, true);
18199 this.doQuery(this.getRawValue());
18201 if (!this.blockFocus) {
18202 this.inputEl().focus();
18207 onTickableTriggerClick : function(e)
18214 this.loadNext = false;
18215 this.hasFocus = true;
18217 if(this.triggerAction == 'all') {
18218 this.doQuery(this.allQuery, true);
18220 this.doQuery(this.getRawValue());
18224 onSearchFieldClick : function(e)
18226 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18227 this.onTickableFooterButtonClick(e, false, false);
18231 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18236 this.loadNext = false;
18237 this.hasFocus = true;
18239 if(this.triggerAction == 'all') {
18240 this.doQuery(this.allQuery, true);
18242 this.doQuery(this.getRawValue());
18246 listKeyPress : function(e)
18248 //Roo.log('listkeypress');
18249 // scroll to first matching element based on key pres..
18250 if (e.isSpecialKey()) {
18253 var k = String.fromCharCode(e.getKey()).toUpperCase();
18256 var csel = this.view.getSelectedNodes();
18257 var cselitem = false;
18259 var ix = this.view.indexOf(csel[0]);
18260 cselitem = this.store.getAt(ix);
18261 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18267 this.store.each(function(v) {
18269 // start at existing selection.
18270 if (cselitem.id == v.id) {
18276 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18277 match = this.store.indexOf(v);
18283 if (match === false) {
18284 return true; // no more action?
18287 this.view.select(match);
18288 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18289 sn.scrollIntoView(sn.dom.parentNode, false);
18292 onViewScroll : function(e, t){
18294 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){
18298 this.hasQuery = true;
18300 this.loading = this.list.select('.loading', true).first();
18302 if(this.loading === null){
18303 this.list.createChild({
18305 cls: 'loading roo-select2-more-results roo-select2-active',
18306 html: 'Loading more results...'
18309 this.loading = this.list.select('.loading', true).first();
18311 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18313 this.loading.hide();
18316 this.loading.show();
18321 this.loadNext = true;
18323 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18328 addItem : function(o)
18330 var dv = ''; // display value
18332 if (this.displayField) {
18333 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18335 // this is an error condition!!!
18336 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18343 var choice = this.choices.createChild({
18345 cls: 'roo-select2-search-choice',
18354 cls: 'roo-select2-search-choice-close fa fa-times',
18359 }, this.searchField);
18361 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18363 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18371 this.inputEl().dom.value = '';
18376 onRemoveItem : function(e, _self, o)
18378 e.preventDefault();
18380 this.lastItem = Roo.apply([], this.item);
18382 var index = this.item.indexOf(o.data) * 1;
18385 Roo.log('not this item?!');
18389 this.item.splice(index, 1);
18394 this.fireEvent('remove', this, e);
18400 syncValue : function()
18402 if(!this.item.length){
18409 Roo.each(this.item, function(i){
18410 if(_this.valueField){
18411 value.push(i[_this.valueField]);
18418 this.value = value.join(',');
18420 if(this.hiddenField){
18421 this.hiddenField.dom.value = this.value;
18424 this.store.fireEvent("datachanged", this.store);
18429 clearItem : function()
18431 if(!this.multiple){
18437 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18445 if(this.tickable && !Roo.isTouch){
18446 this.view.refresh();
18450 inputEl: function ()
18452 if(Roo.isIOS && this.useNativeIOS){
18453 return this.el.select('select.roo-ios-select', true).first();
18456 if(Roo.isTouch && this.mobileTouchView){
18457 return this.el.select('input.form-control',true).first();
18461 return this.searchField;
18464 return this.el.select('input.form-control',true).first();
18467 onTickableFooterButtonClick : function(e, btn, el)
18469 e.preventDefault();
18471 this.lastItem = Roo.apply([], this.item);
18473 if(btn && btn.name == 'cancel'){
18474 this.tickItems = Roo.apply([], this.item);
18483 Roo.each(this.tickItems, function(o){
18491 validate : function()
18493 if(this.getVisibilityEl().hasClass('hidden')){
18497 var v = this.getRawValue();
18500 v = this.getValue();
18503 if(this.disabled || this.allowBlank || v.length){
18508 this.markInvalid();
18512 tickableInputEl : function()
18514 if(!this.tickable || !this.editable){
18515 return this.inputEl();
18518 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18522 getAutoCreateTouchView : function()
18527 cls: 'form-group' //input-group
18533 type : this.inputType,
18534 cls : 'form-control x-combo-noedit',
18535 autocomplete: 'new-password',
18536 placeholder : this.placeholder || '',
18541 input.name = this.name;
18545 input.cls += ' input-' + this.size;
18548 if (this.disabled) {
18549 input.disabled = true;
18553 cls : 'roo-combobox-wrap',
18560 inputblock.cls += ' input-group';
18562 inputblock.cn.unshift({
18564 cls : 'input-group-addon input-group-prepend input-group-text',
18569 if(this.removable && !this.multiple){
18570 inputblock.cls += ' roo-removable';
18572 inputblock.cn.push({
18575 cls : 'roo-combo-removable-btn close'
18579 if(this.hasFeedback && !this.allowBlank){
18581 inputblock.cls += ' has-feedback';
18583 inputblock.cn.push({
18585 cls: 'glyphicon form-control-feedback'
18592 inputblock.cls += (this.before) ? '' : ' input-group';
18594 inputblock.cn.push({
18596 cls : 'input-group-addon input-group-append input-group-text',
18602 var ibwrap = inputblock;
18607 cls: 'roo-select2-choices',
18611 cls: 'roo-select2-search-field',
18624 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18629 cls: 'form-hidden-field'
18635 if(!this.multiple && this.showToggleBtn){
18641 if (this.caret != false) {
18644 cls: 'fa fa-' + this.caret
18651 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18653 Roo.bootstrap.version == 3 ? caret : '',
18656 cls: 'combobox-clear',
18670 combobox.cls += ' roo-select2-container-multi';
18673 var required = this.allowBlank ? {
18675 style: 'display: none'
18678 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18679 tooltip : 'This field is required'
18682 var align = this.labelAlign || this.parentLabelAlign();
18684 if (align ==='left' && this.fieldLabel.length) {
18690 cls : 'control-label col-form-label',
18691 html : this.fieldLabel
18695 cls : 'roo-combobox-wrap ',
18702 var labelCfg = cfg.cn[1];
18703 var contentCfg = cfg.cn[2];
18706 if(this.indicatorpos == 'right'){
18711 cls : 'control-label col-form-label',
18715 html : this.fieldLabel
18721 cls : "roo-combobox-wrap ",
18729 labelCfg = cfg.cn[0];
18730 contentCfg = cfg.cn[1];
18735 if(this.labelWidth > 12){
18736 labelCfg.style = "width: " + this.labelWidth + 'px';
18739 if(this.labelWidth < 13 && this.labelmd == 0){
18740 this.labelmd = this.labelWidth;
18743 if(this.labellg > 0){
18744 labelCfg.cls += ' col-lg-' + this.labellg;
18745 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18748 if(this.labelmd > 0){
18749 labelCfg.cls += ' col-md-' + this.labelmd;
18750 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18753 if(this.labelsm > 0){
18754 labelCfg.cls += ' col-sm-' + this.labelsm;
18755 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18758 if(this.labelxs > 0){
18759 labelCfg.cls += ' col-xs-' + this.labelxs;
18760 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18764 } else if ( this.fieldLabel.length) {
18769 cls : 'control-label',
18770 html : this.fieldLabel
18781 if(this.indicatorpos == 'right'){
18785 cls : 'control-label',
18786 html : this.fieldLabel,
18804 var settings = this;
18806 ['xs','sm','md','lg'].map(function(size){
18807 if (settings[size]) {
18808 cfg.cls += ' col-' + size + '-' + settings[size];
18815 initTouchView : function()
18817 this.renderTouchView();
18819 this.touchViewEl.on('scroll', function(){
18820 this.el.dom.scrollTop = 0;
18823 this.originalValue = this.getValue();
18825 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18827 this.inputEl().on("click", this.showTouchView, this);
18828 if (this.triggerEl) {
18829 this.triggerEl.on("click", this.showTouchView, this);
18833 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18834 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18836 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18838 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18839 this.store.on('load', this.onTouchViewLoad, this);
18840 this.store.on('loadexception', this.onTouchViewLoadException, this);
18842 if(this.hiddenName){
18844 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18846 this.hiddenField.dom.value =
18847 this.hiddenValue !== undefined ? this.hiddenValue :
18848 this.value !== undefined ? this.value : '';
18850 this.el.dom.removeAttribute('name');
18851 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18855 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18856 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18859 if(this.removable && !this.multiple){
18860 var close = this.closeTriggerEl();
18862 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18863 close.on('click', this.removeBtnClick, this, close);
18867 * fix the bug in Safari iOS8
18869 this.inputEl().on("focus", function(e){
18870 document.activeElement.blur();
18873 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18880 renderTouchView : function()
18882 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18883 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18885 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18886 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18888 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18889 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18890 this.touchViewBodyEl.setStyle('overflow', 'auto');
18892 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18893 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18895 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18896 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18900 showTouchView : function()
18906 this.touchViewHeaderEl.hide();
18908 if(this.modalTitle.length){
18909 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18910 this.touchViewHeaderEl.show();
18913 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18914 this.touchViewEl.show();
18916 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18918 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18919 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18921 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18923 if(this.modalTitle.length){
18924 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18927 this.touchViewBodyEl.setHeight(bodyHeight);
18931 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18933 this.touchViewEl.addClass(['in','show']);
18936 if(this._touchViewMask){
18937 Roo.get(document.body).addClass("x-body-masked");
18938 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18939 this._touchViewMask.setStyle('z-index', 10000);
18940 this._touchViewMask.addClass('show');
18943 this.doTouchViewQuery();
18947 hideTouchView : function()
18949 this.touchViewEl.removeClass(['in','show']);
18953 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18955 this.touchViewEl.setStyle('display', 'none');
18958 if(this._touchViewMask){
18959 this._touchViewMask.removeClass('show');
18960 Roo.get(document.body).removeClass("x-body-masked");
18964 setTouchViewValue : function()
18971 Roo.each(this.tickItems, function(o){
18976 this.hideTouchView();
18979 doTouchViewQuery : function()
18988 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18992 if(!this.alwaysQuery || this.mode == 'local'){
18993 this.onTouchViewLoad();
19000 onTouchViewBeforeLoad : function(combo,opts)
19006 onTouchViewLoad : function()
19008 if(this.store.getCount() < 1){
19009 this.onTouchViewEmptyResults();
19013 this.clearTouchView();
19015 var rawValue = this.getRawValue();
19017 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19019 this.tickItems = [];
19021 this.store.data.each(function(d, rowIndex){
19022 var row = this.touchViewListGroup.createChild(template);
19024 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19025 row.addClass(d.data.cls);
19028 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19031 html : d.data[this.displayField]
19034 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19035 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19038 row.removeClass('selected');
19039 if(!this.multiple && this.valueField &&
19040 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19043 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19044 row.addClass('selected');
19047 if(this.multiple && this.valueField &&
19048 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19052 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19053 this.tickItems.push(d.data);
19056 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19060 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19062 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19064 if(this.modalTitle.length){
19065 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19068 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19070 if(this.mobile_restrict_height && listHeight < bodyHeight){
19071 this.touchViewBodyEl.setHeight(listHeight);
19076 if(firstChecked && listHeight > bodyHeight){
19077 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19082 onTouchViewLoadException : function()
19084 this.hideTouchView();
19087 onTouchViewEmptyResults : function()
19089 this.clearTouchView();
19091 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19093 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19097 clearTouchView : function()
19099 this.touchViewListGroup.dom.innerHTML = '';
19102 onTouchViewClick : function(e, el, o)
19104 e.preventDefault();
19107 var rowIndex = o.rowIndex;
19109 var r = this.store.getAt(rowIndex);
19111 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19113 if(!this.multiple){
19114 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19115 c.dom.removeAttribute('checked');
19118 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19120 this.setFromData(r.data);
19122 var close = this.closeTriggerEl();
19128 this.hideTouchView();
19130 this.fireEvent('select', this, r, rowIndex);
19135 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19136 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19137 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19141 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19142 this.addItem(r.data);
19143 this.tickItems.push(r.data);
19147 getAutoCreateNativeIOS : function()
19150 cls: 'form-group' //input-group,
19155 cls : 'roo-ios-select'
19159 combobox.name = this.name;
19162 if (this.disabled) {
19163 combobox.disabled = true;
19166 var settings = this;
19168 ['xs','sm','md','lg'].map(function(size){
19169 if (settings[size]) {
19170 cfg.cls += ' col-' + size + '-' + settings[size];
19180 initIOSView : function()
19182 this.store.on('load', this.onIOSViewLoad, this);
19187 onIOSViewLoad : function()
19189 if(this.store.getCount() < 1){
19193 this.clearIOSView();
19195 if(this.allowBlank) {
19197 var default_text = '-- SELECT --';
19199 if(this.placeholder.length){
19200 default_text = this.placeholder;
19203 if(this.emptyTitle.length){
19204 default_text += ' - ' + this.emptyTitle + ' -';
19207 var opt = this.inputEl().createChild({
19210 html : default_text
19214 o[this.valueField] = 0;
19215 o[this.displayField] = default_text;
19217 this.ios_options.push({
19224 this.store.data.each(function(d, rowIndex){
19228 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19229 html = d.data[this.displayField];
19234 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19235 value = d.data[this.valueField];
19244 if(this.value == d.data[this.valueField]){
19245 option['selected'] = true;
19248 var opt = this.inputEl().createChild(option);
19250 this.ios_options.push({
19257 this.inputEl().on('change', function(){
19258 this.fireEvent('select', this);
19263 clearIOSView: function()
19265 this.inputEl().dom.innerHTML = '';
19267 this.ios_options = [];
19270 setIOSValue: function(v)
19274 if(!this.ios_options){
19278 Roo.each(this.ios_options, function(opts){
19280 opts.el.dom.removeAttribute('selected');
19282 if(opts.data[this.valueField] != v){
19286 opts.el.dom.setAttribute('selected', true);
19292 * @cfg {Boolean} grow
19296 * @cfg {Number} growMin
19300 * @cfg {Number} growMax
19309 Roo.apply(Roo.bootstrap.ComboBox, {
19313 cls: 'modal-header',
19335 cls: 'list-group-item',
19339 cls: 'roo-combobox-list-group-item-value'
19343 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19357 listItemCheckbox : {
19359 cls: 'list-group-item',
19363 cls: 'roo-combobox-list-group-item-value'
19367 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19383 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19388 cls: 'modal-footer',
19396 cls: 'col-xs-6 text-left',
19399 cls: 'btn btn-danger roo-touch-view-cancel',
19405 cls: 'col-xs-6 text-right',
19408 cls: 'btn btn-success roo-touch-view-ok',
19419 Roo.apply(Roo.bootstrap.ComboBox, {
19421 touchViewTemplate : {
19423 cls: 'modal fade roo-combobox-touch-view',
19427 cls: 'modal-dialog',
19428 style : 'position:fixed', // we have to fix position....
19432 cls: 'modal-content',
19434 Roo.bootstrap.ComboBox.header,
19435 Roo.bootstrap.ComboBox.body,
19436 Roo.bootstrap.ComboBox.footer
19445 * Ext JS Library 1.1.1
19446 * Copyright(c) 2006-2007, Ext JS, LLC.
19448 * Originally Released Under LGPL - original licence link has changed is not relivant.
19451 * <script type="text/javascript">
19456 * @extends Roo.util.Observable
19457 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19458 * This class also supports single and multi selection modes. <br>
19459 * Create a data model bound view:
19461 var store = new Roo.data.Store(...);
19463 var view = new Roo.View({
19465 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19467 singleSelect: true,
19468 selectedClass: "ydataview-selected",
19472 // listen for node click?
19473 view.on("click", function(vw, index, node, e){
19474 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19478 dataModel.load("foobar.xml");
19480 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19482 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19483 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19485 * Note: old style constructor is still suported (container, template, config)
19488 * Create a new View
19489 * @param {Object} config The config object
19492 Roo.View = function(config, depreciated_tpl, depreciated_config){
19494 this.parent = false;
19496 if (typeof(depreciated_tpl) == 'undefined') {
19497 // new way.. - universal constructor.
19498 Roo.apply(this, config);
19499 this.el = Roo.get(this.el);
19502 this.el = Roo.get(config);
19503 this.tpl = depreciated_tpl;
19504 Roo.apply(this, depreciated_config);
19506 this.wrapEl = this.el.wrap().wrap();
19507 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19510 if(typeof(this.tpl) == "string"){
19511 this.tpl = new Roo.Template(this.tpl);
19513 // support xtype ctors..
19514 this.tpl = new Roo.factory(this.tpl, Roo);
19518 this.tpl.compile();
19523 * @event beforeclick
19524 * Fires before a click is processed. Returns false to cancel the default action.
19525 * @param {Roo.View} this
19526 * @param {Number} index The index of the target node
19527 * @param {HTMLElement} node The target node
19528 * @param {Roo.EventObject} e The raw event object
19530 "beforeclick" : true,
19533 * Fires when a template node is clicked.
19534 * @param {Roo.View} this
19535 * @param {Number} index The index of the target node
19536 * @param {HTMLElement} node The target node
19537 * @param {Roo.EventObject} e The raw event object
19542 * Fires when a template node is double clicked.
19543 * @param {Roo.View} this
19544 * @param {Number} index The index of the target node
19545 * @param {HTMLElement} node The target node
19546 * @param {Roo.EventObject} e The raw event object
19550 * @event contextmenu
19551 * Fires when a template node is right clicked.
19552 * @param {Roo.View} this
19553 * @param {Number} index The index of the target node
19554 * @param {HTMLElement} node The target node
19555 * @param {Roo.EventObject} e The raw event object
19557 "contextmenu" : true,
19559 * @event selectionchange
19560 * Fires when the selected nodes change.
19561 * @param {Roo.View} this
19562 * @param {Array} selections Array of the selected nodes
19564 "selectionchange" : true,
19567 * @event beforeselect
19568 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19569 * @param {Roo.View} this
19570 * @param {HTMLElement} node The node to be selected
19571 * @param {Array} selections Array of currently selected nodes
19573 "beforeselect" : true,
19575 * @event preparedata
19576 * Fires on every row to render, to allow you to change the data.
19577 * @param {Roo.View} this
19578 * @param {Object} data to be rendered (change this)
19580 "preparedata" : true
19588 "click": this.onClick,
19589 "dblclick": this.onDblClick,
19590 "contextmenu": this.onContextMenu,
19594 this.selections = [];
19596 this.cmp = new Roo.CompositeElementLite([]);
19598 this.store = Roo.factory(this.store, Roo.data);
19599 this.setStore(this.store, true);
19602 if ( this.footer && this.footer.xtype) {
19604 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19606 this.footer.dataSource = this.store;
19607 this.footer.container = fctr;
19608 this.footer = Roo.factory(this.footer, Roo);
19609 fctr.insertFirst(this.el);
19611 // this is a bit insane - as the paging toolbar seems to detach the el..
19612 // dom.parentNode.parentNode.parentNode
19613 // they get detached?
19617 Roo.View.superclass.constructor.call(this);
19622 Roo.extend(Roo.View, Roo.util.Observable, {
19625 * @cfg {Roo.data.Store} store Data store to load data from.
19630 * @cfg {String|Roo.Element} el The container element.
19635 * @cfg {String|Roo.Template} tpl The template used by this View
19639 * @cfg {String} dataName the named area of the template to use as the data area
19640 * Works with domtemplates roo-name="name"
19644 * @cfg {String} selectedClass The css class to add to selected nodes
19646 selectedClass : "x-view-selected",
19648 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19653 * @cfg {String} text to display on mask (default Loading)
19657 * @cfg {Boolean} multiSelect Allow multiple selection
19659 multiSelect : false,
19661 * @cfg {Boolean} singleSelect Allow single selection
19663 singleSelect: false,
19666 * @cfg {Boolean} toggleSelect - selecting
19668 toggleSelect : false,
19671 * @cfg {Boolean} tickable - selecting
19676 * Returns the element this view is bound to.
19677 * @return {Roo.Element}
19679 getEl : function(){
19680 return this.wrapEl;
19686 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19688 refresh : function(){
19689 //Roo.log('refresh');
19692 // if we are using something like 'domtemplate', then
19693 // the what gets used is:
19694 // t.applySubtemplate(NAME, data, wrapping data..)
19695 // the outer template then get' applied with
19696 // the store 'extra data'
19697 // and the body get's added to the
19698 // roo-name="data" node?
19699 // <span class='roo-tpl-{name}'></span> ?????
19703 this.clearSelections();
19704 this.el.update("");
19706 var records = this.store.getRange();
19707 if(records.length < 1) {
19709 // is this valid?? = should it render a template??
19711 this.el.update(this.emptyText);
19715 if (this.dataName) {
19716 this.el.update(t.apply(this.store.meta)); //????
19717 el = this.el.child('.roo-tpl-' + this.dataName);
19720 for(var i = 0, len = records.length; i < len; i++){
19721 var data = this.prepareData(records[i].data, i, records[i]);
19722 this.fireEvent("preparedata", this, data, i, records[i]);
19724 var d = Roo.apply({}, data);
19727 Roo.apply(d, {'roo-id' : Roo.id()});
19731 Roo.each(this.parent.item, function(item){
19732 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19735 Roo.apply(d, {'roo-data-checked' : 'checked'});
19739 html[html.length] = Roo.util.Format.trim(
19741 t.applySubtemplate(this.dataName, d, this.store.meta) :
19748 el.update(html.join(""));
19749 this.nodes = el.dom.childNodes;
19750 this.updateIndexes(0);
19755 * Function to override to reformat the data that is sent to
19756 * the template for each node.
19757 * DEPRICATED - use the preparedata event handler.
19758 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19759 * a JSON object for an UpdateManager bound view).
19761 prepareData : function(data, index, record)
19763 this.fireEvent("preparedata", this, data, index, record);
19767 onUpdate : function(ds, record){
19768 // Roo.log('on update');
19769 this.clearSelections();
19770 var index = this.store.indexOf(record);
19771 var n = this.nodes[index];
19772 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19773 n.parentNode.removeChild(n);
19774 this.updateIndexes(index, index);
19780 onAdd : function(ds, records, index)
19782 //Roo.log(['on Add', ds, records, index] );
19783 this.clearSelections();
19784 if(this.nodes.length == 0){
19788 var n = this.nodes[index];
19789 for(var i = 0, len = records.length; i < len; i++){
19790 var d = this.prepareData(records[i].data, i, records[i]);
19792 this.tpl.insertBefore(n, d);
19795 this.tpl.append(this.el, d);
19798 this.updateIndexes(index);
19801 onRemove : function(ds, record, index){
19802 // Roo.log('onRemove');
19803 this.clearSelections();
19804 var el = this.dataName ?
19805 this.el.child('.roo-tpl-' + this.dataName) :
19808 el.dom.removeChild(this.nodes[index]);
19809 this.updateIndexes(index);
19813 * Refresh an individual node.
19814 * @param {Number} index
19816 refreshNode : function(index){
19817 this.onUpdate(this.store, this.store.getAt(index));
19820 updateIndexes : function(startIndex, endIndex){
19821 var ns = this.nodes;
19822 startIndex = startIndex || 0;
19823 endIndex = endIndex || ns.length - 1;
19824 for(var i = startIndex; i <= endIndex; i++){
19825 ns[i].nodeIndex = i;
19830 * Changes the data store this view uses and refresh the view.
19831 * @param {Store} store
19833 setStore : function(store, initial){
19834 if(!initial && this.store){
19835 this.store.un("datachanged", this.refresh);
19836 this.store.un("add", this.onAdd);
19837 this.store.un("remove", this.onRemove);
19838 this.store.un("update", this.onUpdate);
19839 this.store.un("clear", this.refresh);
19840 this.store.un("beforeload", this.onBeforeLoad);
19841 this.store.un("load", this.onLoad);
19842 this.store.un("loadexception", this.onLoad);
19846 store.on("datachanged", this.refresh, this);
19847 store.on("add", this.onAdd, this);
19848 store.on("remove", this.onRemove, this);
19849 store.on("update", this.onUpdate, this);
19850 store.on("clear", this.refresh, this);
19851 store.on("beforeload", this.onBeforeLoad, this);
19852 store.on("load", this.onLoad, this);
19853 store.on("loadexception", this.onLoad, this);
19861 * onbeforeLoad - masks the loading area.
19864 onBeforeLoad : function(store,opts)
19866 //Roo.log('onBeforeLoad');
19868 this.el.update("");
19870 this.el.mask(this.mask ? this.mask : "Loading" );
19872 onLoad : function ()
19879 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19880 * @param {HTMLElement} node
19881 * @return {HTMLElement} The template node
19883 findItemFromChild : function(node){
19884 var el = this.dataName ?
19885 this.el.child('.roo-tpl-' + this.dataName,true) :
19888 if(!node || node.parentNode == el){
19891 var p = node.parentNode;
19892 while(p && p != el){
19893 if(p.parentNode == el){
19902 onClick : function(e){
19903 var item = this.findItemFromChild(e.getTarget());
19905 var index = this.indexOf(item);
19906 if(this.onItemClick(item, index, e) !== false){
19907 this.fireEvent("click", this, index, item, e);
19910 this.clearSelections();
19915 onContextMenu : function(e){
19916 var item = this.findItemFromChild(e.getTarget());
19918 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19923 onDblClick : function(e){
19924 var item = this.findItemFromChild(e.getTarget());
19926 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19930 onItemClick : function(item, index, e)
19932 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19935 if (this.toggleSelect) {
19936 var m = this.isSelected(item) ? 'unselect' : 'select';
19939 _t[m](item, true, false);
19942 if(this.multiSelect || this.singleSelect){
19943 if(this.multiSelect && e.shiftKey && this.lastSelection){
19944 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19946 this.select(item, this.multiSelect && e.ctrlKey);
19947 this.lastSelection = item;
19950 if(!this.tickable){
19951 e.preventDefault();
19959 * Get the number of selected nodes.
19962 getSelectionCount : function(){
19963 return this.selections.length;
19967 * Get the currently selected nodes.
19968 * @return {Array} An array of HTMLElements
19970 getSelectedNodes : function(){
19971 return this.selections;
19975 * Get the indexes of the selected nodes.
19978 getSelectedIndexes : function(){
19979 var indexes = [], s = this.selections;
19980 for(var i = 0, len = s.length; i < len; i++){
19981 indexes.push(s[i].nodeIndex);
19987 * Clear all selections
19988 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19990 clearSelections : function(suppressEvent){
19991 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19992 this.cmp.elements = this.selections;
19993 this.cmp.removeClass(this.selectedClass);
19994 this.selections = [];
19995 if(!suppressEvent){
19996 this.fireEvent("selectionchange", this, this.selections);
20002 * Returns true if the passed node is selected
20003 * @param {HTMLElement/Number} node The node or node index
20004 * @return {Boolean}
20006 isSelected : function(node){
20007 var s = this.selections;
20011 node = this.getNode(node);
20012 return s.indexOf(node) !== -1;
20017 * @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
20018 * @param {Boolean} keepExisting (optional) true to keep existing selections
20019 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20021 select : function(nodeInfo, keepExisting, suppressEvent){
20022 if(nodeInfo instanceof Array){
20024 this.clearSelections(true);
20026 for(var i = 0, len = nodeInfo.length; i < len; i++){
20027 this.select(nodeInfo[i], true, true);
20031 var node = this.getNode(nodeInfo);
20032 if(!node || this.isSelected(node)){
20033 return; // already selected.
20036 this.clearSelections(true);
20039 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20040 Roo.fly(node).addClass(this.selectedClass);
20041 this.selections.push(node);
20042 if(!suppressEvent){
20043 this.fireEvent("selectionchange", this, this.selections);
20051 * @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
20052 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20053 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20055 unselect : function(nodeInfo, keepExisting, suppressEvent)
20057 if(nodeInfo instanceof Array){
20058 Roo.each(this.selections, function(s) {
20059 this.unselect(s, nodeInfo);
20063 var node = this.getNode(nodeInfo);
20064 if(!node || !this.isSelected(node)){
20065 //Roo.log("not selected");
20066 return; // not selected.
20070 Roo.each(this.selections, function(s) {
20072 Roo.fly(node).removeClass(this.selectedClass);
20079 this.selections= ns;
20080 this.fireEvent("selectionchange", this, this.selections);
20084 * Gets a template node.
20085 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20086 * @return {HTMLElement} The node or null if it wasn't found
20088 getNode : function(nodeInfo){
20089 if(typeof nodeInfo == "string"){
20090 return document.getElementById(nodeInfo);
20091 }else if(typeof nodeInfo == "number"){
20092 return this.nodes[nodeInfo];
20098 * Gets a range template nodes.
20099 * @param {Number} startIndex
20100 * @param {Number} endIndex
20101 * @return {Array} An array of nodes
20103 getNodes : function(start, end){
20104 var ns = this.nodes;
20105 start = start || 0;
20106 end = typeof end == "undefined" ? ns.length - 1 : end;
20109 for(var i = start; i <= end; i++){
20113 for(var i = start; i >= end; i--){
20121 * Finds the index of the passed node
20122 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20123 * @return {Number} The index of the node or -1
20125 indexOf : function(node){
20126 node = this.getNode(node);
20127 if(typeof node.nodeIndex == "number"){
20128 return node.nodeIndex;
20130 var ns = this.nodes;
20131 for(var i = 0, len = ns.length; i < len; i++){
20142 * based on jquery fullcalendar
20146 Roo.bootstrap = Roo.bootstrap || {};
20148 * @class Roo.bootstrap.Calendar
20149 * @extends Roo.bootstrap.Component
20150 * Bootstrap Calendar class
20151 * @cfg {Boolean} loadMask (true|false) default false
20152 * @cfg {Object} header generate the user specific header of the calendar, default false
20155 * Create a new Container
20156 * @param {Object} config The config object
20161 Roo.bootstrap.Calendar = function(config){
20162 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20166 * Fires when a date is selected
20167 * @param {DatePicker} this
20168 * @param {Date} date The selected date
20172 * @event monthchange
20173 * Fires when the displayed month changes
20174 * @param {DatePicker} this
20175 * @param {Date} date The selected month
20177 'monthchange': true,
20179 * @event evententer
20180 * Fires when mouse over an event
20181 * @param {Calendar} this
20182 * @param {event} Event
20184 'evententer': true,
20186 * @event eventleave
20187 * Fires when the mouse leaves an
20188 * @param {Calendar} this
20191 'eventleave': true,
20193 * @event eventclick
20194 * Fires when the mouse click an
20195 * @param {Calendar} this
20204 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20207 * @cfg {Roo.data.Store} store
20208 * The data source for the calendar
20212 * @cfg {Number} startDay
20213 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20221 getAutoCreate : function(){
20224 var fc_button = function(name, corner, style, content ) {
20225 return Roo.apply({},{
20227 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20229 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20232 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20243 style : 'width:100%',
20250 cls : 'fc-header-left',
20252 fc_button('prev', 'left', 'arrow', '‹' ),
20253 fc_button('next', 'right', 'arrow', '›' ),
20254 { tag: 'span', cls: 'fc-header-space' },
20255 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20263 cls : 'fc-header-center',
20267 cls: 'fc-header-title',
20270 html : 'month / year'
20278 cls : 'fc-header-right',
20280 /* fc_button('month', 'left', '', 'month' ),
20281 fc_button('week', '', '', 'week' ),
20282 fc_button('day', 'right', '', 'day' )
20294 header = this.header;
20297 var cal_heads = function() {
20299 // fixme - handle this.
20301 for (var i =0; i < Date.dayNames.length; i++) {
20302 var d = Date.dayNames[i];
20305 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20306 html : d.substring(0,3)
20310 ret[0].cls += ' fc-first';
20311 ret[6].cls += ' fc-last';
20314 var cal_cell = function(n) {
20317 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20322 cls: 'fc-day-number',
20326 cls: 'fc-day-content',
20330 style: 'position: relative;' // height: 17px;
20342 var cal_rows = function() {
20345 for (var r = 0; r < 6; r++) {
20352 for (var i =0; i < Date.dayNames.length; i++) {
20353 var d = Date.dayNames[i];
20354 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20357 row.cn[0].cls+=' fc-first';
20358 row.cn[0].cn[0].style = 'min-height:90px';
20359 row.cn[6].cls+=' fc-last';
20363 ret[0].cls += ' fc-first';
20364 ret[4].cls += ' fc-prev-last';
20365 ret[5].cls += ' fc-last';
20372 cls: 'fc-border-separate',
20373 style : 'width:100%',
20381 cls : 'fc-first fc-last',
20399 cls : 'fc-content',
20400 style : "position: relative;",
20403 cls : 'fc-view fc-view-month fc-grid',
20404 style : 'position: relative',
20405 unselectable : 'on',
20408 cls : 'fc-event-container',
20409 style : 'position:absolute;z-index:8;top:0;left:0;'
20427 initEvents : function()
20430 throw "can not find store for calendar";
20436 style: "text-align:center",
20440 style: "background-color:white;width:50%;margin:250 auto",
20444 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20455 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20457 var size = this.el.select('.fc-content', true).first().getSize();
20458 this.maskEl.setSize(size.width, size.height);
20459 this.maskEl.enableDisplayMode("block");
20460 if(!this.loadMask){
20461 this.maskEl.hide();
20464 this.store = Roo.factory(this.store, Roo.data);
20465 this.store.on('load', this.onLoad, this);
20466 this.store.on('beforeload', this.onBeforeLoad, this);
20470 this.cells = this.el.select('.fc-day',true);
20471 //Roo.log(this.cells);
20472 this.textNodes = this.el.query('.fc-day-number');
20473 this.cells.addClassOnOver('fc-state-hover');
20475 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20476 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20477 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20478 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20480 this.on('monthchange', this.onMonthChange, this);
20482 this.update(new Date().clearTime());
20485 resize : function() {
20486 var sz = this.el.getSize();
20488 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20489 this.el.select('.fc-day-content div',true).setHeight(34);
20494 showPrevMonth : function(e){
20495 this.update(this.activeDate.add("mo", -1));
20497 showToday : function(e){
20498 this.update(new Date().clearTime());
20501 showNextMonth : function(e){
20502 this.update(this.activeDate.add("mo", 1));
20506 showPrevYear : function(){
20507 this.update(this.activeDate.add("y", -1));
20511 showNextYear : function(){
20512 this.update(this.activeDate.add("y", 1));
20517 update : function(date)
20519 var vd = this.activeDate;
20520 this.activeDate = date;
20521 // if(vd && this.el){
20522 // var t = date.getTime();
20523 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20524 // Roo.log('using add remove');
20526 // this.fireEvent('monthchange', this, date);
20528 // this.cells.removeClass("fc-state-highlight");
20529 // this.cells.each(function(c){
20530 // if(c.dateValue == t){
20531 // c.addClass("fc-state-highlight");
20532 // setTimeout(function(){
20533 // try{c.dom.firstChild.focus();}catch(e){}
20543 var days = date.getDaysInMonth();
20545 var firstOfMonth = date.getFirstDateOfMonth();
20546 var startingPos = firstOfMonth.getDay()-this.startDay;
20548 if(startingPos < this.startDay){
20552 var pm = date.add(Date.MONTH, -1);
20553 var prevStart = pm.getDaysInMonth()-startingPos;
20555 this.cells = this.el.select('.fc-day',true);
20556 this.textNodes = this.el.query('.fc-day-number');
20557 this.cells.addClassOnOver('fc-state-hover');
20559 var cells = this.cells.elements;
20560 var textEls = this.textNodes;
20562 Roo.each(cells, function(cell){
20563 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20566 days += startingPos;
20568 // convert everything to numbers so it's fast
20569 var day = 86400000;
20570 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20573 //Roo.log(prevStart);
20575 var today = new Date().clearTime().getTime();
20576 var sel = date.clearTime().getTime();
20577 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20578 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20579 var ddMatch = this.disabledDatesRE;
20580 var ddText = this.disabledDatesText;
20581 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20582 var ddaysText = this.disabledDaysText;
20583 var format = this.format;
20585 var setCellClass = function(cal, cell){
20589 //Roo.log('set Cell Class');
20591 var t = d.getTime();
20595 cell.dateValue = t;
20597 cell.className += " fc-today";
20598 cell.className += " fc-state-highlight";
20599 cell.title = cal.todayText;
20602 // disable highlight in other month..
20603 //cell.className += " fc-state-highlight";
20608 cell.className = " fc-state-disabled";
20609 cell.title = cal.minText;
20613 cell.className = " fc-state-disabled";
20614 cell.title = cal.maxText;
20618 if(ddays.indexOf(d.getDay()) != -1){
20619 cell.title = ddaysText;
20620 cell.className = " fc-state-disabled";
20623 if(ddMatch && format){
20624 var fvalue = d.dateFormat(format);
20625 if(ddMatch.test(fvalue)){
20626 cell.title = ddText.replace("%0", fvalue);
20627 cell.className = " fc-state-disabled";
20631 if (!cell.initialClassName) {
20632 cell.initialClassName = cell.dom.className;
20635 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20640 for(; i < startingPos; i++) {
20641 textEls[i].innerHTML = (++prevStart);
20642 d.setDate(d.getDate()+1);
20644 cells[i].className = "fc-past fc-other-month";
20645 setCellClass(this, cells[i]);
20650 for(; i < days; i++){
20651 intDay = i - startingPos + 1;
20652 textEls[i].innerHTML = (intDay);
20653 d.setDate(d.getDate()+1);
20655 cells[i].className = ''; // "x-date-active";
20656 setCellClass(this, cells[i]);
20660 for(; i < 42; i++) {
20661 textEls[i].innerHTML = (++extraDays);
20662 d.setDate(d.getDate()+1);
20664 cells[i].className = "fc-future fc-other-month";
20665 setCellClass(this, cells[i]);
20668 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20670 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20672 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20673 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20675 if(totalRows != 6){
20676 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20677 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20680 this.fireEvent('monthchange', this, date);
20684 if(!this.internalRender){
20685 var main = this.el.dom.firstChild;
20686 var w = main.offsetWidth;
20687 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20688 Roo.fly(main).setWidth(w);
20689 this.internalRender = true;
20690 // opera does not respect the auto grow header center column
20691 // then, after it gets a width opera refuses to recalculate
20692 // without a second pass
20693 if(Roo.isOpera && !this.secondPass){
20694 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20695 this.secondPass = true;
20696 this.update.defer(10, this, [date]);
20703 findCell : function(dt) {
20704 dt = dt.clearTime().getTime();
20706 this.cells.each(function(c){
20707 //Roo.log("check " +c.dateValue + '?=' + dt);
20708 if(c.dateValue == dt){
20718 findCells : function(ev) {
20719 var s = ev.start.clone().clearTime().getTime();
20721 var e= ev.end.clone().clearTime().getTime();
20724 this.cells.each(function(c){
20725 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20727 if(c.dateValue > e){
20730 if(c.dateValue < s){
20739 // findBestRow: function(cells)
20743 // for (var i =0 ; i < cells.length;i++) {
20744 // ret = Math.max(cells[i].rows || 0,ret);
20751 addItem : function(ev)
20753 // look for vertical location slot in
20754 var cells = this.findCells(ev);
20756 // ev.row = this.findBestRow(cells);
20758 // work out the location.
20762 for(var i =0; i < cells.length; i++) {
20764 cells[i].row = cells[0].row;
20767 cells[i].row = cells[i].row + 1;
20777 if (crow.start.getY() == cells[i].getY()) {
20779 crow.end = cells[i];
20796 cells[0].events.push(ev);
20798 this.calevents.push(ev);
20801 clearEvents: function() {
20803 if(!this.calevents){
20807 Roo.each(this.cells.elements, function(c){
20813 Roo.each(this.calevents, function(e) {
20814 Roo.each(e.els, function(el) {
20815 el.un('mouseenter' ,this.onEventEnter, this);
20816 el.un('mouseleave' ,this.onEventLeave, this);
20821 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20827 renderEvents: function()
20831 this.cells.each(function(c) {
20840 if(c.row != c.events.length){
20841 r = 4 - (4 - (c.row - c.events.length));
20844 c.events = ev.slice(0, r);
20845 c.more = ev.slice(r);
20847 if(c.more.length && c.more.length == 1){
20848 c.events.push(c.more.pop());
20851 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20855 this.cells.each(function(c) {
20857 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20860 for (var e = 0; e < c.events.length; e++){
20861 var ev = c.events[e];
20862 var rows = ev.rows;
20864 for(var i = 0; i < rows.length; i++) {
20866 // how many rows should it span..
20869 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20870 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20872 unselectable : "on",
20875 cls: 'fc-event-inner',
20879 // cls: 'fc-event-time',
20880 // html : cells.length > 1 ? '' : ev.time
20884 cls: 'fc-event-title',
20885 html : String.format('{0}', ev.title)
20892 cls: 'ui-resizable-handle ui-resizable-e',
20893 html : '  '
20900 cfg.cls += ' fc-event-start';
20902 if ((i+1) == rows.length) {
20903 cfg.cls += ' fc-event-end';
20906 var ctr = _this.el.select('.fc-event-container',true).first();
20907 var cg = ctr.createChild(cfg);
20909 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20910 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20912 var r = (c.more.length) ? 1 : 0;
20913 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20914 cg.setWidth(ebox.right - sbox.x -2);
20916 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20917 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20918 cg.on('click', _this.onEventClick, _this, ev);
20929 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20930 style : 'position: absolute',
20931 unselectable : "on",
20934 cls: 'fc-event-inner',
20938 cls: 'fc-event-title',
20946 cls: 'ui-resizable-handle ui-resizable-e',
20947 html : '  '
20953 var ctr = _this.el.select('.fc-event-container',true).first();
20954 var cg = ctr.createChild(cfg);
20956 var sbox = c.select('.fc-day-content',true).first().getBox();
20957 var ebox = c.select('.fc-day-content',true).first().getBox();
20959 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20960 cg.setWidth(ebox.right - sbox.x -2);
20962 cg.on('click', _this.onMoreEventClick, _this, c.more);
20972 onEventEnter: function (e, el,event,d) {
20973 this.fireEvent('evententer', this, el, event);
20976 onEventLeave: function (e, el,event,d) {
20977 this.fireEvent('eventleave', this, el, event);
20980 onEventClick: function (e, el,event,d) {
20981 this.fireEvent('eventclick', this, el, event);
20984 onMonthChange: function () {
20988 onMoreEventClick: function(e, el, more)
20992 this.calpopover.placement = 'right';
20993 this.calpopover.setTitle('More');
20995 this.calpopover.setContent('');
20997 var ctr = this.calpopover.el.select('.popover-content', true).first();
20999 Roo.each(more, function(m){
21001 cls : 'fc-event-hori fc-event-draggable',
21004 var cg = ctr.createChild(cfg);
21006 cg.on('click', _this.onEventClick, _this, m);
21009 this.calpopover.show(el);
21014 onLoad: function ()
21016 this.calevents = [];
21019 if(this.store.getCount() > 0){
21020 this.store.data.each(function(d){
21023 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21024 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21025 time : d.data.start_time,
21026 title : d.data.title,
21027 description : d.data.description,
21028 venue : d.data.venue
21033 this.renderEvents();
21035 if(this.calevents.length && this.loadMask){
21036 this.maskEl.hide();
21040 onBeforeLoad: function()
21042 this.clearEvents();
21044 this.maskEl.show();
21058 * @class Roo.bootstrap.Popover
21059 * @extends Roo.bootstrap.Component
21060 * Bootstrap Popover class
21061 * @cfg {String} html contents of the popover (or false to use children..)
21062 * @cfg {String} title of popover (or false to hide)
21063 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21064 * @cfg {String} trigger click || hover (or false to trigger manually)
21065 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21066 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21067 * - if false and it has a 'parent' then it will be automatically added to that element
21068 * - if string - Roo.get will be called
21069 * @cfg {Number} delay - delay before showing
21072 * Create a new Popover
21073 * @param {Object} config The config object
21076 Roo.bootstrap.Popover = function(config){
21077 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21083 * After the popover show
21085 * @param {Roo.bootstrap.Popover} this
21090 * After the popover hide
21092 * @param {Roo.bootstrap.Popover} this
21098 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21103 placement : 'right',
21104 trigger : 'hover', // hover
21110 can_build_overlaid : false,
21112 maskEl : false, // the mask element
21115 alignEl : false, // when show is called with an element - this get's stored.
21117 getChildContainer : function()
21119 return this.contentEl;
21122 getPopoverHeader : function()
21124 this.title = true; // flag not to hide it..
21125 this.headerEl.addClass('p-0');
21126 return this.headerEl
21130 getAutoCreate : function(){
21133 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21134 style: 'display:block',
21140 cls : 'popover-inner ',
21144 cls: 'popover-title popover-header',
21145 html : this.title === false ? '' : this.title
21148 cls : 'popover-content popover-body ' + (this.cls || ''),
21149 html : this.html || ''
21160 * @param {string} the title
21162 setTitle: function(str)
21166 this.headerEl.dom.innerHTML = str;
21171 * @param {string} the body content
21173 setContent: function(str)
21176 if (this.contentEl) {
21177 this.contentEl.dom.innerHTML = str;
21181 // as it get's added to the bottom of the page.
21182 onRender : function(ct, position)
21184 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21189 var cfg = Roo.apply({}, this.getAutoCreate());
21193 cfg.cls += ' ' + this.cls;
21196 cfg.style = this.style;
21198 //Roo.log("adding to ");
21199 this.el = Roo.get(document.body).createChild(cfg, position);
21200 // Roo.log(this.el);
21203 this.contentEl = this.el.select('.popover-content',true).first();
21204 this.headerEl = this.el.select('.popover-title',true).first();
21207 if(typeof(this.items) != 'undefined'){
21208 var items = this.items;
21211 for(var i =0;i < items.length;i++) {
21212 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21216 this.items = nitems;
21218 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21219 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21226 resizeMask : function()
21228 this.maskEl.setSize(
21229 Roo.lib.Dom.getViewWidth(true),
21230 Roo.lib.Dom.getViewHeight(true)
21234 initEvents : function()
21238 Roo.bootstrap.Popover.register(this);
21241 this.arrowEl = this.el.select('.arrow',true).first();
21242 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21243 this.el.enableDisplayMode('block');
21247 if (this.over === false && !this.parent()) {
21250 if (this.triggers === false) {
21255 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21256 var triggers = this.trigger ? this.trigger.split(' ') : [];
21257 Roo.each(triggers, function(trigger) {
21259 if (trigger == 'click') {
21260 on_el.on('click', this.toggle, this);
21261 } else if (trigger != 'manual') {
21262 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21263 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21265 on_el.on(eventIn ,this.enter, this);
21266 on_el.on(eventOut, this.leave, this);
21276 toggle : function () {
21277 this.hoverState == 'in' ? this.leave() : this.enter();
21280 enter : function () {
21282 clearTimeout(this.timeout);
21284 this.hoverState = 'in';
21286 if (!this.delay || !this.delay.show) {
21291 this.timeout = setTimeout(function () {
21292 if (_t.hoverState == 'in') {
21295 }, this.delay.show)
21298 leave : function() {
21299 clearTimeout(this.timeout);
21301 this.hoverState = 'out';
21303 if (!this.delay || !this.delay.hide) {
21308 this.timeout = setTimeout(function () {
21309 if (_t.hoverState == 'out') {
21312 }, this.delay.hide)
21316 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21317 * @param {string} (left|right|top|bottom) position
21319 show : function (on_el, placement)
21321 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21322 on_el = on_el || false; // default to false
21325 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21326 on_el = this.parent().el;
21327 } else if (this.over) {
21328 on_el = Roo.get(this.over);
21333 this.alignEl = Roo.get( on_el );
21336 this.render(document.body);
21342 if (this.title === false) {
21343 this.headerEl.hide();
21348 this.el.dom.style.display = 'block';
21351 if (this.alignEl) {
21352 this.updatePosition(this.placement, true);
21355 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21356 var es = this.el.getSize();
21357 var x = Roo.lib.Dom.getViewWidth()/2;
21358 var y = Roo.lib.Dom.getViewHeight()/2;
21359 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21364 //var arrow = this.el.select('.arrow',true).first();
21365 //arrow.set(align[2],
21367 this.el.addClass('in');
21371 this.hoverState = 'in';
21374 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21375 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21376 this.maskEl.dom.style.display = 'block';
21377 this.maskEl.addClass('show');
21379 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21381 this.fireEvent('show', this);
21385 * fire this manually after loading a grid in the table for example
21386 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21387 * @param {Boolean} try and move it if we cant get right position.
21389 updatePosition : function(placement, try_move)
21391 // allow for calling with no parameters
21392 placement = placement ? placement : this.placement;
21393 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21395 this.el.removeClass([
21396 'fade','top','bottom', 'left', 'right','in',
21397 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21399 this.el.addClass(placement + ' bs-popover-' + placement);
21401 if (!this.alignEl ) {
21405 switch (placement) {
21407 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21408 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21409 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21410 //normal display... or moved up/down.
21411 this.el.setXY(offset);
21412 var xy = this.alignEl.getAnchorXY('tr', false);
21414 this.arrowEl.setXY(xy);
21417 // continue through...
21418 return this.updatePosition('left', false);
21422 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21423 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21424 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21425 //normal display... or moved up/down.
21426 this.el.setXY(offset);
21427 var xy = this.alignEl.getAnchorXY('tl', false);
21428 xy[0]-=10;xy[1]+=5; // << fix me
21429 this.arrowEl.setXY(xy);
21433 return this.updatePosition('right', false);
21436 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21437 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21438 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21439 //normal display... or moved up/down.
21440 this.el.setXY(offset);
21441 var xy = this.alignEl.getAnchorXY('t', false);
21442 xy[1]-=10; // << fix me
21443 this.arrowEl.setXY(xy);
21447 return this.updatePosition('bottom', false);
21450 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21451 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21452 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21453 //normal display... or moved up/down.
21454 this.el.setXY(offset);
21455 var xy = this.alignEl.getAnchorXY('b', false);
21456 xy[1]+=2; // << fix me
21457 this.arrowEl.setXY(xy);
21461 return this.updatePosition('top', false);
21472 this.el.setXY([0,0]);
21473 this.el.removeClass('in');
21475 this.hoverState = null;
21476 this.maskEl.hide(); // always..
21477 this.fireEvent('hide', this);
21483 Roo.apply(Roo.bootstrap.Popover, {
21486 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21487 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21488 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21489 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21494 clickHander : false,
21498 onMouseDown : function(e)
21500 if (this.popups.length && !e.getTarget(".roo-popover")) {
21501 /// what is nothing is showing..
21510 register : function(popup)
21512 if (!Roo.bootstrap.Popover.clickHandler) {
21513 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21515 // hide other popups.
21516 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21517 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21518 this.hideAll(); //<< why?
21519 //this.popups.push(popup);
21521 hideAll : function()
21523 this.popups.forEach(function(p) {
21527 onShow : function() {
21528 Roo.bootstrap.Popover.popups.push(this);
21530 onHide : function() {
21531 Roo.bootstrap.Popover.popups.remove(this);
21537 * Card header - holder for the card header elements.
21542 * @class Roo.bootstrap.PopoverNav
21543 * @extends Roo.bootstrap.NavGroup
21544 * Bootstrap Popover header navigation class
21546 * Create a new Popover Header Navigation
21547 * @param {Object} config The config object
21550 Roo.bootstrap.PopoverNav = function(config){
21551 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21554 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21557 container_method : 'getPopoverHeader'
21575 * @class Roo.bootstrap.Progress
21576 * @extends Roo.bootstrap.Component
21577 * Bootstrap Progress class
21578 * @cfg {Boolean} striped striped of the progress bar
21579 * @cfg {Boolean} active animated of the progress bar
21583 * Create a new Progress
21584 * @param {Object} config The config object
21587 Roo.bootstrap.Progress = function(config){
21588 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21591 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21596 getAutoCreate : function(){
21604 cfg.cls += ' progress-striped';
21608 cfg.cls += ' active';
21627 * @class Roo.bootstrap.ProgressBar
21628 * @extends Roo.bootstrap.Component
21629 * Bootstrap ProgressBar class
21630 * @cfg {Number} aria_valuenow aria-value now
21631 * @cfg {Number} aria_valuemin aria-value min
21632 * @cfg {Number} aria_valuemax aria-value max
21633 * @cfg {String} label label for the progress bar
21634 * @cfg {String} panel (success | info | warning | danger )
21635 * @cfg {String} role role of the progress bar
21636 * @cfg {String} sr_only text
21640 * Create a new ProgressBar
21641 * @param {Object} config The config object
21644 Roo.bootstrap.ProgressBar = function(config){
21645 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21648 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21652 aria_valuemax : 100,
21658 getAutoCreate : function()
21663 cls: 'progress-bar',
21664 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21676 cfg.role = this.role;
21679 if(this.aria_valuenow){
21680 cfg['aria-valuenow'] = this.aria_valuenow;
21683 if(this.aria_valuemin){
21684 cfg['aria-valuemin'] = this.aria_valuemin;
21687 if(this.aria_valuemax){
21688 cfg['aria-valuemax'] = this.aria_valuemax;
21691 if(this.label && !this.sr_only){
21692 cfg.html = this.label;
21696 cfg.cls += ' progress-bar-' + this.panel;
21702 update : function(aria_valuenow)
21704 this.aria_valuenow = aria_valuenow;
21706 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21721 * @class Roo.bootstrap.TabGroup
21722 * @extends Roo.bootstrap.Column
21723 * Bootstrap Column class
21724 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21725 * @cfg {Boolean} carousel true to make the group behave like a carousel
21726 * @cfg {Boolean} bullets show bullets for the panels
21727 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21728 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21729 * @cfg {Boolean} showarrow (true|false) show arrow default true
21732 * Create a new TabGroup
21733 * @param {Object} config The config object
21736 Roo.bootstrap.TabGroup = function(config){
21737 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21739 this.navId = Roo.id();
21742 Roo.bootstrap.TabGroup.register(this);
21746 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21749 transition : false,
21754 slideOnTouch : false,
21757 getAutoCreate : function()
21759 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21761 cfg.cls += ' tab-content';
21763 if (this.carousel) {
21764 cfg.cls += ' carousel slide';
21767 cls : 'carousel-inner',
21771 if(this.bullets && !Roo.isTouch){
21774 cls : 'carousel-bullets',
21778 if(this.bullets_cls){
21779 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21786 cfg.cn[0].cn.push(bullets);
21789 if(this.showarrow){
21790 cfg.cn[0].cn.push({
21792 class : 'carousel-arrow',
21796 class : 'carousel-prev',
21800 class : 'fa fa-chevron-left'
21806 class : 'carousel-next',
21810 class : 'fa fa-chevron-right'
21823 initEvents: function()
21825 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21826 // this.el.on("touchstart", this.onTouchStart, this);
21829 if(this.autoslide){
21832 this.slideFn = window.setInterval(function() {
21833 _this.showPanelNext();
21837 if(this.showarrow){
21838 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21839 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21845 // onTouchStart : function(e, el, o)
21847 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21851 // this.showPanelNext();
21855 getChildContainer : function()
21857 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21861 * register a Navigation item
21862 * @param {Roo.bootstrap.NavItem} the navitem to add
21864 register : function(item)
21866 this.tabs.push( item);
21867 item.navId = this.navId; // not really needed..
21872 getActivePanel : function()
21875 Roo.each(this.tabs, function(t) {
21885 getPanelByName : function(n)
21888 Roo.each(this.tabs, function(t) {
21889 if (t.tabId == n) {
21897 indexOfPanel : function(p)
21900 Roo.each(this.tabs, function(t,i) {
21901 if (t.tabId == p.tabId) {
21910 * show a specific panel
21911 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21912 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21914 showPanel : function (pan)
21916 if(this.transition || typeof(pan) == 'undefined'){
21917 Roo.log("waiting for the transitionend");
21921 if (typeof(pan) == 'number') {
21922 pan = this.tabs[pan];
21925 if (typeof(pan) == 'string') {
21926 pan = this.getPanelByName(pan);
21929 var cur = this.getActivePanel();
21932 Roo.log('pan or acitve pan is undefined');
21936 if (pan.tabId == this.getActivePanel().tabId) {
21940 if (false === cur.fireEvent('beforedeactivate')) {
21944 if(this.bullets > 0 && !Roo.isTouch){
21945 this.setActiveBullet(this.indexOfPanel(pan));
21948 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21950 //class="carousel-item carousel-item-next carousel-item-left"
21952 this.transition = true;
21953 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21954 var lr = dir == 'next' ? 'left' : 'right';
21955 pan.el.addClass(dir); // or prev
21956 pan.el.addClass('carousel-item-' + dir); // or prev
21957 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21958 cur.el.addClass(lr); // or right
21959 pan.el.addClass(lr);
21960 cur.el.addClass('carousel-item-' +lr); // or right
21961 pan.el.addClass('carousel-item-' +lr);
21965 cur.el.on('transitionend', function() {
21966 Roo.log("trans end?");
21968 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21969 pan.setActive(true);
21971 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21972 cur.setActive(false);
21974 _this.transition = false;
21976 }, this, { single: true } );
21981 cur.setActive(false);
21982 pan.setActive(true);
21987 showPanelNext : function()
21989 var i = this.indexOfPanel(this.getActivePanel());
21991 if (i >= this.tabs.length - 1 && !this.autoslide) {
21995 if (i >= this.tabs.length - 1 && this.autoslide) {
21999 this.showPanel(this.tabs[i+1]);
22002 showPanelPrev : function()
22004 var i = this.indexOfPanel(this.getActivePanel());
22006 if (i < 1 && !this.autoslide) {
22010 if (i < 1 && this.autoslide) {
22011 i = this.tabs.length;
22014 this.showPanel(this.tabs[i-1]);
22018 addBullet: function()
22020 if(!this.bullets || Roo.isTouch){
22023 var ctr = this.el.select('.carousel-bullets',true).first();
22024 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22025 var bullet = ctr.createChild({
22026 cls : 'bullet bullet-' + i
22027 },ctr.dom.lastChild);
22032 bullet.on('click', (function(e, el, o, ii, t){
22034 e.preventDefault();
22036 this.showPanel(ii);
22038 if(this.autoslide && this.slideFn){
22039 clearInterval(this.slideFn);
22040 this.slideFn = window.setInterval(function() {
22041 _this.showPanelNext();
22045 }).createDelegate(this, [i, bullet], true));
22050 setActiveBullet : function(i)
22056 Roo.each(this.el.select('.bullet', true).elements, function(el){
22057 el.removeClass('selected');
22060 var bullet = this.el.select('.bullet-' + i, true).first();
22066 bullet.addClass('selected');
22077 Roo.apply(Roo.bootstrap.TabGroup, {
22081 * register a Navigation Group
22082 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22084 register : function(navgrp)
22086 this.groups[navgrp.navId] = navgrp;
22090 * fetch a Navigation Group based on the navigation ID
22091 * if one does not exist , it will get created.
22092 * @param {string} the navgroup to add
22093 * @returns {Roo.bootstrap.NavGroup} the navgroup
22095 get: function(navId) {
22096 if (typeof(this.groups[navId]) == 'undefined') {
22097 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22099 return this.groups[navId] ;
22114 * @class Roo.bootstrap.TabPanel
22115 * @extends Roo.bootstrap.Component
22116 * Bootstrap TabPanel class
22117 * @cfg {Boolean} active panel active
22118 * @cfg {String} html panel content
22119 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22120 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22121 * @cfg {String} href click to link..
22122 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22126 * Create a new TabPanel
22127 * @param {Object} config The config object
22130 Roo.bootstrap.TabPanel = function(config){
22131 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22135 * Fires when the active status changes
22136 * @param {Roo.bootstrap.TabPanel} this
22137 * @param {Boolean} state the new state
22142 * @event beforedeactivate
22143 * Fires before a tab is de-activated - can be used to do validation on a form.
22144 * @param {Roo.bootstrap.TabPanel} this
22145 * @return {Boolean} false if there is an error
22148 'beforedeactivate': true
22151 this.tabId = this.tabId || Roo.id();
22155 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22162 touchSlide : false,
22163 getAutoCreate : function(){
22168 // item is needed for carousel - not sure if it has any effect otherwise
22169 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22170 html: this.html || ''
22174 cfg.cls += ' active';
22178 cfg.tabId = this.tabId;
22186 initEvents: function()
22188 var p = this.parent();
22190 this.navId = this.navId || p.navId;
22192 if (typeof(this.navId) != 'undefined') {
22193 // not really needed.. but just in case.. parent should be a NavGroup.
22194 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22198 var i = tg.tabs.length - 1;
22200 if(this.active && tg.bullets > 0 && i < tg.bullets){
22201 tg.setActiveBullet(i);
22205 this.el.on('click', this.onClick, this);
22207 if(Roo.isTouch && this.touchSlide){
22208 this.el.on("touchstart", this.onTouchStart, this);
22209 this.el.on("touchmove", this.onTouchMove, this);
22210 this.el.on("touchend", this.onTouchEnd, this);
22215 onRender : function(ct, position)
22217 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22220 setActive : function(state)
22222 Roo.log("panel - set active " + this.tabId + "=" + state);
22224 this.active = state;
22226 this.el.removeClass('active');
22228 } else if (!this.el.hasClass('active')) {
22229 this.el.addClass('active');
22232 this.fireEvent('changed', this, state);
22235 onClick : function(e)
22237 e.preventDefault();
22239 if(!this.href.length){
22243 window.location.href = this.href;
22252 onTouchStart : function(e)
22254 this.swiping = false;
22256 this.startX = e.browserEvent.touches[0].clientX;
22257 this.startY = e.browserEvent.touches[0].clientY;
22260 onTouchMove : function(e)
22262 this.swiping = true;
22264 this.endX = e.browserEvent.touches[0].clientX;
22265 this.endY = e.browserEvent.touches[0].clientY;
22268 onTouchEnd : function(e)
22275 var tabGroup = this.parent();
22277 if(this.endX > this.startX){ // swiping right
22278 tabGroup.showPanelPrev();
22282 if(this.startX > this.endX){ // swiping left
22283 tabGroup.showPanelNext();
22302 * @class Roo.bootstrap.DateField
22303 * @extends Roo.bootstrap.Input
22304 * Bootstrap DateField class
22305 * @cfg {Number} weekStart default 0
22306 * @cfg {String} viewMode default empty, (months|years)
22307 * @cfg {String} minViewMode default empty, (months|years)
22308 * @cfg {Number} startDate default -Infinity
22309 * @cfg {Number} endDate default Infinity
22310 * @cfg {Boolean} todayHighlight default false
22311 * @cfg {Boolean} todayBtn default false
22312 * @cfg {Boolean} calendarWeeks default false
22313 * @cfg {Object} daysOfWeekDisabled default empty
22314 * @cfg {Boolean} singleMode default false (true | false)
22316 * @cfg {Boolean} keyboardNavigation default true
22317 * @cfg {String} language default en
22320 * Create a new DateField
22321 * @param {Object} config The config object
22324 Roo.bootstrap.DateField = function(config){
22325 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22329 * Fires when this field show.
22330 * @param {Roo.bootstrap.DateField} this
22331 * @param {Mixed} date The date value
22336 * Fires when this field hide.
22337 * @param {Roo.bootstrap.DateField} this
22338 * @param {Mixed} date The date value
22343 * Fires when select a date.
22344 * @param {Roo.bootstrap.DateField} this
22345 * @param {Mixed} date The date value
22349 * @event beforeselect
22350 * Fires when before select a date.
22351 * @param {Roo.bootstrap.DateField} this
22352 * @param {Mixed} date The date value
22354 beforeselect : true
22358 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22361 * @cfg {String} format
22362 * The default date format string which can be overriden for localization support. The format must be
22363 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22367 * @cfg {String} altFormats
22368 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22369 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22371 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22379 todayHighlight : false,
22385 keyboardNavigation: true,
22387 calendarWeeks: false,
22389 startDate: -Infinity,
22393 daysOfWeekDisabled: [],
22397 singleMode : false,
22399 UTCDate: function()
22401 return new Date(Date.UTC.apply(Date, arguments));
22404 UTCToday: function()
22406 var today = new Date();
22407 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22410 getDate: function() {
22411 var d = this.getUTCDate();
22412 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22415 getUTCDate: function() {
22419 setDate: function(d) {
22420 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22423 setUTCDate: function(d) {
22425 this.setValue(this.formatDate(this.date));
22428 onRender: function(ct, position)
22431 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22433 this.language = this.language || 'en';
22434 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22435 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22437 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22438 this.format = this.format || 'm/d/y';
22439 this.isInline = false;
22440 this.isInput = true;
22441 this.component = this.el.select('.add-on', true).first() || false;
22442 this.component = (this.component && this.component.length === 0) ? false : this.component;
22443 this.hasInput = this.component && this.inputEl().length;
22445 if (typeof(this.minViewMode === 'string')) {
22446 switch (this.minViewMode) {
22448 this.minViewMode = 1;
22451 this.minViewMode = 2;
22454 this.minViewMode = 0;
22459 if (typeof(this.viewMode === 'string')) {
22460 switch (this.viewMode) {
22473 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22475 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22477 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22479 this.picker().on('mousedown', this.onMousedown, this);
22480 this.picker().on('click', this.onClick, this);
22482 this.picker().addClass('datepicker-dropdown');
22484 this.startViewMode = this.viewMode;
22486 if(this.singleMode){
22487 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22488 v.setVisibilityMode(Roo.Element.DISPLAY);
22492 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22493 v.setStyle('width', '189px');
22497 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22498 if(!this.calendarWeeks){
22503 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22504 v.attr('colspan', function(i, val){
22505 return parseInt(val) + 1;
22510 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22512 this.setStartDate(this.startDate);
22513 this.setEndDate(this.endDate);
22515 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22522 if(this.isInline) {
22527 picker : function()
22529 return this.pickerEl;
22530 // return this.el.select('.datepicker', true).first();
22533 fillDow: function()
22535 var dowCnt = this.weekStart;
22544 if(this.calendarWeeks){
22552 while (dowCnt < this.weekStart + 7) {
22556 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22560 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22563 fillMonths: function()
22566 var months = this.picker().select('>.datepicker-months td', true).first();
22568 months.dom.innerHTML = '';
22574 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22577 months.createChild(month);
22584 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;
22586 if (this.date < this.startDate) {
22587 this.viewDate = new Date(this.startDate);
22588 } else if (this.date > this.endDate) {
22589 this.viewDate = new Date(this.endDate);
22591 this.viewDate = new Date(this.date);
22599 var d = new Date(this.viewDate),
22600 year = d.getUTCFullYear(),
22601 month = d.getUTCMonth(),
22602 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22603 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22604 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22605 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22606 currentDate = this.date && this.date.valueOf(),
22607 today = this.UTCToday();
22609 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22611 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22613 // this.picker.select('>tfoot th.today').
22614 // .text(dates[this.language].today)
22615 // .toggle(this.todayBtn !== false);
22617 this.updateNavArrows();
22620 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22622 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22624 prevMonth.setUTCDate(day);
22626 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22628 var nextMonth = new Date(prevMonth);
22630 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22632 nextMonth = nextMonth.valueOf();
22634 var fillMonths = false;
22636 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22638 while(prevMonth.valueOf() <= nextMonth) {
22641 if (prevMonth.getUTCDay() === this.weekStart) {
22643 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22651 if(this.calendarWeeks){
22652 // ISO 8601: First week contains first thursday.
22653 // ISO also states week starts on Monday, but we can be more abstract here.
22655 // Start of current week: based on weekstart/current date
22656 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22657 // Thursday of this week
22658 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22659 // First Thursday of year, year from thursday
22660 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22661 // Calendar week: ms between thursdays, div ms per day, div 7 days
22662 calWeek = (th - yth) / 864e5 / 7 + 1;
22664 fillMonths.cn.push({
22672 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22674 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22677 if (this.todayHighlight &&
22678 prevMonth.getUTCFullYear() == today.getFullYear() &&
22679 prevMonth.getUTCMonth() == today.getMonth() &&
22680 prevMonth.getUTCDate() == today.getDate()) {
22681 clsName += ' today';
22684 if (currentDate && prevMonth.valueOf() === currentDate) {
22685 clsName += ' active';
22688 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22689 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22690 clsName += ' disabled';
22693 fillMonths.cn.push({
22695 cls: 'day ' + clsName,
22696 html: prevMonth.getDate()
22699 prevMonth.setDate(prevMonth.getDate()+1);
22702 var currentYear = this.date && this.date.getUTCFullYear();
22703 var currentMonth = this.date && this.date.getUTCMonth();
22705 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22707 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22708 v.removeClass('active');
22710 if(currentYear === year && k === currentMonth){
22711 v.addClass('active');
22714 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22715 v.addClass('disabled');
22721 year = parseInt(year/10, 10) * 10;
22723 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22725 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22728 for (var i = -1; i < 11; i++) {
22729 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22731 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22739 showMode: function(dir)
22742 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22745 Roo.each(this.picker().select('>div',true).elements, function(v){
22746 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22749 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22754 if(this.isInline) {
22758 this.picker().removeClass(['bottom', 'top']);
22760 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22762 * place to the top of element!
22766 this.picker().addClass('top');
22767 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22772 this.picker().addClass('bottom');
22774 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22777 parseDate : function(value)
22779 if(!value || value instanceof Date){
22782 var v = Date.parseDate(value, this.format);
22783 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22784 v = Date.parseDate(value, 'Y-m-d');
22786 if(!v && this.altFormats){
22787 if(!this.altFormatsArray){
22788 this.altFormatsArray = this.altFormats.split("|");
22790 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22791 v = Date.parseDate(value, this.altFormatsArray[i]);
22797 formatDate : function(date, fmt)
22799 return (!date || !(date instanceof Date)) ?
22800 date : date.dateFormat(fmt || this.format);
22803 onFocus : function()
22805 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22809 onBlur : function()
22811 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22813 var d = this.inputEl().getValue();
22820 showPopup : function()
22822 this.picker().show();
22826 this.fireEvent('showpopup', this, this.date);
22829 hidePopup : function()
22831 if(this.isInline) {
22834 this.picker().hide();
22835 this.viewMode = this.startViewMode;
22838 this.fireEvent('hidepopup', this, this.date);
22842 onMousedown: function(e)
22844 e.stopPropagation();
22845 e.preventDefault();
22850 Roo.bootstrap.DateField.superclass.keyup.call(this);
22854 setValue: function(v)
22856 if(this.fireEvent('beforeselect', this, v) !== false){
22857 var d = new Date(this.parseDate(v) ).clearTime();
22859 if(isNaN(d.getTime())){
22860 this.date = this.viewDate = '';
22861 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22865 v = this.formatDate(d);
22867 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22869 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22873 this.fireEvent('select', this, this.date);
22877 getValue: function()
22879 return this.formatDate(this.date);
22882 fireKey: function(e)
22884 if (!this.picker().isVisible()){
22885 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22891 var dateChanged = false,
22893 newDate, newViewDate;
22898 e.preventDefault();
22902 if (!this.keyboardNavigation) {
22905 dir = e.keyCode == 37 ? -1 : 1;
22908 newDate = this.moveYear(this.date, dir);
22909 newViewDate = this.moveYear(this.viewDate, dir);
22910 } else if (e.shiftKey){
22911 newDate = this.moveMonth(this.date, dir);
22912 newViewDate = this.moveMonth(this.viewDate, dir);
22914 newDate = new Date(this.date);
22915 newDate.setUTCDate(this.date.getUTCDate() + dir);
22916 newViewDate = new Date(this.viewDate);
22917 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22919 if (this.dateWithinRange(newDate)){
22920 this.date = newDate;
22921 this.viewDate = newViewDate;
22922 this.setValue(this.formatDate(this.date));
22924 e.preventDefault();
22925 dateChanged = true;
22930 if (!this.keyboardNavigation) {
22933 dir = e.keyCode == 38 ? -1 : 1;
22935 newDate = this.moveYear(this.date, dir);
22936 newViewDate = this.moveYear(this.viewDate, dir);
22937 } else if (e.shiftKey){
22938 newDate = this.moveMonth(this.date, dir);
22939 newViewDate = this.moveMonth(this.viewDate, dir);
22941 newDate = new Date(this.date);
22942 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22943 newViewDate = new Date(this.viewDate);
22944 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22946 if (this.dateWithinRange(newDate)){
22947 this.date = newDate;
22948 this.viewDate = newViewDate;
22949 this.setValue(this.formatDate(this.date));
22951 e.preventDefault();
22952 dateChanged = true;
22956 this.setValue(this.formatDate(this.date));
22958 e.preventDefault();
22961 this.setValue(this.formatDate(this.date));
22975 onClick: function(e)
22977 e.stopPropagation();
22978 e.preventDefault();
22980 var target = e.getTarget();
22982 if(target.nodeName.toLowerCase() === 'i'){
22983 target = Roo.get(target).dom.parentNode;
22986 var nodeName = target.nodeName;
22987 var className = target.className;
22988 var html = target.innerHTML;
22989 //Roo.log(nodeName);
22991 switch(nodeName.toLowerCase()) {
22993 switch(className) {
22999 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23000 switch(this.viewMode){
23002 this.viewDate = this.moveMonth(this.viewDate, dir);
23006 this.viewDate = this.moveYear(this.viewDate, dir);
23012 var date = new Date();
23013 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23015 this.setValue(this.formatDate(this.date));
23022 if (className.indexOf('disabled') < 0) {
23023 if (!this.viewDate) {
23024 this.viewDate = new Date();
23026 this.viewDate.setUTCDate(1);
23027 if (className.indexOf('month') > -1) {
23028 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23030 var year = parseInt(html, 10) || 0;
23031 this.viewDate.setUTCFullYear(year);
23035 if(this.singleMode){
23036 this.setValue(this.formatDate(this.viewDate));
23047 //Roo.log(className);
23048 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23049 var day = parseInt(html, 10) || 1;
23050 var year = (this.viewDate || new Date()).getUTCFullYear(),
23051 month = (this.viewDate || new Date()).getUTCMonth();
23053 if (className.indexOf('old') > -1) {
23060 } else if (className.indexOf('new') > -1) {
23068 //Roo.log([year,month,day]);
23069 this.date = this.UTCDate(year, month, day,0,0,0,0);
23070 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23072 //Roo.log(this.formatDate(this.date));
23073 this.setValue(this.formatDate(this.date));
23080 setStartDate: function(startDate)
23082 this.startDate = startDate || -Infinity;
23083 if (this.startDate !== -Infinity) {
23084 this.startDate = this.parseDate(this.startDate);
23087 this.updateNavArrows();
23090 setEndDate: function(endDate)
23092 this.endDate = endDate || Infinity;
23093 if (this.endDate !== Infinity) {
23094 this.endDate = this.parseDate(this.endDate);
23097 this.updateNavArrows();
23100 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23102 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23103 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23104 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23106 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23107 return parseInt(d, 10);
23110 this.updateNavArrows();
23113 updateNavArrows: function()
23115 if(this.singleMode){
23119 var d = new Date(this.viewDate),
23120 year = d.getUTCFullYear(),
23121 month = d.getUTCMonth();
23123 Roo.each(this.picker().select('.prev', true).elements, function(v){
23125 switch (this.viewMode) {
23128 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23134 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23141 Roo.each(this.picker().select('.next', true).elements, function(v){
23143 switch (this.viewMode) {
23146 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23152 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23160 moveMonth: function(date, dir)
23165 var new_date = new Date(date.valueOf()),
23166 day = new_date.getUTCDate(),
23167 month = new_date.getUTCMonth(),
23168 mag = Math.abs(dir),
23170 dir = dir > 0 ? 1 : -1;
23173 // If going back one month, make sure month is not current month
23174 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23176 return new_date.getUTCMonth() == month;
23178 // If going forward one month, make sure month is as expected
23179 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23181 return new_date.getUTCMonth() != new_month;
23183 new_month = month + dir;
23184 new_date.setUTCMonth(new_month);
23185 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23186 if (new_month < 0 || new_month > 11) {
23187 new_month = (new_month + 12) % 12;
23190 // For magnitudes >1, move one month at a time...
23191 for (var i=0; i<mag; i++) {
23192 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23193 new_date = this.moveMonth(new_date, dir);
23195 // ...then reset the day, keeping it in the new month
23196 new_month = new_date.getUTCMonth();
23197 new_date.setUTCDate(day);
23199 return new_month != new_date.getUTCMonth();
23202 // Common date-resetting loop -- if date is beyond end of month, make it
23205 new_date.setUTCDate(--day);
23206 new_date.setUTCMonth(new_month);
23211 moveYear: function(date, dir)
23213 return this.moveMonth(date, dir*12);
23216 dateWithinRange: function(date)
23218 return date >= this.startDate && date <= this.endDate;
23224 this.picker().remove();
23227 validateValue : function(value)
23229 if(this.getVisibilityEl().hasClass('hidden')){
23233 if(value.length < 1) {
23234 if(this.allowBlank){
23240 if(value.length < this.minLength){
23243 if(value.length > this.maxLength){
23247 var vt = Roo.form.VTypes;
23248 if(!vt[this.vtype](value, this)){
23252 if(typeof this.validator == "function"){
23253 var msg = this.validator(value);
23259 if(this.regex && !this.regex.test(value)){
23263 if(typeof(this.parseDate(value)) == 'undefined'){
23267 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23271 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23281 this.date = this.viewDate = '';
23283 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23288 Roo.apply(Roo.bootstrap.DateField, {
23299 html: '<i class="fa fa-arrow-left"/>'
23309 html: '<i class="fa fa-arrow-right"/>'
23351 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23352 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23353 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23354 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23355 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23368 navFnc: 'FullYear',
23373 navFnc: 'FullYear',
23378 Roo.apply(Roo.bootstrap.DateField, {
23382 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23386 cls: 'datepicker-days',
23390 cls: 'table-condensed',
23392 Roo.bootstrap.DateField.head,
23396 Roo.bootstrap.DateField.footer
23403 cls: 'datepicker-months',
23407 cls: 'table-condensed',
23409 Roo.bootstrap.DateField.head,
23410 Roo.bootstrap.DateField.content,
23411 Roo.bootstrap.DateField.footer
23418 cls: 'datepicker-years',
23422 cls: 'table-condensed',
23424 Roo.bootstrap.DateField.head,
23425 Roo.bootstrap.DateField.content,
23426 Roo.bootstrap.DateField.footer
23445 * @class Roo.bootstrap.TimeField
23446 * @extends Roo.bootstrap.Input
23447 * Bootstrap DateField class
23451 * Create a new TimeField
23452 * @param {Object} config The config object
23455 Roo.bootstrap.TimeField = function(config){
23456 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23460 * Fires when this field show.
23461 * @param {Roo.bootstrap.DateField} thisthis
23462 * @param {Mixed} date The date value
23467 * Fires when this field hide.
23468 * @param {Roo.bootstrap.DateField} this
23469 * @param {Mixed} date The date value
23474 * Fires when select a date.
23475 * @param {Roo.bootstrap.DateField} this
23476 * @param {Mixed} date The date value
23482 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23485 * @cfg {String} format
23486 * The default time format string which can be overriden for localization support. The format must be
23487 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23491 getAutoCreate : function()
23493 this.after = '<i class="fa far fa-clock"></i>';
23494 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23498 onRender: function(ct, position)
23501 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23503 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23505 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23507 this.pop = this.picker().select('>.datepicker-time',true).first();
23508 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23510 this.picker().on('mousedown', this.onMousedown, this);
23511 this.picker().on('click', this.onClick, this);
23513 this.picker().addClass('datepicker-dropdown');
23518 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23519 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23520 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23521 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23522 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23523 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23527 fireKey: function(e){
23528 if (!this.picker().isVisible()){
23529 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23535 e.preventDefault();
23543 this.onTogglePeriod();
23546 this.onIncrementMinutes();
23549 this.onDecrementMinutes();
23558 onClick: function(e) {
23559 e.stopPropagation();
23560 e.preventDefault();
23563 picker : function()
23565 return this.pickerEl;
23568 fillTime: function()
23570 var time = this.pop.select('tbody', true).first();
23572 time.dom.innerHTML = '';
23587 cls: 'hours-up fa fas fa-chevron-up'
23607 cls: 'minutes-up fa fas fa-chevron-up'
23628 cls: 'timepicker-hour',
23643 cls: 'timepicker-minute',
23658 cls: 'btn btn-primary period',
23680 cls: 'hours-down fa fas fa-chevron-down'
23700 cls: 'minutes-down fa fas fa-chevron-down'
23718 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23725 var hours = this.time.getHours();
23726 var minutes = this.time.getMinutes();
23739 hours = hours - 12;
23743 hours = '0' + hours;
23747 minutes = '0' + minutes;
23750 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23751 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23752 this.pop.select('button', true).first().dom.innerHTML = period;
23758 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23760 var cls = ['bottom'];
23762 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23769 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23773 //this.picker().setXY(20000,20000);
23774 this.picker().addClass(cls.join('-'));
23778 Roo.each(cls, function(c){
23783 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23784 //_this.picker().setTop(_this.inputEl().getHeight());
23788 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23790 //_this.picker().setTop(0 - _this.picker().getHeight());
23795 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23799 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23807 onFocus : function()
23809 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23813 onBlur : function()
23815 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23821 this.picker().show();
23826 this.fireEvent('show', this, this.date);
23831 this.picker().hide();
23834 this.fireEvent('hide', this, this.date);
23837 setTime : function()
23840 this.setValue(this.time.format(this.format));
23842 this.fireEvent('select', this, this.date);
23847 onMousedown: function(e){
23848 e.stopPropagation();
23849 e.preventDefault();
23852 onIncrementHours: function()
23854 Roo.log('onIncrementHours');
23855 this.time = this.time.add(Date.HOUR, 1);
23860 onDecrementHours: function()
23862 Roo.log('onDecrementHours');
23863 this.time = this.time.add(Date.HOUR, -1);
23867 onIncrementMinutes: function()
23869 Roo.log('onIncrementMinutes');
23870 this.time = this.time.add(Date.MINUTE, 1);
23874 onDecrementMinutes: function()
23876 Roo.log('onDecrementMinutes');
23877 this.time = this.time.add(Date.MINUTE, -1);
23881 onTogglePeriod: function()
23883 Roo.log('onTogglePeriod');
23884 this.time = this.time.add(Date.HOUR, 12);
23892 Roo.apply(Roo.bootstrap.TimeField, {
23896 cls: 'datepicker dropdown-menu',
23900 cls: 'datepicker-time',
23904 cls: 'table-condensed',
23933 cls: 'btn btn-info ok',
23961 * @class Roo.bootstrap.MonthField
23962 * @extends Roo.bootstrap.Input
23963 * Bootstrap MonthField class
23965 * @cfg {String} language default en
23968 * Create a new MonthField
23969 * @param {Object} config The config object
23972 Roo.bootstrap.MonthField = function(config){
23973 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23978 * Fires when this field show.
23979 * @param {Roo.bootstrap.MonthField} this
23980 * @param {Mixed} date The date value
23985 * Fires when this field hide.
23986 * @param {Roo.bootstrap.MonthField} this
23987 * @param {Mixed} date The date value
23992 * Fires when select a date.
23993 * @param {Roo.bootstrap.MonthField} this
23994 * @param {String} oldvalue The old value
23995 * @param {String} newvalue The new value
24001 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
24003 onRender: function(ct, position)
24006 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24008 this.language = this.language || 'en';
24009 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24010 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24012 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24013 this.isInline = false;
24014 this.isInput = true;
24015 this.component = this.el.select('.add-on', true).first() || false;
24016 this.component = (this.component && this.component.length === 0) ? false : this.component;
24017 this.hasInput = this.component && this.inputEL().length;
24019 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24021 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24023 this.picker().on('mousedown', this.onMousedown, this);
24024 this.picker().on('click', this.onClick, this);
24026 this.picker().addClass('datepicker-dropdown');
24028 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24029 v.setStyle('width', '189px');
24036 if(this.isInline) {
24042 setValue: function(v, suppressEvent)
24044 var o = this.getValue();
24046 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24050 if(suppressEvent !== true){
24051 this.fireEvent('select', this, o, v);
24056 getValue: function()
24061 onClick: function(e)
24063 e.stopPropagation();
24064 e.preventDefault();
24066 var target = e.getTarget();
24068 if(target.nodeName.toLowerCase() === 'i'){
24069 target = Roo.get(target).dom.parentNode;
24072 var nodeName = target.nodeName;
24073 var className = target.className;
24074 var html = target.innerHTML;
24076 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24080 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24082 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24088 picker : function()
24090 return this.pickerEl;
24093 fillMonths: function()
24096 var months = this.picker().select('>.datepicker-months td', true).first();
24098 months.dom.innerHTML = '';
24104 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24107 months.createChild(month);
24116 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24117 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24120 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24121 e.removeClass('active');
24123 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24124 e.addClass('active');
24131 if(this.isInline) {
24135 this.picker().removeClass(['bottom', 'top']);
24137 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24139 * place to the top of element!
24143 this.picker().addClass('top');
24144 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24149 this.picker().addClass('bottom');
24151 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24154 onFocus : function()
24156 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24160 onBlur : function()
24162 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24164 var d = this.inputEl().getValue();
24173 this.picker().show();
24174 this.picker().select('>.datepicker-months', true).first().show();
24178 this.fireEvent('show', this, this.date);
24183 if(this.isInline) {
24186 this.picker().hide();
24187 this.fireEvent('hide', this, this.date);
24191 onMousedown: function(e)
24193 e.stopPropagation();
24194 e.preventDefault();
24199 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24203 fireKey: function(e)
24205 if (!this.picker().isVisible()){
24206 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24217 e.preventDefault();
24221 dir = e.keyCode == 37 ? -1 : 1;
24223 this.vIndex = this.vIndex + dir;
24225 if(this.vIndex < 0){
24229 if(this.vIndex > 11){
24233 if(isNaN(this.vIndex)){
24237 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24243 dir = e.keyCode == 38 ? -1 : 1;
24245 this.vIndex = this.vIndex + dir * 4;
24247 if(this.vIndex < 0){
24251 if(this.vIndex > 11){
24255 if(isNaN(this.vIndex)){
24259 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24264 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24265 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24269 e.preventDefault();
24272 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24273 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24289 this.picker().remove();
24294 Roo.apply(Roo.bootstrap.MonthField, {
24313 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24314 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24319 Roo.apply(Roo.bootstrap.MonthField, {
24323 cls: 'datepicker dropdown-menu roo-dynamic',
24327 cls: 'datepicker-months',
24331 cls: 'table-condensed',
24333 Roo.bootstrap.DateField.content
24353 * @class Roo.bootstrap.CheckBox
24354 * @extends Roo.bootstrap.Input
24355 * Bootstrap CheckBox class
24357 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24358 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24359 * @cfg {String} boxLabel The text that appears beside the checkbox
24360 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24361 * @cfg {Boolean} checked initnal the element
24362 * @cfg {Boolean} inline inline the element (default false)
24363 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24364 * @cfg {String} tooltip label tooltip
24367 * Create a new CheckBox
24368 * @param {Object} config The config object
24371 Roo.bootstrap.CheckBox = function(config){
24372 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24377 * Fires when the element is checked or unchecked.
24378 * @param {Roo.bootstrap.CheckBox} this This input
24379 * @param {Boolean} checked The new checked value
24384 * Fires when the element is click.
24385 * @param {Roo.bootstrap.CheckBox} this This input
24392 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24394 inputType: 'checkbox',
24403 // checkbox success does not make any sense really..
24408 getAutoCreate : function()
24410 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24416 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24419 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24425 type : this.inputType,
24426 value : this.inputValue,
24427 cls : 'roo-' + this.inputType, //'form-box',
24428 placeholder : this.placeholder || ''
24432 if(this.inputType != 'radio'){
24436 cls : 'roo-hidden-value',
24437 value : this.checked ? this.inputValue : this.valueOff
24442 if (this.weight) { // Validity check?
24443 cfg.cls += " " + this.inputType + "-" + this.weight;
24446 if (this.disabled) {
24447 input.disabled=true;
24451 input.checked = this.checked;
24456 input.name = this.name;
24458 if(this.inputType != 'radio'){
24459 hidden.name = this.name;
24460 input.name = '_hidden_' + this.name;
24465 input.cls += ' input-' + this.size;
24470 ['xs','sm','md','lg'].map(function(size){
24471 if (settings[size]) {
24472 cfg.cls += ' col-' + size + '-' + settings[size];
24476 var inputblock = input;
24478 if (this.before || this.after) {
24481 cls : 'input-group',
24486 inputblock.cn.push({
24488 cls : 'input-group-addon',
24493 inputblock.cn.push(input);
24495 if(this.inputType != 'radio'){
24496 inputblock.cn.push(hidden);
24500 inputblock.cn.push({
24502 cls : 'input-group-addon',
24508 var boxLabelCfg = false;
24514 //'for': id, // box label is handled by onclick - so no for...
24516 html: this.boxLabel
24519 boxLabelCfg.tooltip = this.tooltip;
24525 if (align ==='left' && this.fieldLabel.length) {
24526 // Roo.log("left and has label");
24531 cls : 'control-label',
24532 html : this.fieldLabel
24543 cfg.cn[1].cn.push(boxLabelCfg);
24546 if(this.labelWidth > 12){
24547 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24550 if(this.labelWidth < 13 && this.labelmd == 0){
24551 this.labelmd = this.labelWidth;
24554 if(this.labellg > 0){
24555 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24556 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24559 if(this.labelmd > 0){
24560 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24561 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24564 if(this.labelsm > 0){
24565 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24566 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24569 if(this.labelxs > 0){
24570 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24571 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24574 } else if ( this.fieldLabel.length) {
24575 // Roo.log(" label");
24579 tag: this.boxLabel ? 'span' : 'label',
24581 cls: 'control-label box-input-label',
24582 //cls : 'input-group-addon',
24583 html : this.fieldLabel
24590 cfg.cn.push(boxLabelCfg);
24595 // Roo.log(" no label && no align");
24596 cfg.cn = [ inputblock ] ;
24598 cfg.cn.push(boxLabelCfg);
24606 if(this.inputType != 'radio'){
24607 cfg.cn.push(hidden);
24615 * return the real input element.
24617 inputEl: function ()
24619 return this.el.select('input.roo-' + this.inputType,true).first();
24621 hiddenEl: function ()
24623 return this.el.select('input.roo-hidden-value',true).first();
24626 labelEl: function()
24628 return this.el.select('label.control-label',true).first();
24630 /* depricated... */
24634 return this.labelEl();
24637 boxLabelEl: function()
24639 return this.el.select('label.box-label',true).first();
24642 initEvents : function()
24644 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24646 this.inputEl().on('click', this.onClick, this);
24648 if (this.boxLabel) {
24649 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24652 this.startValue = this.getValue();
24655 Roo.bootstrap.CheckBox.register(this);
24659 onClick : function(e)
24661 if(this.fireEvent('click', this, e) !== false){
24662 this.setChecked(!this.checked);
24667 setChecked : function(state,suppressEvent)
24669 this.startValue = this.getValue();
24671 if(this.inputType == 'radio'){
24673 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24674 e.dom.checked = false;
24677 this.inputEl().dom.checked = true;
24679 this.inputEl().dom.value = this.inputValue;
24681 if(suppressEvent !== true){
24682 this.fireEvent('check', this, true);
24690 this.checked = state;
24692 this.inputEl().dom.checked = state;
24695 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24697 if(suppressEvent !== true){
24698 this.fireEvent('check', this, state);
24704 getValue : function()
24706 if(this.inputType == 'radio'){
24707 return this.getGroupValue();
24710 return this.hiddenEl().dom.value;
24714 getGroupValue : function()
24716 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24720 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24723 setValue : function(v,suppressEvent)
24725 if(this.inputType == 'radio'){
24726 this.setGroupValue(v, suppressEvent);
24730 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24735 setGroupValue : function(v, suppressEvent)
24737 this.startValue = this.getValue();
24739 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24740 e.dom.checked = false;
24742 if(e.dom.value == v){
24743 e.dom.checked = true;
24747 if(suppressEvent !== true){
24748 this.fireEvent('check', this, true);
24756 validate : function()
24758 if(this.getVisibilityEl().hasClass('hidden')){
24764 (this.inputType == 'radio' && this.validateRadio()) ||
24765 (this.inputType == 'checkbox' && this.validateCheckbox())
24771 this.markInvalid();
24775 validateRadio : function()
24777 if(this.getVisibilityEl().hasClass('hidden')){
24781 if(this.allowBlank){
24787 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24788 if(!e.dom.checked){
24800 validateCheckbox : function()
24803 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24804 //return (this.getValue() == this.inputValue) ? true : false;
24807 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24815 for(var i in group){
24816 if(group[i].el.isVisible(true)){
24824 for(var i in group){
24829 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24836 * Mark this field as valid
24838 markValid : function()
24842 this.fireEvent('valid', this);
24844 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24847 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24854 if(this.inputType == 'radio'){
24855 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24856 var fg = e.findParent('.form-group', false, true);
24857 if (Roo.bootstrap.version == 3) {
24858 fg.removeClass([_this.invalidClass, _this.validClass]);
24859 fg.addClass(_this.validClass);
24861 fg.removeClass(['is-valid', 'is-invalid']);
24862 fg.addClass('is-valid');
24870 var fg = this.el.findParent('.form-group', false, true);
24871 if (Roo.bootstrap.version == 3) {
24872 fg.removeClass([this.invalidClass, this.validClass]);
24873 fg.addClass(this.validClass);
24875 fg.removeClass(['is-valid', 'is-invalid']);
24876 fg.addClass('is-valid');
24881 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24887 for(var i in group){
24888 var fg = group[i].el.findParent('.form-group', false, true);
24889 if (Roo.bootstrap.version == 3) {
24890 fg.removeClass([this.invalidClass, this.validClass]);
24891 fg.addClass(this.validClass);
24893 fg.removeClass(['is-valid', 'is-invalid']);
24894 fg.addClass('is-valid');
24900 * Mark this field as invalid
24901 * @param {String} msg The validation message
24903 markInvalid : function(msg)
24905 if(this.allowBlank){
24911 this.fireEvent('invalid', this, msg);
24913 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24916 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24920 label.markInvalid();
24923 if(this.inputType == 'radio'){
24925 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24926 var fg = e.findParent('.form-group', false, true);
24927 if (Roo.bootstrap.version == 3) {
24928 fg.removeClass([_this.invalidClass, _this.validClass]);
24929 fg.addClass(_this.invalidClass);
24931 fg.removeClass(['is-invalid', 'is-valid']);
24932 fg.addClass('is-invalid');
24940 var fg = this.el.findParent('.form-group', false, true);
24941 if (Roo.bootstrap.version == 3) {
24942 fg.removeClass([_this.invalidClass, _this.validClass]);
24943 fg.addClass(_this.invalidClass);
24945 fg.removeClass(['is-invalid', 'is-valid']);
24946 fg.addClass('is-invalid');
24951 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24957 for(var i in group){
24958 var fg = group[i].el.findParent('.form-group', false, true);
24959 if (Roo.bootstrap.version == 3) {
24960 fg.removeClass([_this.invalidClass, _this.validClass]);
24961 fg.addClass(_this.invalidClass);
24963 fg.removeClass(['is-invalid', 'is-valid']);
24964 fg.addClass('is-invalid');
24970 clearInvalid : function()
24972 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24974 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24976 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24978 if (label && label.iconEl) {
24979 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24980 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24984 disable : function()
24986 if(this.inputType != 'radio'){
24987 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24994 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24995 _this.getActionEl().addClass(this.disabledClass);
24996 e.dom.disabled = true;
25000 this.disabled = true;
25001 this.fireEvent("disable", this);
25005 enable : function()
25007 if(this.inputType != 'radio'){
25008 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25015 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25016 _this.getActionEl().removeClass(this.disabledClass);
25017 e.dom.disabled = false;
25021 this.disabled = false;
25022 this.fireEvent("enable", this);
25026 setBoxLabel : function(v)
25031 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25037 Roo.apply(Roo.bootstrap.CheckBox, {
25042 * register a CheckBox Group
25043 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25045 register : function(checkbox)
25047 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25048 this.groups[checkbox.groupId] = {};
25051 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25055 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25059 * fetch a CheckBox Group based on the group ID
25060 * @param {string} the group ID
25061 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25063 get: function(groupId) {
25064 if (typeof(this.groups[groupId]) == 'undefined') {
25068 return this.groups[groupId] ;
25081 * @class Roo.bootstrap.Radio
25082 * @extends Roo.bootstrap.Component
25083 * Bootstrap Radio class
25084 * @cfg {String} boxLabel - the label associated
25085 * @cfg {String} value - the value of radio
25088 * Create a new Radio
25089 * @param {Object} config The config object
25091 Roo.bootstrap.Radio = function(config){
25092 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25096 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25102 getAutoCreate : function()
25106 cls : 'form-group radio',
25111 html : this.boxLabel
25119 initEvents : function()
25121 this.parent().register(this);
25123 this.el.on('click', this.onClick, this);
25127 onClick : function(e)
25129 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25130 this.setChecked(true);
25134 setChecked : function(state, suppressEvent)
25136 this.parent().setValue(this.value, suppressEvent);
25140 setBoxLabel : function(v)
25145 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25160 * @class Roo.bootstrap.SecurePass
25161 * @extends Roo.bootstrap.Input
25162 * Bootstrap SecurePass class
25166 * Create a new SecurePass
25167 * @param {Object} config The config object
25170 Roo.bootstrap.SecurePass = function (config) {
25171 // these go here, so the translation tool can replace them..
25173 PwdEmpty: "Please type a password, and then retype it to confirm.",
25174 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25175 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25176 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25177 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25178 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25179 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25180 TooWeak: "Your password is Too Weak."
25182 this.meterLabel = "Password strength:";
25183 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25184 this.meterClass = [
25185 "roo-password-meter-tooweak",
25186 "roo-password-meter-weak",
25187 "roo-password-meter-medium",
25188 "roo-password-meter-strong",
25189 "roo-password-meter-grey"
25194 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25197 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25199 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25201 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25202 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25203 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25204 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25205 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25206 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25207 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25217 * @cfg {String/Object} Label for the strength meter (defaults to
25218 * 'Password strength:')
25223 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25224 * ['Weak', 'Medium', 'Strong'])
25227 pwdStrengths: false,
25240 initEvents: function ()
25242 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25244 if (this.el.is('input[type=password]') && Roo.isSafari) {
25245 this.el.on('keydown', this.SafariOnKeyDown, this);
25248 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25251 onRender: function (ct, position)
25253 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25254 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25255 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25257 this.trigger.createChild({
25262 cls: 'roo-password-meter-grey col-xs-12',
25265 //width: this.meterWidth + 'px'
25269 cls: 'roo-password-meter-text'
25275 if (this.hideTrigger) {
25276 this.trigger.setDisplayed(false);
25278 this.setSize(this.width || '', this.height || '');
25281 onDestroy: function ()
25283 if (this.trigger) {
25284 this.trigger.removeAllListeners();
25285 this.trigger.remove();
25288 this.wrap.remove();
25290 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25293 checkStrength: function ()
25295 var pwd = this.inputEl().getValue();
25296 if (pwd == this._lastPwd) {
25301 if (this.ClientSideStrongPassword(pwd)) {
25303 } else if (this.ClientSideMediumPassword(pwd)) {
25305 } else if (this.ClientSideWeakPassword(pwd)) {
25311 Roo.log('strength1: ' + strength);
25313 //var pm = this.trigger.child('div/div/div').dom;
25314 var pm = this.trigger.child('div/div');
25315 pm.removeClass(this.meterClass);
25316 pm.addClass(this.meterClass[strength]);
25319 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25321 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25323 this._lastPwd = pwd;
25327 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25329 this._lastPwd = '';
25331 var pm = this.trigger.child('div/div');
25332 pm.removeClass(this.meterClass);
25333 pm.addClass('roo-password-meter-grey');
25336 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25339 this.inputEl().dom.type='password';
25342 validateValue: function (value)
25344 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25347 if (value.length == 0) {
25348 if (this.allowBlank) {
25349 this.clearInvalid();
25353 this.markInvalid(this.errors.PwdEmpty);
25354 this.errorMsg = this.errors.PwdEmpty;
25362 if (!value.match(/[\x21-\x7e]+/)) {
25363 this.markInvalid(this.errors.PwdBadChar);
25364 this.errorMsg = this.errors.PwdBadChar;
25367 if (value.length < 6) {
25368 this.markInvalid(this.errors.PwdShort);
25369 this.errorMsg = this.errors.PwdShort;
25372 if (value.length > 16) {
25373 this.markInvalid(this.errors.PwdLong);
25374 this.errorMsg = this.errors.PwdLong;
25378 if (this.ClientSideStrongPassword(value)) {
25380 } else if (this.ClientSideMediumPassword(value)) {
25382 } else if (this.ClientSideWeakPassword(value)) {
25389 if (strength < 2) {
25390 //this.markInvalid(this.errors.TooWeak);
25391 this.errorMsg = this.errors.TooWeak;
25396 console.log('strength2: ' + strength);
25398 //var pm = this.trigger.child('div/div/div').dom;
25400 var pm = this.trigger.child('div/div');
25401 pm.removeClass(this.meterClass);
25402 pm.addClass(this.meterClass[strength]);
25404 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25406 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25408 this.errorMsg = '';
25412 CharacterSetChecks: function (type)
25415 this.fResult = false;
25418 isctype: function (character, type)
25421 case this.kCapitalLetter:
25422 if (character >= 'A' && character <= 'Z') {
25427 case this.kSmallLetter:
25428 if (character >= 'a' && character <= 'z') {
25434 if (character >= '0' && character <= '9') {
25439 case this.kPunctuation:
25440 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25451 IsLongEnough: function (pwd, size)
25453 return !(pwd == null || isNaN(size) || pwd.length < size);
25456 SpansEnoughCharacterSets: function (word, nb)
25458 if (!this.IsLongEnough(word, nb))
25463 var characterSetChecks = new Array(
25464 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25465 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25468 for (var index = 0; index < word.length; ++index) {
25469 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25470 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25471 characterSetChecks[nCharSet].fResult = true;
25478 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25479 if (characterSetChecks[nCharSet].fResult) {
25484 if (nCharSets < nb) {
25490 ClientSideStrongPassword: function (pwd)
25492 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25495 ClientSideMediumPassword: function (pwd)
25497 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25500 ClientSideWeakPassword: function (pwd)
25502 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25505 })//<script type="text/javascript">
25508 * Based Ext JS Library 1.1.1
25509 * Copyright(c) 2006-2007, Ext JS, LLC.
25515 * @class Roo.HtmlEditorCore
25516 * @extends Roo.Component
25517 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25519 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25522 Roo.HtmlEditorCore = function(config){
25525 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25530 * @event initialize
25531 * Fires when the editor is fully initialized (including the iframe)
25532 * @param {Roo.HtmlEditorCore} this
25537 * Fires when the editor is first receives the focus. Any insertion must wait
25538 * until after this event.
25539 * @param {Roo.HtmlEditorCore} this
25543 * @event beforesync
25544 * Fires before the textarea is updated with content from the editor iframe. Return false
25545 * to cancel the sync.
25546 * @param {Roo.HtmlEditorCore} this
25547 * @param {String} html
25551 * @event beforepush
25552 * Fires before the iframe editor is updated with content from the textarea. Return false
25553 * to cancel the push.
25554 * @param {Roo.HtmlEditorCore} this
25555 * @param {String} html
25560 * Fires when the textarea is updated with content from the editor iframe.
25561 * @param {Roo.HtmlEditorCore} this
25562 * @param {String} html
25567 * Fires when the iframe editor is updated with content from the textarea.
25568 * @param {Roo.HtmlEditorCore} this
25569 * @param {String} html
25574 * @event editorevent
25575 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25576 * @param {Roo.HtmlEditorCore} this
25582 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25584 // defaults : white / black...
25585 this.applyBlacklists();
25592 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25596 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25602 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25607 * @cfg {Number} height (in pixels)
25611 * @cfg {Number} width (in pixels)
25616 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25619 stylesheets: false,
25622 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25624 allowComments: false,
25628 // private properties
25629 validationEvent : false,
25631 initialized : false,
25633 sourceEditMode : false,
25634 onFocus : Roo.emptyFn,
25636 hideMode:'offsets',
25640 // blacklist + whitelisted elements..
25647 * Protected method that will not generally be called directly. It
25648 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25649 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25651 getDocMarkup : function(){
25655 // inherit styels from page...??
25656 if (this.stylesheets === false) {
25658 Roo.get(document.head).select('style').each(function(node) {
25659 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25662 Roo.get(document.head).select('link').each(function(node) {
25663 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25666 } else if (!this.stylesheets.length) {
25668 st = '<style type="text/css">' +
25669 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25672 for (var i in this.stylesheets) {
25673 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25678 st += '<style type="text/css">' +
25679 'IMG { cursor: pointer } ' +
25682 var cls = 'roo-htmleditor-body';
25684 if(this.bodyCls.length){
25685 cls += ' ' + this.bodyCls;
25688 return '<html><head>' + st +
25689 //<style type="text/css">' +
25690 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25692 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25696 onRender : function(ct, position)
25699 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25700 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25703 this.el.dom.style.border = '0 none';
25704 this.el.dom.setAttribute('tabIndex', -1);
25705 this.el.addClass('x-hidden hide');
25709 if(Roo.isIE){ // fix IE 1px bogus margin
25710 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25714 this.frameId = Roo.id();
25718 var iframe = this.owner.wrap.createChild({
25720 cls: 'form-control', // bootstrap..
25722 name: this.frameId,
25723 frameBorder : 'no',
25724 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25729 this.iframe = iframe.dom;
25731 this.assignDocWin();
25733 this.doc.designMode = 'on';
25736 this.doc.write(this.getDocMarkup());
25740 var task = { // must defer to wait for browser to be ready
25742 //console.log("run task?" + this.doc.readyState);
25743 this.assignDocWin();
25744 if(this.doc.body || this.doc.readyState == 'complete'){
25746 this.doc.designMode="on";
25750 Roo.TaskMgr.stop(task);
25751 this.initEditor.defer(10, this);
25758 Roo.TaskMgr.start(task);
25763 onResize : function(w, h)
25765 Roo.log('resize: ' +w + ',' + h );
25766 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25770 if(typeof w == 'number'){
25772 this.iframe.style.width = w + 'px';
25774 if(typeof h == 'number'){
25776 this.iframe.style.height = h + 'px';
25778 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25785 * Toggles the editor between standard and source edit mode.
25786 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25788 toggleSourceEdit : function(sourceEditMode){
25790 this.sourceEditMode = sourceEditMode === true;
25792 if(this.sourceEditMode){
25794 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25797 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25798 //this.iframe.className = '';
25801 //this.setSize(this.owner.wrap.getSize());
25802 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25809 * Protected method that will not generally be called directly. If you need/want
25810 * custom HTML cleanup, this is the method you should override.
25811 * @param {String} html The HTML to be cleaned
25812 * return {String} The cleaned HTML
25814 cleanHtml : function(html){
25815 html = String(html);
25816 if(html.length > 5){
25817 if(Roo.isSafari){ // strip safari nonsense
25818 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25821 if(html == ' '){
25828 * HTML Editor -> Textarea
25829 * Protected method that will not generally be called directly. Syncs the contents
25830 * of the editor iframe with the textarea.
25832 syncValue : function(){
25833 if(this.initialized){
25834 var bd = (this.doc.body || this.doc.documentElement);
25835 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25836 var html = bd.innerHTML;
25838 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25839 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25841 html = '<div style="'+m[0]+'">' + html + '</div>';
25844 html = this.cleanHtml(html);
25845 // fix up the special chars.. normaly like back quotes in word...
25846 // however we do not want to do this with chinese..
25847 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25849 var cc = match.charCodeAt();
25851 // Get the character value, handling surrogate pairs
25852 if (match.length == 2) {
25853 // It's a surrogate pair, calculate the Unicode code point
25854 var high = match.charCodeAt(0) - 0xD800;
25855 var low = match.charCodeAt(1) - 0xDC00;
25856 cc = (high * 0x400) + low + 0x10000;
25858 (cc >= 0x4E00 && cc < 0xA000 ) ||
25859 (cc >= 0x3400 && cc < 0x4E00 ) ||
25860 (cc >= 0xf900 && cc < 0xfb00 )
25865 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25866 return "&#" + cc + ";";
25873 if(this.owner.fireEvent('beforesync', this, html) !== false){
25874 this.el.dom.value = html;
25875 this.owner.fireEvent('sync', this, html);
25881 * Protected method that will not generally be called directly. Pushes the value of the textarea
25882 * into the iframe editor.
25884 pushValue : function(){
25885 if(this.initialized){
25886 var v = this.el.dom.value.trim();
25888 // if(v.length < 1){
25892 if(this.owner.fireEvent('beforepush', this, v) !== false){
25893 var d = (this.doc.body || this.doc.documentElement);
25895 this.cleanUpPaste();
25896 this.el.dom.value = d.innerHTML;
25897 this.owner.fireEvent('push', this, v);
25903 deferFocus : function(){
25904 this.focus.defer(10, this);
25908 focus : function(){
25909 if(this.win && !this.sourceEditMode){
25916 assignDocWin: function()
25918 var iframe = this.iframe;
25921 this.doc = iframe.contentWindow.document;
25922 this.win = iframe.contentWindow;
25924 // if (!Roo.get(this.frameId)) {
25927 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25928 // this.win = Roo.get(this.frameId).dom.contentWindow;
25930 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25934 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25935 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25940 initEditor : function(){
25941 //console.log("INIT EDITOR");
25942 this.assignDocWin();
25946 this.doc.designMode="on";
25948 this.doc.write(this.getDocMarkup());
25951 var dbody = (this.doc.body || this.doc.documentElement);
25952 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25953 // this copies styles from the containing element into thsi one..
25954 // not sure why we need all of this..
25955 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25957 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25958 //ss['background-attachment'] = 'fixed'; // w3c
25959 dbody.bgProperties = 'fixed'; // ie
25960 //Roo.DomHelper.applyStyles(dbody, ss);
25961 Roo.EventManager.on(this.doc, {
25962 //'mousedown': this.onEditorEvent,
25963 'mouseup': this.onEditorEvent,
25964 'dblclick': this.onEditorEvent,
25965 'click': this.onEditorEvent,
25966 'keyup': this.onEditorEvent,
25971 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25973 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25974 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25976 this.initialized = true;
25978 this.owner.fireEvent('initialize', this);
25983 onDestroy : function(){
25989 //for (var i =0; i < this.toolbars.length;i++) {
25990 // // fixme - ask toolbars for heights?
25991 // this.toolbars[i].onDestroy();
25994 //this.wrap.dom.innerHTML = '';
25995 //this.wrap.remove();
26000 onFirstFocus : function(){
26002 this.assignDocWin();
26005 this.activated = true;
26008 if(Roo.isGecko){ // prevent silly gecko errors
26010 var s = this.win.getSelection();
26011 if(!s.focusNode || s.focusNode.nodeType != 3){
26012 var r = s.getRangeAt(0);
26013 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26018 this.execCmd('useCSS', true);
26019 this.execCmd('styleWithCSS', false);
26022 this.owner.fireEvent('activate', this);
26026 adjustFont: function(btn){
26027 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26028 //if(Roo.isSafari){ // safari
26031 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26032 if(Roo.isSafari){ // safari
26033 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26034 v = (v < 10) ? 10 : v;
26035 v = (v > 48) ? 48 : v;
26036 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26041 v = Math.max(1, v+adjust);
26043 this.execCmd('FontSize', v );
26046 onEditorEvent : function(e)
26048 this.owner.fireEvent('editorevent', this, e);
26049 // this.updateToolbar();
26050 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26053 insertTag : function(tg)
26055 // could be a bit smarter... -> wrap the current selected tRoo..
26056 if (tg.toLowerCase() == 'span' ||
26057 tg.toLowerCase() == 'code' ||
26058 tg.toLowerCase() == 'sup' ||
26059 tg.toLowerCase() == 'sub'
26062 range = this.createRange(this.getSelection());
26063 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26064 wrappingNode.appendChild(range.extractContents());
26065 range.insertNode(wrappingNode);
26072 this.execCmd("formatblock", tg);
26076 insertText : function(txt)
26080 var range = this.createRange();
26081 range.deleteContents();
26082 //alert(Sender.getAttribute('label'));
26084 range.insertNode(this.doc.createTextNode(txt));
26090 * Executes a Midas editor command on the editor document and performs necessary focus and
26091 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26092 * @param {String} cmd The Midas command
26093 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26095 relayCmd : function(cmd, value){
26097 this.execCmd(cmd, value);
26098 this.owner.fireEvent('editorevent', this);
26099 //this.updateToolbar();
26100 this.owner.deferFocus();
26104 * Executes a Midas editor command directly on the editor document.
26105 * For visual commands, you should use {@link #relayCmd} instead.
26106 * <b>This should only be called after the editor is initialized.</b>
26107 * @param {String} cmd The Midas command
26108 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26110 execCmd : function(cmd, value){
26111 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26118 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26120 * @param {String} text | dom node..
26122 insertAtCursor : function(text)
26125 if(!this.activated){
26131 var r = this.doc.selection.createRange();
26142 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26146 // from jquery ui (MIT licenced)
26148 var win = this.win;
26150 if (win.getSelection && win.getSelection().getRangeAt) {
26151 range = win.getSelection().getRangeAt(0);
26152 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26153 range.insertNode(node);
26154 } else if (win.document.selection && win.document.selection.createRange) {
26155 // no firefox support
26156 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26157 win.document.selection.createRange().pasteHTML(txt);
26159 // no firefox support
26160 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26161 this.execCmd('InsertHTML', txt);
26170 mozKeyPress : function(e){
26172 var c = e.getCharCode(), cmd;
26175 c = String.fromCharCode(c).toLowerCase();
26189 this.cleanUpPaste.defer(100, this);
26197 e.preventDefault();
26205 fixKeys : function(){ // load time branching for fastest keydown performance
26207 return function(e){
26208 var k = e.getKey(), r;
26211 r = this.doc.selection.createRange();
26214 r.pasteHTML('    ');
26221 r = this.doc.selection.createRange();
26223 var target = r.parentElement();
26224 if(!target || target.tagName.toLowerCase() != 'li'){
26226 r.pasteHTML('<br />');
26232 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26233 this.cleanUpPaste.defer(100, this);
26239 }else if(Roo.isOpera){
26240 return function(e){
26241 var k = e.getKey();
26245 this.execCmd('InsertHTML','    ');
26248 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26249 this.cleanUpPaste.defer(100, this);
26254 }else if(Roo.isSafari){
26255 return function(e){
26256 var k = e.getKey();
26260 this.execCmd('InsertText','\t');
26264 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26265 this.cleanUpPaste.defer(100, this);
26273 getAllAncestors: function()
26275 var p = this.getSelectedNode();
26278 a.push(p); // push blank onto stack..
26279 p = this.getParentElement();
26283 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26287 a.push(this.doc.body);
26291 lastSelNode : false,
26294 getSelection : function()
26296 this.assignDocWin();
26297 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26300 getSelectedNode: function()
26302 // this may only work on Gecko!!!
26304 // should we cache this!!!!
26309 var range = this.createRange(this.getSelection()).cloneRange();
26312 var parent = range.parentElement();
26314 var testRange = range.duplicate();
26315 testRange.moveToElementText(parent);
26316 if (testRange.inRange(range)) {
26319 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26322 parent = parent.parentElement;
26327 // is ancestor a text element.
26328 var ac = range.commonAncestorContainer;
26329 if (ac.nodeType == 3) {
26330 ac = ac.parentNode;
26333 var ar = ac.childNodes;
26336 var other_nodes = [];
26337 var has_other_nodes = false;
26338 for (var i=0;i<ar.length;i++) {
26339 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26342 // fullly contained node.
26344 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26349 // probably selected..
26350 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26351 other_nodes.push(ar[i]);
26355 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26360 has_other_nodes = true;
26362 if (!nodes.length && other_nodes.length) {
26363 nodes= other_nodes;
26365 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26371 createRange: function(sel)
26373 // this has strange effects when using with
26374 // top toolbar - not sure if it's a great idea.
26375 //this.editor.contentWindow.focus();
26376 if (typeof sel != "undefined") {
26378 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26380 return this.doc.createRange();
26383 return this.doc.createRange();
26386 getParentElement: function()
26389 this.assignDocWin();
26390 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26392 var range = this.createRange(sel);
26395 var p = range.commonAncestorContainer;
26396 while (p.nodeType == 3) { // text node
26407 * Range intersection.. the hard stuff...
26411 * [ -- selected range --- ]
26415 * if end is before start or hits it. fail.
26416 * if start is after end or hits it fail.
26418 * if either hits (but other is outside. - then it's not
26424 // @see http://www.thismuchiknow.co.uk/?p=64.
26425 rangeIntersectsNode : function(range, node)
26427 var nodeRange = node.ownerDocument.createRange();
26429 nodeRange.selectNode(node);
26431 nodeRange.selectNodeContents(node);
26434 var rangeStartRange = range.cloneRange();
26435 rangeStartRange.collapse(true);
26437 var rangeEndRange = range.cloneRange();
26438 rangeEndRange.collapse(false);
26440 var nodeStartRange = nodeRange.cloneRange();
26441 nodeStartRange.collapse(true);
26443 var nodeEndRange = nodeRange.cloneRange();
26444 nodeEndRange.collapse(false);
26446 return rangeStartRange.compareBoundaryPoints(
26447 Range.START_TO_START, nodeEndRange) == -1 &&
26448 rangeEndRange.compareBoundaryPoints(
26449 Range.START_TO_START, nodeStartRange) == 1;
26453 rangeCompareNode : function(range, node)
26455 var nodeRange = node.ownerDocument.createRange();
26457 nodeRange.selectNode(node);
26459 nodeRange.selectNodeContents(node);
26463 range.collapse(true);
26465 nodeRange.collapse(true);
26467 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26468 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26470 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26472 var nodeIsBefore = ss == 1;
26473 var nodeIsAfter = ee == -1;
26475 if (nodeIsBefore && nodeIsAfter) {
26478 if (!nodeIsBefore && nodeIsAfter) {
26479 return 1; //right trailed.
26482 if (nodeIsBefore && !nodeIsAfter) {
26483 return 2; // left trailed.
26489 // private? - in a new class?
26490 cleanUpPaste : function()
26492 // cleans up the whole document..
26493 Roo.log('cleanuppaste');
26495 this.cleanUpChildren(this.doc.body);
26496 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26497 if (clean != this.doc.body.innerHTML) {
26498 this.doc.body.innerHTML = clean;
26503 cleanWordChars : function(input) {// change the chars to hex code
26504 var he = Roo.HtmlEditorCore;
26506 var output = input;
26507 Roo.each(he.swapCodes, function(sw) {
26508 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26510 output = output.replace(swapper, sw[1]);
26517 cleanUpChildren : function (n)
26519 if (!n.childNodes.length) {
26522 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26523 this.cleanUpChild(n.childNodes[i]);
26530 cleanUpChild : function (node)
26533 //console.log(node);
26534 if (node.nodeName == "#text") {
26535 // clean up silly Windows -- stuff?
26538 if (node.nodeName == "#comment") {
26539 if (!this.allowComments) {
26540 node.parentNode.removeChild(node);
26542 // clean up silly Windows -- stuff?
26545 var lcname = node.tagName.toLowerCase();
26546 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26547 // whitelist of tags..
26549 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26551 node.parentNode.removeChild(node);
26556 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26558 // spans with no attributes - just remove them..
26559 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26560 remove_keep_children = true;
26563 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26564 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26566 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26567 // remove_keep_children = true;
26570 if (remove_keep_children) {
26571 this.cleanUpChildren(node);
26572 // inserts everything just before this node...
26573 while (node.childNodes.length) {
26574 var cn = node.childNodes[0];
26575 node.removeChild(cn);
26576 node.parentNode.insertBefore(cn, node);
26578 node.parentNode.removeChild(node);
26582 if (!node.attributes || !node.attributes.length) {
26587 this.cleanUpChildren(node);
26591 function cleanAttr(n,v)
26594 if (v.match(/^\./) || v.match(/^\//)) {
26597 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26600 if (v.match(/^#/)) {
26603 if (v.match(/^\{/)) { // allow template editing.
26606 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26607 node.removeAttribute(n);
26611 var cwhite = this.cwhite;
26612 var cblack = this.cblack;
26614 function cleanStyle(n,v)
26616 if (v.match(/expression/)) { //XSS?? should we even bother..
26617 node.removeAttribute(n);
26621 var parts = v.split(/;/);
26624 Roo.each(parts, function(p) {
26625 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26629 var l = p.split(':').shift().replace(/\s+/g,'');
26630 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26632 if ( cwhite.length && cblack.indexOf(l) > -1) {
26633 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26634 //node.removeAttribute(n);
26638 // only allow 'c whitelisted system attributes'
26639 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26640 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26641 //node.removeAttribute(n);
26651 if (clean.length) {
26652 node.setAttribute(n, clean.join(';'));
26654 node.removeAttribute(n);
26660 for (var i = node.attributes.length-1; i > -1 ; i--) {
26661 var a = node.attributes[i];
26664 if (a.name.toLowerCase().substr(0,2)=='on') {
26665 node.removeAttribute(a.name);
26668 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26669 node.removeAttribute(a.name);
26672 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26673 cleanAttr(a.name,a.value); // fixme..
26676 if (a.name == 'style') {
26677 cleanStyle(a.name,a.value);
26680 /// clean up MS crap..
26681 // tecnically this should be a list of valid class'es..
26684 if (a.name == 'class') {
26685 if (a.value.match(/^Mso/)) {
26686 node.removeAttribute('class');
26689 if (a.value.match(/^body$/)) {
26690 node.removeAttribute('class');
26701 this.cleanUpChildren(node);
26707 * Clean up MS wordisms...
26709 cleanWord : function(node)
26712 this.cleanWord(this.doc.body);
26717 node.nodeName == 'SPAN' &&
26718 !node.hasAttributes() &&
26719 node.childNodes.length == 1 &&
26720 node.firstChild.nodeName == "#text"
26722 var textNode = node.firstChild;
26723 node.removeChild(textNode);
26724 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26725 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26727 node.parentNode.insertBefore(textNode, node);
26728 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26729 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26731 node.parentNode.removeChild(node);
26734 if (node.nodeName == "#text") {
26735 // clean up silly Windows -- stuff?
26738 if (node.nodeName == "#comment") {
26739 node.parentNode.removeChild(node);
26740 // clean up silly Windows -- stuff?
26744 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26745 node.parentNode.removeChild(node);
26748 //Roo.log(node.tagName);
26749 // remove - but keep children..
26750 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26751 //Roo.log('-- removed');
26752 while (node.childNodes.length) {
26753 var cn = node.childNodes[0];
26754 node.removeChild(cn);
26755 node.parentNode.insertBefore(cn, node);
26756 // move node to parent - and clean it..
26757 this.cleanWord(cn);
26759 node.parentNode.removeChild(node);
26760 /// no need to iterate chidlren = it's got none..
26761 //this.iterateChildren(node, this.cleanWord);
26765 if (node.className.length) {
26767 var cn = node.className.split(/\W+/);
26769 Roo.each(cn, function(cls) {
26770 if (cls.match(/Mso[a-zA-Z]+/)) {
26775 node.className = cna.length ? cna.join(' ') : '';
26777 node.removeAttribute("class");
26781 if (node.hasAttribute("lang")) {
26782 node.removeAttribute("lang");
26785 if (node.hasAttribute("style")) {
26787 var styles = node.getAttribute("style").split(";");
26789 Roo.each(styles, function(s) {
26790 if (!s.match(/:/)) {
26793 var kv = s.split(":");
26794 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26797 // what ever is left... we allow.
26800 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26801 if (!nstyle.length) {
26802 node.removeAttribute('style');
26805 this.iterateChildren(node, this.cleanWord);
26811 * iterateChildren of a Node, calling fn each time, using this as the scole..
26812 * @param {DomNode} node node to iterate children of.
26813 * @param {Function} fn method of this class to call on each item.
26815 iterateChildren : function(node, fn)
26817 if (!node.childNodes.length) {
26820 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26821 fn.call(this, node.childNodes[i])
26827 * cleanTableWidths.
26829 * Quite often pasting from word etc.. results in tables with column and widths.
26830 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26833 cleanTableWidths : function(node)
26838 this.cleanTableWidths(this.doc.body);
26843 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26846 Roo.log(node.tagName);
26847 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26848 this.iterateChildren(node, this.cleanTableWidths);
26851 if (node.hasAttribute('width')) {
26852 node.removeAttribute('width');
26856 if (node.hasAttribute("style")) {
26859 var styles = node.getAttribute("style").split(";");
26861 Roo.each(styles, function(s) {
26862 if (!s.match(/:/)) {
26865 var kv = s.split(":");
26866 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26869 // what ever is left... we allow.
26872 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26873 if (!nstyle.length) {
26874 node.removeAttribute('style');
26878 this.iterateChildren(node, this.cleanTableWidths);
26886 domToHTML : function(currentElement, depth, nopadtext) {
26888 depth = depth || 0;
26889 nopadtext = nopadtext || false;
26891 if (!currentElement) {
26892 return this.domToHTML(this.doc.body);
26895 //Roo.log(currentElement);
26897 var allText = false;
26898 var nodeName = currentElement.nodeName;
26899 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26901 if (nodeName == '#text') {
26903 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26908 if (nodeName != 'BODY') {
26911 // Prints the node tagName, such as <A>, <IMG>, etc
26914 for(i = 0; i < currentElement.attributes.length;i++) {
26916 var aname = currentElement.attributes.item(i).name;
26917 if (!currentElement.attributes.item(i).value.length) {
26920 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26923 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26932 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26935 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26940 // Traverse the tree
26942 var currentElementChild = currentElement.childNodes.item(i);
26943 var allText = true;
26944 var innerHTML = '';
26946 while (currentElementChild) {
26947 // Formatting code (indent the tree so it looks nice on the screen)
26948 var nopad = nopadtext;
26949 if (lastnode == 'SPAN') {
26953 if (currentElementChild.nodeName == '#text') {
26954 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26955 toadd = nopadtext ? toadd : toadd.trim();
26956 if (!nopad && toadd.length > 80) {
26957 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26959 innerHTML += toadd;
26962 currentElementChild = currentElement.childNodes.item(i);
26968 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26970 // Recursively traverse the tree structure of the child node
26971 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26972 lastnode = currentElementChild.nodeName;
26974 currentElementChild=currentElement.childNodes.item(i);
26980 // The remaining code is mostly for formatting the tree
26981 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26986 ret+= "</"+tagName+">";
26992 applyBlacklists : function()
26994 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26995 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26999 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27000 if (b.indexOf(tag) > -1) {
27003 this.white.push(tag);
27007 Roo.each(w, function(tag) {
27008 if (b.indexOf(tag) > -1) {
27011 if (this.white.indexOf(tag) > -1) {
27014 this.white.push(tag);
27019 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27020 if (w.indexOf(tag) > -1) {
27023 this.black.push(tag);
27027 Roo.each(b, function(tag) {
27028 if (w.indexOf(tag) > -1) {
27031 if (this.black.indexOf(tag) > -1) {
27034 this.black.push(tag);
27039 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27040 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27044 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27045 if (b.indexOf(tag) > -1) {
27048 this.cwhite.push(tag);
27052 Roo.each(w, function(tag) {
27053 if (b.indexOf(tag) > -1) {
27056 if (this.cwhite.indexOf(tag) > -1) {
27059 this.cwhite.push(tag);
27064 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27065 if (w.indexOf(tag) > -1) {
27068 this.cblack.push(tag);
27072 Roo.each(b, function(tag) {
27073 if (w.indexOf(tag) > -1) {
27076 if (this.cblack.indexOf(tag) > -1) {
27079 this.cblack.push(tag);
27084 setStylesheets : function(stylesheets)
27086 if(typeof(stylesheets) == 'string'){
27087 Roo.get(this.iframe.contentDocument.head).createChild({
27089 rel : 'stylesheet',
27098 Roo.each(stylesheets, function(s) {
27103 Roo.get(_this.iframe.contentDocument.head).createChild({
27105 rel : 'stylesheet',
27114 removeStylesheets : function()
27118 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27123 setStyle : function(style)
27125 Roo.get(this.iframe.contentDocument.head).createChild({
27134 // hide stuff that is not compatible
27148 * @event specialkey
27152 * @cfg {String} fieldClass @hide
27155 * @cfg {String} focusClass @hide
27158 * @cfg {String} autoCreate @hide
27161 * @cfg {String} inputType @hide
27164 * @cfg {String} invalidClass @hide
27167 * @cfg {String} invalidText @hide
27170 * @cfg {String} msgFx @hide
27173 * @cfg {String} validateOnBlur @hide
27177 Roo.HtmlEditorCore.white = [
27178 'area', 'br', 'img', 'input', 'hr', 'wbr',
27180 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27181 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27182 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27183 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27184 'table', 'ul', 'xmp',
27186 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27189 'dir', 'menu', 'ol', 'ul', 'dl',
27195 Roo.HtmlEditorCore.black = [
27196 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27198 'base', 'basefont', 'bgsound', 'blink', 'body',
27199 'frame', 'frameset', 'head', 'html', 'ilayer',
27200 'iframe', 'layer', 'link', 'meta', 'object',
27201 'script', 'style' ,'title', 'xml' // clean later..
27203 Roo.HtmlEditorCore.clean = [
27204 'script', 'style', 'title', 'xml'
27206 Roo.HtmlEditorCore.remove = [
27211 Roo.HtmlEditorCore.ablack = [
27215 Roo.HtmlEditorCore.aclean = [
27216 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27220 Roo.HtmlEditorCore.pwhite= [
27221 'http', 'https', 'mailto'
27224 // white listed style attributes.
27225 Roo.HtmlEditorCore.cwhite= [
27226 // 'text-align', /// default is to allow most things..
27232 // black listed style attributes.
27233 Roo.HtmlEditorCore.cblack= [
27234 // 'font-size' -- this can be set by the project
27238 Roo.HtmlEditorCore.swapCodes =[
27239 [ 8211, "–" ],
27240 [ 8212, "—" ],
27257 * @class Roo.bootstrap.HtmlEditor
27258 * @extends Roo.bootstrap.TextArea
27259 * Bootstrap HtmlEditor class
27262 * Create a new HtmlEditor
27263 * @param {Object} config The config object
27266 Roo.bootstrap.HtmlEditor = function(config){
27267 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27268 if (!this.toolbars) {
27269 this.toolbars = [];
27272 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27275 * @event initialize
27276 * Fires when the editor is fully initialized (including the iframe)
27277 * @param {HtmlEditor} this
27282 * Fires when the editor is first receives the focus. Any insertion must wait
27283 * until after this event.
27284 * @param {HtmlEditor} this
27288 * @event beforesync
27289 * Fires before the textarea is updated with content from the editor iframe. Return false
27290 * to cancel the sync.
27291 * @param {HtmlEditor} this
27292 * @param {String} html
27296 * @event beforepush
27297 * Fires before the iframe editor is updated with content from the textarea. Return false
27298 * to cancel the push.
27299 * @param {HtmlEditor} this
27300 * @param {String} html
27305 * Fires when the textarea is updated with content from the editor iframe.
27306 * @param {HtmlEditor} this
27307 * @param {String} html
27312 * Fires when the iframe editor is updated with content from the textarea.
27313 * @param {HtmlEditor} this
27314 * @param {String} html
27318 * @event editmodechange
27319 * Fires when the editor switches edit modes
27320 * @param {HtmlEditor} this
27321 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27323 editmodechange: true,
27325 * @event editorevent
27326 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27327 * @param {HtmlEditor} this
27331 * @event firstfocus
27332 * Fires when on first focus - needed by toolbars..
27333 * @param {HtmlEditor} this
27338 * Auto save the htmlEditor value as a file into Events
27339 * @param {HtmlEditor} this
27343 * @event savedpreview
27344 * preview the saved version of htmlEditor
27345 * @param {HtmlEditor} this
27352 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27356 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27361 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27366 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27371 * @cfg {Number} height (in pixels)
27375 * @cfg {Number} width (in pixels)
27380 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27383 stylesheets: false,
27388 // private properties
27389 validationEvent : false,
27391 initialized : false,
27394 onFocus : Roo.emptyFn,
27396 hideMode:'offsets',
27398 tbContainer : false,
27402 toolbarContainer :function() {
27403 return this.wrap.select('.x-html-editor-tb',true).first();
27407 * Protected method that will not generally be called directly. It
27408 * is called when the editor creates its toolbar. Override this method if you need to
27409 * add custom toolbar buttons.
27410 * @param {HtmlEditor} editor
27412 createToolbar : function(){
27413 Roo.log('renewing');
27414 Roo.log("create toolbars");
27416 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27417 this.toolbars[0].render(this.toolbarContainer());
27421 // if (!editor.toolbars || !editor.toolbars.length) {
27422 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27425 // for (var i =0 ; i < editor.toolbars.length;i++) {
27426 // editor.toolbars[i] = Roo.factory(
27427 // typeof(editor.toolbars[i]) == 'string' ?
27428 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27429 // Roo.bootstrap.HtmlEditor);
27430 // editor.toolbars[i].init(editor);
27436 onRender : function(ct, position)
27438 // Roo.log("Call onRender: " + this.xtype);
27440 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27442 this.wrap = this.inputEl().wrap({
27443 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27446 this.editorcore.onRender(ct, position);
27448 if (this.resizable) {
27449 this.resizeEl = new Roo.Resizable(this.wrap, {
27453 minHeight : this.height,
27454 height: this.height,
27455 handles : this.resizable,
27458 resize : function(r, w, h) {
27459 _t.onResize(w,h); // -something
27465 this.createToolbar(this);
27468 if(!this.width && this.resizable){
27469 this.setSize(this.wrap.getSize());
27471 if (this.resizeEl) {
27472 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27473 // should trigger onReize..
27479 onResize : function(w, h)
27481 Roo.log('resize: ' +w + ',' + h );
27482 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27486 if(this.inputEl() ){
27487 if(typeof w == 'number'){
27488 var aw = w - this.wrap.getFrameWidth('lr');
27489 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27492 if(typeof h == 'number'){
27493 var tbh = -11; // fixme it needs to tool bar size!
27494 for (var i =0; i < this.toolbars.length;i++) {
27495 // fixme - ask toolbars for heights?
27496 tbh += this.toolbars[i].el.getHeight();
27497 //if (this.toolbars[i].footer) {
27498 // tbh += this.toolbars[i].footer.el.getHeight();
27506 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27507 ah -= 5; // knock a few pixes off for look..
27508 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27512 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27513 this.editorcore.onResize(ew,eh);
27518 * Toggles the editor between standard and source edit mode.
27519 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27521 toggleSourceEdit : function(sourceEditMode)
27523 this.editorcore.toggleSourceEdit(sourceEditMode);
27525 if(this.editorcore.sourceEditMode){
27526 Roo.log('editor - showing textarea');
27529 // Roo.log(this.syncValue());
27531 this.inputEl().removeClass(['hide', 'x-hidden']);
27532 this.inputEl().dom.removeAttribute('tabIndex');
27533 this.inputEl().focus();
27535 Roo.log('editor - hiding textarea');
27537 // Roo.log(this.pushValue());
27540 this.inputEl().addClass(['hide', 'x-hidden']);
27541 this.inputEl().dom.setAttribute('tabIndex', -1);
27542 //this.deferFocus();
27545 if(this.resizable){
27546 this.setSize(this.wrap.getSize());
27549 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27552 // private (for BoxComponent)
27553 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27555 // private (for BoxComponent)
27556 getResizeEl : function(){
27560 // private (for BoxComponent)
27561 getPositionEl : function(){
27566 initEvents : function(){
27567 this.originalValue = this.getValue();
27571 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27574 // markInvalid : Roo.emptyFn,
27576 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27579 // clearInvalid : Roo.emptyFn,
27581 setValue : function(v){
27582 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27583 this.editorcore.pushValue();
27588 deferFocus : function(){
27589 this.focus.defer(10, this);
27593 focus : function(){
27594 this.editorcore.focus();
27600 onDestroy : function(){
27606 for (var i =0; i < this.toolbars.length;i++) {
27607 // fixme - ask toolbars for heights?
27608 this.toolbars[i].onDestroy();
27611 this.wrap.dom.innerHTML = '';
27612 this.wrap.remove();
27617 onFirstFocus : function(){
27618 //Roo.log("onFirstFocus");
27619 this.editorcore.onFirstFocus();
27620 for (var i =0; i < this.toolbars.length;i++) {
27621 this.toolbars[i].onFirstFocus();
27627 syncValue : function()
27629 this.editorcore.syncValue();
27632 pushValue : function()
27634 this.editorcore.pushValue();
27638 // hide stuff that is not compatible
27652 * @event specialkey
27656 * @cfg {String} fieldClass @hide
27659 * @cfg {String} focusClass @hide
27662 * @cfg {String} autoCreate @hide
27665 * @cfg {String} inputType @hide
27669 * @cfg {String} invalidText @hide
27672 * @cfg {String} msgFx @hide
27675 * @cfg {String} validateOnBlur @hide
27684 Roo.namespace('Roo.bootstrap.htmleditor');
27686 * @class Roo.bootstrap.HtmlEditorToolbar1
27692 new Roo.bootstrap.HtmlEditor({
27695 new Roo.bootstrap.HtmlEditorToolbar1({
27696 disable : { fonts: 1 , format: 1, ..., ... , ...],
27702 * @cfg {Object} disable List of elements to disable..
27703 * @cfg {Array} btns List of additional buttons.
27707 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27710 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27713 Roo.apply(this, config);
27715 // default disabled, based on 'good practice'..
27716 this.disable = this.disable || {};
27717 Roo.applyIf(this.disable, {
27720 specialElements : true
27722 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27724 this.editor = config.editor;
27725 this.editorcore = config.editor.editorcore;
27727 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27729 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27730 // dont call parent... till later.
27732 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27737 editorcore : false,
27742 "h1","h2","h3","h4","h5","h6",
27744 "abbr", "acronym", "address", "cite", "samp", "var",
27748 onRender : function(ct, position)
27750 // Roo.log("Call onRender: " + this.xtype);
27752 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27754 this.el.dom.style.marginBottom = '0';
27756 var editorcore = this.editorcore;
27757 var editor= this.editor;
27760 var btn = function(id,cmd , toggle, handler, html){
27762 var event = toggle ? 'toggle' : 'click';
27767 xns: Roo.bootstrap,
27771 enableToggle:toggle !== false,
27773 pressed : toggle ? false : null,
27776 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27777 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27783 // var cb_box = function...
27788 xns: Roo.bootstrap,
27793 xns: Roo.bootstrap,
27797 Roo.each(this.formats, function(f) {
27798 style.menu.items.push({
27800 xns: Roo.bootstrap,
27801 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27806 editorcore.insertTag(this.tagname);
27813 children.push(style);
27815 btn('bold',false,true);
27816 btn('italic',false,true);
27817 btn('align-left', 'justifyleft',true);
27818 btn('align-center', 'justifycenter',true);
27819 btn('align-right' , 'justifyright',true);
27820 btn('link', false, false, function(btn) {
27821 //Roo.log("create link?");
27822 var url = prompt(this.createLinkText, this.defaultLinkValue);
27823 if(url && url != 'http:/'+'/'){
27824 this.editorcore.relayCmd('createlink', url);
27827 btn('list','insertunorderedlist',true);
27828 btn('pencil', false,true, function(btn){
27830 this.toggleSourceEdit(btn.pressed);
27833 if (this.editor.btns.length > 0) {
27834 for (var i = 0; i<this.editor.btns.length; i++) {
27835 children.push(this.editor.btns[i]);
27843 xns: Roo.bootstrap,
27848 xns: Roo.bootstrap,
27853 cog.menu.items.push({
27855 xns: Roo.bootstrap,
27856 html : Clean styles,
27861 editorcore.insertTag(this.tagname);
27870 this.xtype = 'NavSimplebar';
27872 for(var i=0;i< children.length;i++) {
27874 this.buttons.add(this.addxtypeChild(children[i]));
27878 editor.on('editorevent', this.updateToolbar, this);
27880 onBtnClick : function(id)
27882 this.editorcore.relayCmd(id);
27883 this.editorcore.focus();
27887 * Protected method that will not generally be called directly. It triggers
27888 * a toolbar update by reading the markup state of the current selection in the editor.
27890 updateToolbar: function(){
27892 if(!this.editorcore.activated){
27893 this.editor.onFirstFocus(); // is this neeed?
27897 var btns = this.buttons;
27898 var doc = this.editorcore.doc;
27899 btns.get('bold').setActive(doc.queryCommandState('bold'));
27900 btns.get('italic').setActive(doc.queryCommandState('italic'));
27901 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27903 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27904 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27905 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27907 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27908 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27911 var ans = this.editorcore.getAllAncestors();
27912 if (this.formatCombo) {
27915 var store = this.formatCombo.store;
27916 this.formatCombo.setValue("");
27917 for (var i =0; i < ans.length;i++) {
27918 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27920 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27928 // hides menus... - so this cant be on a menu...
27929 Roo.bootstrap.MenuMgr.hideAll();
27931 Roo.bootstrap.MenuMgr.hideAll();
27932 //this.editorsyncValue();
27934 onFirstFocus: function() {
27935 this.buttons.each(function(item){
27939 toggleSourceEdit : function(sourceEditMode){
27942 if(sourceEditMode){
27943 Roo.log("disabling buttons");
27944 this.buttons.each( function(item){
27945 if(item.cmd != 'pencil'){
27951 Roo.log("enabling buttons");
27952 if(this.editorcore.initialized){
27953 this.buttons.each( function(item){
27959 Roo.log("calling toggole on editor");
27960 // tell the editor that it's been pressed..
27961 this.editor.toggleSourceEdit(sourceEditMode);
27975 * @class Roo.bootstrap.Markdown
27976 * @extends Roo.bootstrap.TextArea
27977 * Bootstrap Showdown editable area
27978 * @cfg {string} content
27981 * Create a new Showdown
27984 Roo.bootstrap.Markdown = function(config){
27985 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27989 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27993 initEvents : function()
27996 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27997 this.markdownEl = this.el.createChild({
27998 cls : 'roo-markdown-area'
28000 this.inputEl().addClass('d-none');
28001 if (this.getValue() == '') {
28002 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28005 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28007 this.markdownEl.on('click', this.toggleTextEdit, this);
28008 this.on('blur', this.toggleTextEdit, this);
28009 this.on('specialkey', this.resizeTextArea, this);
28012 toggleTextEdit : function()
28014 var sh = this.markdownEl.getHeight();
28015 this.inputEl().addClass('d-none');
28016 this.markdownEl.addClass('d-none');
28017 if (!this.editing) {
28019 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28020 this.inputEl().removeClass('d-none');
28021 this.inputEl().focus();
28022 this.editing = true;
28025 // show showdown...
28026 this.updateMarkdown();
28027 this.markdownEl.removeClass('d-none');
28028 this.editing = false;
28031 updateMarkdown : function()
28033 if (this.getValue() == '') {
28034 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28038 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28041 resizeTextArea: function () {
28044 Roo.log([sh, this.getValue().split("\n").length * 30]);
28045 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28047 setValue : function(val)
28049 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28050 if (!this.editing) {
28051 this.updateMarkdown();
28057 if (!this.editing) {
28058 this.toggleTextEdit();
28066 * Ext JS Library 1.1.1
28067 * Copyright(c) 2006-2007, Ext JS, LLC.
28069 * Originally Released Under LGPL - original licence link has changed is not relivant.
28072 * <script type="text/javascript">
28076 * @class Roo.bootstrap.PagingToolbar
28077 * @extends Roo.bootstrap.NavSimplebar
28078 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28080 * Create a new PagingToolbar
28081 * @param {Object} config The config object
28082 * @param {Roo.data.Store} store
28084 Roo.bootstrap.PagingToolbar = function(config)
28086 // old args format still supported... - xtype is prefered..
28087 // created from xtype...
28089 this.ds = config.dataSource;
28091 if (config.store && !this.ds) {
28092 this.store= Roo.factory(config.store, Roo.data);
28093 this.ds = this.store;
28094 this.ds.xmodule = this.xmodule || false;
28097 this.toolbarItems = [];
28098 if (config.items) {
28099 this.toolbarItems = config.items;
28102 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28107 this.bind(this.ds);
28110 if (Roo.bootstrap.version == 4) {
28111 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28113 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28118 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28120 * @cfg {Roo.data.Store} dataSource
28121 * The underlying data store providing the paged data
28124 * @cfg {String/HTMLElement/Element} container
28125 * container The id or element that will contain the toolbar
28128 * @cfg {Boolean} displayInfo
28129 * True to display the displayMsg (defaults to false)
28132 * @cfg {Number} pageSize
28133 * The number of records to display per page (defaults to 20)
28137 * @cfg {String} displayMsg
28138 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28140 displayMsg : 'Displaying {0} - {1} of {2}',
28142 * @cfg {String} emptyMsg
28143 * The message to display when no records are found (defaults to "No data to display")
28145 emptyMsg : 'No data to display',
28147 * Customizable piece of the default paging text (defaults to "Page")
28150 beforePageText : "Page",
28152 * Customizable piece of the default paging text (defaults to "of %0")
28155 afterPageText : "of {0}",
28157 * Customizable piece of the default paging text (defaults to "First Page")
28160 firstText : "First Page",
28162 * Customizable piece of the default paging text (defaults to "Previous Page")
28165 prevText : "Previous Page",
28167 * Customizable piece of the default paging text (defaults to "Next Page")
28170 nextText : "Next Page",
28172 * Customizable piece of the default paging text (defaults to "Last Page")
28175 lastText : "Last Page",
28177 * Customizable piece of the default paging text (defaults to "Refresh")
28180 refreshText : "Refresh",
28184 onRender : function(ct, position)
28186 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28187 this.navgroup.parentId = this.id;
28188 this.navgroup.onRender(this.el, null);
28189 // add the buttons to the navgroup
28191 if(this.displayInfo){
28192 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28193 this.displayEl = this.el.select('.x-paging-info', true).first();
28194 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28195 // this.displayEl = navel.el.select('span',true).first();
28201 Roo.each(_this.buttons, function(e){ // this might need to use render????
28202 Roo.factory(e).render(_this.el);
28206 Roo.each(_this.toolbarItems, function(e) {
28207 _this.navgroup.addItem(e);
28211 this.first = this.navgroup.addItem({
28212 tooltip: this.firstText,
28213 cls: "prev btn-outline-secondary",
28214 html : ' <i class="fa fa-step-backward"></i>',
28216 preventDefault: true,
28217 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28220 this.prev = this.navgroup.addItem({
28221 tooltip: this.prevText,
28222 cls: "prev btn-outline-secondary",
28223 html : ' <i class="fa fa-backward"></i>',
28225 preventDefault: true,
28226 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28228 //this.addSeparator();
28231 var field = this.navgroup.addItem( {
28233 cls : 'x-paging-position btn-outline-secondary',
28235 html : this.beforePageText +
28236 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28237 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28240 this.field = field.el.select('input', true).first();
28241 this.field.on("keydown", this.onPagingKeydown, this);
28242 this.field.on("focus", function(){this.dom.select();});
28245 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28246 //this.field.setHeight(18);
28247 //this.addSeparator();
28248 this.next = this.navgroup.addItem({
28249 tooltip: this.nextText,
28250 cls: "next btn-outline-secondary",
28251 html : ' <i class="fa fa-forward"></i>',
28253 preventDefault: true,
28254 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28256 this.last = this.navgroup.addItem({
28257 tooltip: this.lastText,
28258 html : ' <i class="fa fa-step-forward"></i>',
28259 cls: "next btn-outline-secondary",
28261 preventDefault: true,
28262 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28264 //this.addSeparator();
28265 this.loading = this.navgroup.addItem({
28266 tooltip: this.refreshText,
28267 cls: "btn-outline-secondary",
28268 html : ' <i class="fa fa-refresh"></i>',
28269 preventDefault: true,
28270 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28276 updateInfo : function(){
28277 if(this.displayEl){
28278 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28279 var msg = count == 0 ?
28283 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28285 this.displayEl.update(msg);
28290 onLoad : function(ds, r, o)
28292 this.cursor = o.params && o.params.start ? o.params.start : 0;
28294 var d = this.getPageData(),
28299 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28300 this.field.dom.value = ap;
28301 this.first.setDisabled(ap == 1);
28302 this.prev.setDisabled(ap == 1);
28303 this.next.setDisabled(ap == ps);
28304 this.last.setDisabled(ap == ps);
28305 this.loading.enable();
28310 getPageData : function(){
28311 var total = this.ds.getTotalCount();
28314 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28315 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28320 onLoadError : function(){
28321 this.loading.enable();
28325 onPagingKeydown : function(e){
28326 var k = e.getKey();
28327 var d = this.getPageData();
28329 var v = this.field.dom.value, pageNum;
28330 if(!v || isNaN(pageNum = parseInt(v, 10))){
28331 this.field.dom.value = d.activePage;
28334 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28335 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28338 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))
28340 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28341 this.field.dom.value = pageNum;
28342 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28345 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28347 var v = this.field.dom.value, pageNum;
28348 var increment = (e.shiftKey) ? 10 : 1;
28349 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28352 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28353 this.field.dom.value = d.activePage;
28356 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28358 this.field.dom.value = parseInt(v, 10) + increment;
28359 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28360 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28367 beforeLoad : function(){
28369 this.loading.disable();
28374 onClick : function(which){
28383 ds.load({params:{start: 0, limit: this.pageSize}});
28386 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28389 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28392 var total = ds.getTotalCount();
28393 var extra = total % this.pageSize;
28394 var lastStart = extra ? (total - extra) : total-this.pageSize;
28395 ds.load({params:{start: lastStart, limit: this.pageSize}});
28398 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28404 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28405 * @param {Roo.data.Store} store The data store to unbind
28407 unbind : function(ds){
28408 ds.un("beforeload", this.beforeLoad, this);
28409 ds.un("load", this.onLoad, this);
28410 ds.un("loadexception", this.onLoadError, this);
28411 ds.un("remove", this.updateInfo, this);
28412 ds.un("add", this.updateInfo, this);
28413 this.ds = undefined;
28417 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28418 * @param {Roo.data.Store} store The data store to bind
28420 bind : function(ds){
28421 ds.on("beforeload", this.beforeLoad, this);
28422 ds.on("load", this.onLoad, this);
28423 ds.on("loadexception", this.onLoadError, this);
28424 ds.on("remove", this.updateInfo, this);
28425 ds.on("add", this.updateInfo, this);
28436 * @class Roo.bootstrap.MessageBar
28437 * @extends Roo.bootstrap.Component
28438 * Bootstrap MessageBar class
28439 * @cfg {String} html contents of the MessageBar
28440 * @cfg {String} weight (info | success | warning | danger) default info
28441 * @cfg {String} beforeClass insert the bar before the given class
28442 * @cfg {Boolean} closable (true | false) default false
28443 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28446 * Create a new Element
28447 * @param {Object} config The config object
28450 Roo.bootstrap.MessageBar = function(config){
28451 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28454 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28460 beforeClass: 'bootstrap-sticky-wrap',
28462 getAutoCreate : function(){
28466 cls: 'alert alert-dismissable alert-' + this.weight,
28471 html: this.html || ''
28477 cfg.cls += ' alert-messages-fixed';
28491 onRender : function(ct, position)
28493 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28496 var cfg = Roo.apply({}, this.getAutoCreate());
28500 cfg.cls += ' ' + this.cls;
28503 cfg.style = this.style;
28505 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28507 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28510 this.el.select('>button.close').on('click', this.hide, this);
28516 if (!this.rendered) {
28522 this.fireEvent('show', this);
28528 if (!this.rendered) {
28534 this.fireEvent('hide', this);
28537 update : function()
28539 // var e = this.el.dom.firstChild;
28541 // if(this.closable){
28542 // e = e.nextSibling;
28545 // e.data = this.html || '';
28547 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28563 * @class Roo.bootstrap.Graph
28564 * @extends Roo.bootstrap.Component
28565 * Bootstrap Graph class
28569 @cfg {String} graphtype bar | vbar | pie
28570 @cfg {number} g_x coodinator | centre x (pie)
28571 @cfg {number} g_y coodinator | centre y (pie)
28572 @cfg {number} g_r radius (pie)
28573 @cfg {number} g_height height of the chart (respected by all elements in the set)
28574 @cfg {number} g_width width of the chart (respected by all elements in the set)
28575 @cfg {Object} title The title of the chart
28578 -opts (object) options for the chart
28580 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28581 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28583 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.
28584 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28586 o stretch (boolean)
28588 -opts (object) options for the pie
28591 o startAngle (number)
28592 o endAngle (number)
28596 * Create a new Input
28597 * @param {Object} config The config object
28600 Roo.bootstrap.Graph = function(config){
28601 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28607 * The img click event for the img.
28608 * @param {Roo.EventObject} e
28614 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28625 //g_colors: this.colors,
28632 getAutoCreate : function(){
28643 onRender : function(ct,position){
28646 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28648 if (typeof(Raphael) == 'undefined') {
28649 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28653 this.raphael = Raphael(this.el.dom);
28655 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28656 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28657 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28658 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28660 r.text(160, 10, "Single Series Chart").attr(txtattr);
28661 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28662 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28663 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28665 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28666 r.barchart(330, 10, 300, 220, data1);
28667 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28668 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28671 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28672 // r.barchart(30, 30, 560, 250, xdata, {
28673 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28674 // axis : "0 0 1 1",
28675 // axisxlabels : xdata
28676 // //yvalues : cols,
28679 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28681 // this.load(null,xdata,{
28682 // axis : "0 0 1 1",
28683 // axisxlabels : xdata
28688 load : function(graphtype,xdata,opts)
28690 this.raphael.clear();
28692 graphtype = this.graphtype;
28697 var r = this.raphael,
28698 fin = function () {
28699 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28701 fout = function () {
28702 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28704 pfin = function() {
28705 this.sector.stop();
28706 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28709 this.label[0].stop();
28710 this.label[0].attr({ r: 7.5 });
28711 this.label[1].attr({ "font-weight": 800 });
28714 pfout = function() {
28715 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28718 this.label[0].animate({ r: 5 }, 500, "bounce");
28719 this.label[1].attr({ "font-weight": 400 });
28725 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28728 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28731 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28732 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28734 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28741 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28746 setTitle: function(o)
28751 initEvents: function() {
28754 this.el.on('click', this.onClick, this);
28758 onClick : function(e)
28760 Roo.log('img onclick');
28761 this.fireEvent('click', this, e);
28773 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28776 * @class Roo.bootstrap.dash.NumberBox
28777 * @extends Roo.bootstrap.Component
28778 * Bootstrap NumberBox class
28779 * @cfg {String} headline Box headline
28780 * @cfg {String} content Box content
28781 * @cfg {String} icon Box icon
28782 * @cfg {String} footer Footer text
28783 * @cfg {String} fhref Footer href
28786 * Create a new NumberBox
28787 * @param {Object} config The config object
28791 Roo.bootstrap.dash.NumberBox = function(config){
28792 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28796 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28805 getAutoCreate : function(){
28809 cls : 'small-box ',
28817 cls : 'roo-headline',
28818 html : this.headline
28822 cls : 'roo-content',
28823 html : this.content
28837 cls : 'ion ' + this.icon
28846 cls : 'small-box-footer',
28847 href : this.fhref || '#',
28851 cfg.cn.push(footer);
28858 onRender : function(ct,position){
28859 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28866 setHeadline: function (value)
28868 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28871 setFooter: function (value, href)
28873 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28876 this.el.select('a.small-box-footer',true).first().attr('href', href);
28881 setContent: function (value)
28883 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28886 initEvents: function()
28900 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28903 * @class Roo.bootstrap.dash.TabBox
28904 * @extends Roo.bootstrap.Component
28905 * Bootstrap TabBox class
28906 * @cfg {String} title Title of the TabBox
28907 * @cfg {String} icon Icon of the TabBox
28908 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28909 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28912 * Create a new TabBox
28913 * @param {Object} config The config object
28917 Roo.bootstrap.dash.TabBox = function(config){
28918 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28923 * When a pane is added
28924 * @param {Roo.bootstrap.dash.TabPane} pane
28928 * @event activatepane
28929 * When a pane is activated
28930 * @param {Roo.bootstrap.dash.TabPane} pane
28932 "activatepane" : true
28940 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28945 tabScrollable : false,
28947 getChildContainer : function()
28949 return this.el.select('.tab-content', true).first();
28952 getAutoCreate : function(){
28956 cls: 'pull-left header',
28964 cls: 'fa ' + this.icon
28970 cls: 'nav nav-tabs pull-right',
28976 if(this.tabScrollable){
28983 cls: 'nav nav-tabs pull-right',
28994 cls: 'nav-tabs-custom',
28999 cls: 'tab-content no-padding',
29007 initEvents : function()
29009 //Roo.log('add add pane handler');
29010 this.on('addpane', this.onAddPane, this);
29013 * Updates the box title
29014 * @param {String} html to set the title to.
29016 setTitle : function(value)
29018 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29020 onAddPane : function(pane)
29022 this.panes.push(pane);
29023 //Roo.log('addpane');
29025 // tabs are rendere left to right..
29026 if(!this.showtabs){
29030 var ctr = this.el.select('.nav-tabs', true).first();
29033 var existing = ctr.select('.nav-tab',true);
29034 var qty = existing.getCount();;
29037 var tab = ctr.createChild({
29039 cls : 'nav-tab' + (qty ? '' : ' active'),
29047 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29050 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29052 pane.el.addClass('active');
29057 onTabClick : function(ev,un,ob,pane)
29059 //Roo.log('tab - prev default');
29060 ev.preventDefault();
29063 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29064 pane.tab.addClass('active');
29065 //Roo.log(pane.title);
29066 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29067 // technically we should have a deactivate event.. but maybe add later.
29068 // and it should not de-activate the selected tab...
29069 this.fireEvent('activatepane', pane);
29070 pane.el.addClass('active');
29071 pane.fireEvent('activate');
29076 getActivePane : function()
29079 Roo.each(this.panes, function(p) {
29080 if(p.el.hasClass('active')){
29101 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29103 * @class Roo.bootstrap.TabPane
29104 * @extends Roo.bootstrap.Component
29105 * Bootstrap TabPane class
29106 * @cfg {Boolean} active (false | true) Default false
29107 * @cfg {String} title title of panel
29111 * Create a new TabPane
29112 * @param {Object} config The config object
29115 Roo.bootstrap.dash.TabPane = function(config){
29116 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29122 * When a pane is activated
29123 * @param {Roo.bootstrap.dash.TabPane} pane
29130 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29135 // the tabBox that this is attached to.
29138 getAutoCreate : function()
29146 cfg.cls += ' active';
29151 initEvents : function()
29153 //Roo.log('trigger add pane handler');
29154 this.parent().fireEvent('addpane', this)
29158 * Updates the tab title
29159 * @param {String} html to set the title to.
29161 setTitle: function(str)
29167 this.tab.select('a', true).first().dom.innerHTML = str;
29184 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29187 * @class Roo.bootstrap.menu.Menu
29188 * @extends Roo.bootstrap.Component
29189 * Bootstrap Menu class - container for Menu
29190 * @cfg {String} html Text of the menu
29191 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29192 * @cfg {String} icon Font awesome icon
29193 * @cfg {String} pos Menu align to (top | bottom) default bottom
29197 * Create a new Menu
29198 * @param {Object} config The config object
29202 Roo.bootstrap.menu.Menu = function(config){
29203 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29207 * @event beforeshow
29208 * Fires before this menu is displayed
29209 * @param {Roo.bootstrap.menu.Menu} this
29213 * @event beforehide
29214 * Fires before this menu is hidden
29215 * @param {Roo.bootstrap.menu.Menu} this
29220 * Fires after this menu is displayed
29221 * @param {Roo.bootstrap.menu.Menu} this
29226 * Fires after this menu is hidden
29227 * @param {Roo.bootstrap.menu.Menu} this
29232 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29233 * @param {Roo.bootstrap.menu.Menu} this
29234 * @param {Roo.EventObject} e
29241 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29245 weight : 'default',
29250 getChildContainer : function() {
29251 if(this.isSubMenu){
29255 return this.el.select('ul.dropdown-menu', true).first();
29258 getAutoCreate : function()
29263 cls : 'roo-menu-text',
29271 cls : 'fa ' + this.icon
29282 cls : 'dropdown-button btn btn-' + this.weight,
29287 cls : 'dropdown-toggle btn btn-' + this.weight,
29297 cls : 'dropdown-menu'
29303 if(this.pos == 'top'){
29304 cfg.cls += ' dropup';
29307 if(this.isSubMenu){
29310 cls : 'dropdown-menu'
29317 onRender : function(ct, position)
29319 this.isSubMenu = ct.hasClass('dropdown-submenu');
29321 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29324 initEvents : function()
29326 if(this.isSubMenu){
29330 this.hidden = true;
29332 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29333 this.triggerEl.on('click', this.onTriggerPress, this);
29335 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29336 this.buttonEl.on('click', this.onClick, this);
29342 if(this.isSubMenu){
29346 return this.el.select('ul.dropdown-menu', true).first();
29349 onClick : function(e)
29351 this.fireEvent("click", this, e);
29354 onTriggerPress : function(e)
29356 if (this.isVisible()) {
29363 isVisible : function(){
29364 return !this.hidden;
29369 this.fireEvent("beforeshow", this);
29371 this.hidden = false;
29372 this.el.addClass('open');
29374 Roo.get(document).on("mouseup", this.onMouseUp, this);
29376 this.fireEvent("show", this);
29383 this.fireEvent("beforehide", this);
29385 this.hidden = true;
29386 this.el.removeClass('open');
29388 Roo.get(document).un("mouseup", this.onMouseUp);
29390 this.fireEvent("hide", this);
29393 onMouseUp : function()
29407 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29410 * @class Roo.bootstrap.menu.Item
29411 * @extends Roo.bootstrap.Component
29412 * Bootstrap MenuItem class
29413 * @cfg {Boolean} submenu (true | false) default false
29414 * @cfg {String} html text of the item
29415 * @cfg {String} href the link
29416 * @cfg {Boolean} disable (true | false) default false
29417 * @cfg {Boolean} preventDefault (true | false) default true
29418 * @cfg {String} icon Font awesome icon
29419 * @cfg {String} pos Submenu align to (left | right) default right
29423 * Create a new Item
29424 * @param {Object} config The config object
29428 Roo.bootstrap.menu.Item = function(config){
29429 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29433 * Fires when the mouse is hovering over this menu
29434 * @param {Roo.bootstrap.menu.Item} this
29435 * @param {Roo.EventObject} e
29440 * Fires when the mouse exits this menu
29441 * @param {Roo.bootstrap.menu.Item} this
29442 * @param {Roo.EventObject} e
29448 * The raw click event for the entire grid.
29449 * @param {Roo.EventObject} e
29455 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29460 preventDefault: true,
29465 getAutoCreate : function()
29470 cls : 'roo-menu-item-text',
29478 cls : 'fa ' + this.icon
29487 href : this.href || '#',
29494 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29498 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29500 if(this.pos == 'left'){
29501 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29508 initEvents : function()
29510 this.el.on('mouseover', this.onMouseOver, this);
29511 this.el.on('mouseout', this.onMouseOut, this);
29513 this.el.select('a', true).first().on('click', this.onClick, this);
29517 onClick : function(e)
29519 if(this.preventDefault){
29520 e.preventDefault();
29523 this.fireEvent("click", this, e);
29526 onMouseOver : function(e)
29528 if(this.submenu && this.pos == 'left'){
29529 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29532 this.fireEvent("mouseover", this, e);
29535 onMouseOut : function(e)
29537 this.fireEvent("mouseout", this, e);
29549 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29552 * @class Roo.bootstrap.menu.Separator
29553 * @extends Roo.bootstrap.Component
29554 * Bootstrap Separator class
29557 * Create a new Separator
29558 * @param {Object} config The config object
29562 Roo.bootstrap.menu.Separator = function(config){
29563 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29566 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29568 getAutoCreate : function(){
29571 cls: 'dropdown-divider divider'
29589 * @class Roo.bootstrap.Tooltip
29590 * Bootstrap Tooltip class
29591 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29592 * to determine which dom element triggers the tooltip.
29594 * It needs to add support for additional attributes like tooltip-position
29597 * Create a new Toolti
29598 * @param {Object} config The config object
29601 Roo.bootstrap.Tooltip = function(config){
29602 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29604 this.alignment = Roo.bootstrap.Tooltip.alignment;
29606 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29607 this.alignment = config.alignment;
29612 Roo.apply(Roo.bootstrap.Tooltip, {
29614 * @function init initialize tooltip monitoring.
29618 currentTip : false,
29619 currentRegion : false,
29625 Roo.get(document).on('mouseover', this.enter ,this);
29626 Roo.get(document).on('mouseout', this.leave, this);
29629 this.currentTip = new Roo.bootstrap.Tooltip();
29632 enter : function(ev)
29634 var dom = ev.getTarget();
29636 //Roo.log(['enter',dom]);
29637 var el = Roo.fly(dom);
29638 if (this.currentEl) {
29640 //Roo.log(this.currentEl);
29641 //Roo.log(this.currentEl.contains(dom));
29642 if (this.currentEl == el) {
29645 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29651 if (this.currentTip.el) {
29652 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29656 if(!el || el.dom == document){
29662 if (!el.attr('tooltip')) {
29663 pel = el.findParent("[tooltip]");
29665 bindEl = Roo.get(pel);
29671 // you can not look for children, as if el is the body.. then everythign is the child..
29672 if (!pel && !el.attr('tooltip')) { //
29673 if (!el.select("[tooltip]").elements.length) {
29676 // is the mouse over this child...?
29677 bindEl = el.select("[tooltip]").first();
29678 var xy = ev.getXY();
29679 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29680 //Roo.log("not in region.");
29683 //Roo.log("child element over..");
29686 this.currentEl = el;
29687 this.currentTip.bind(bindEl);
29688 this.currentRegion = Roo.lib.Region.getRegion(dom);
29689 this.currentTip.enter();
29692 leave : function(ev)
29694 var dom = ev.getTarget();
29695 //Roo.log(['leave',dom]);
29696 if (!this.currentEl) {
29701 if (dom != this.currentEl.dom) {
29704 var xy = ev.getXY();
29705 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29708 // only activate leave if mouse cursor is outside... bounding box..
29713 if (this.currentTip) {
29714 this.currentTip.leave();
29716 //Roo.log('clear currentEl');
29717 this.currentEl = false;
29722 'left' : ['r-l', [-2,0], 'right'],
29723 'right' : ['l-r', [2,0], 'left'],
29724 'bottom' : ['t-b', [0,2], 'top'],
29725 'top' : [ 'b-t', [0,-2], 'bottom']
29731 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29736 delay : null, // can be { show : 300 , hide: 500}
29740 hoverState : null, //???
29742 placement : 'bottom',
29746 getAutoCreate : function(){
29753 cls : 'tooltip-arrow arrow'
29756 cls : 'tooltip-inner'
29763 bind : function(el)
29768 initEvents : function()
29770 this.arrowEl = this.el.select('.arrow', true).first();
29771 this.innerEl = this.el.select('.tooltip-inner', true).first();
29774 enter : function () {
29776 if (this.timeout != null) {
29777 clearTimeout(this.timeout);
29780 this.hoverState = 'in';
29781 //Roo.log("enter - show");
29782 if (!this.delay || !this.delay.show) {
29787 this.timeout = setTimeout(function () {
29788 if (_t.hoverState == 'in') {
29791 }, this.delay.show);
29795 clearTimeout(this.timeout);
29797 this.hoverState = 'out';
29798 if (!this.delay || !this.delay.hide) {
29804 this.timeout = setTimeout(function () {
29805 //Roo.log("leave - timeout");
29807 if (_t.hoverState == 'out') {
29809 Roo.bootstrap.Tooltip.currentEl = false;
29814 show : function (msg)
29817 this.render(document.body);
29820 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29822 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29824 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29826 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29827 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29829 var placement = typeof this.placement == 'function' ?
29830 this.placement.call(this, this.el, on_el) :
29833 var autoToken = /\s?auto?\s?/i;
29834 var autoPlace = autoToken.test(placement);
29836 placement = placement.replace(autoToken, '') || 'top';
29840 //this.el.setXY([0,0]);
29842 //this.el.dom.style.display='block';
29844 //this.el.appendTo(on_el);
29846 var p = this.getPosition();
29847 var box = this.el.getBox();
29853 var align = this.alignment[placement];
29855 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29857 if(placement == 'top' || placement == 'bottom'){
29859 placement = 'right';
29862 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29863 placement = 'left';
29866 var scroll = Roo.select('body', true).first().getScroll();
29868 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29872 align = this.alignment[placement];
29874 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29878 var elems = document.getElementsByTagName('div');
29879 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29880 for (var i = 0; i < elems.length; i++) {
29881 var zindex = Number.parseInt(
29882 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29885 if (zindex > highest) {
29892 this.el.dom.style.zIndex = highest;
29894 this.el.alignTo(this.bindEl, align[0],align[1]);
29895 //var arrow = this.el.select('.arrow',true).first();
29896 //arrow.set(align[2],
29898 this.el.addClass(placement);
29899 this.el.addClass("bs-tooltip-"+ placement);
29901 this.el.addClass('in fade show');
29903 this.hoverState = null;
29905 if (this.el.hasClass('fade')) {
29920 //this.el.setXY([0,0]);
29921 this.el.removeClass(['show', 'in']);
29937 * @class Roo.bootstrap.LocationPicker
29938 * @extends Roo.bootstrap.Component
29939 * Bootstrap LocationPicker class
29940 * @cfg {Number} latitude Position when init default 0
29941 * @cfg {Number} longitude Position when init default 0
29942 * @cfg {Number} zoom default 15
29943 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29944 * @cfg {Boolean} mapTypeControl default false
29945 * @cfg {Boolean} disableDoubleClickZoom default false
29946 * @cfg {Boolean} scrollwheel default true
29947 * @cfg {Boolean} streetViewControl default false
29948 * @cfg {Number} radius default 0
29949 * @cfg {String} locationName
29950 * @cfg {Boolean} draggable default true
29951 * @cfg {Boolean} enableAutocomplete default false
29952 * @cfg {Boolean} enableReverseGeocode default true
29953 * @cfg {String} markerTitle
29956 * Create a new LocationPicker
29957 * @param {Object} config The config object
29961 Roo.bootstrap.LocationPicker = function(config){
29963 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29968 * Fires when the picker initialized.
29969 * @param {Roo.bootstrap.LocationPicker} this
29970 * @param {Google Location} location
29974 * @event positionchanged
29975 * Fires when the picker position changed.
29976 * @param {Roo.bootstrap.LocationPicker} this
29977 * @param {Google Location} location
29979 positionchanged : true,
29982 * Fires when the map resize.
29983 * @param {Roo.bootstrap.LocationPicker} this
29988 * Fires when the map show.
29989 * @param {Roo.bootstrap.LocationPicker} this
29994 * Fires when the map hide.
29995 * @param {Roo.bootstrap.LocationPicker} this
30000 * Fires when click the map.
30001 * @param {Roo.bootstrap.LocationPicker} this
30002 * @param {Map event} e
30006 * @event mapRightClick
30007 * Fires when right click the map.
30008 * @param {Roo.bootstrap.LocationPicker} this
30009 * @param {Map event} e
30011 mapRightClick : true,
30013 * @event markerClick
30014 * Fires when click the marker.
30015 * @param {Roo.bootstrap.LocationPicker} this
30016 * @param {Map event} e
30018 markerClick : true,
30020 * @event markerRightClick
30021 * Fires when right click the marker.
30022 * @param {Roo.bootstrap.LocationPicker} this
30023 * @param {Map event} e
30025 markerRightClick : true,
30027 * @event OverlayViewDraw
30028 * Fires when OverlayView Draw
30029 * @param {Roo.bootstrap.LocationPicker} this
30031 OverlayViewDraw : true,
30033 * @event OverlayViewOnAdd
30034 * Fires when OverlayView Draw
30035 * @param {Roo.bootstrap.LocationPicker} this
30037 OverlayViewOnAdd : true,
30039 * @event OverlayViewOnRemove
30040 * Fires when OverlayView Draw
30041 * @param {Roo.bootstrap.LocationPicker} this
30043 OverlayViewOnRemove : true,
30045 * @event OverlayViewShow
30046 * Fires when OverlayView Draw
30047 * @param {Roo.bootstrap.LocationPicker} this
30048 * @param {Pixel} cpx
30050 OverlayViewShow : true,
30052 * @event OverlayViewHide
30053 * Fires when OverlayView Draw
30054 * @param {Roo.bootstrap.LocationPicker} this
30056 OverlayViewHide : true,
30058 * @event loadexception
30059 * Fires when load google lib failed.
30060 * @param {Roo.bootstrap.LocationPicker} this
30062 loadexception : true
30067 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30069 gMapContext: false,
30075 mapTypeControl: false,
30076 disableDoubleClickZoom: false,
30078 streetViewControl: false,
30082 enableAutocomplete: false,
30083 enableReverseGeocode: true,
30086 getAutoCreate: function()
30091 cls: 'roo-location-picker'
30097 initEvents: function(ct, position)
30099 if(!this.el.getWidth() || this.isApplied()){
30103 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30108 initial: function()
30110 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30111 this.fireEvent('loadexception', this);
30115 if(!this.mapTypeId){
30116 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30119 this.gMapContext = this.GMapContext();
30121 this.initOverlayView();
30123 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30127 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30128 _this.setPosition(_this.gMapContext.marker.position);
30131 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30132 _this.fireEvent('mapClick', this, event);
30136 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30137 _this.fireEvent('mapRightClick', this, event);
30141 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30142 _this.fireEvent('markerClick', this, event);
30146 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30147 _this.fireEvent('markerRightClick', this, event);
30151 this.setPosition(this.gMapContext.location);
30153 this.fireEvent('initial', this, this.gMapContext.location);
30156 initOverlayView: function()
30160 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30164 _this.fireEvent('OverlayViewDraw', _this);
30169 _this.fireEvent('OverlayViewOnAdd', _this);
30172 onRemove: function()
30174 _this.fireEvent('OverlayViewOnRemove', _this);
30177 show: function(cpx)
30179 _this.fireEvent('OverlayViewShow', _this, cpx);
30184 _this.fireEvent('OverlayViewHide', _this);
30190 fromLatLngToContainerPixel: function(event)
30192 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30195 isApplied: function()
30197 return this.getGmapContext() == false ? false : true;
30200 getGmapContext: function()
30202 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30205 GMapContext: function()
30207 var position = new google.maps.LatLng(this.latitude, this.longitude);
30209 var _map = new google.maps.Map(this.el.dom, {
30212 mapTypeId: this.mapTypeId,
30213 mapTypeControl: this.mapTypeControl,
30214 disableDoubleClickZoom: this.disableDoubleClickZoom,
30215 scrollwheel: this.scrollwheel,
30216 streetViewControl: this.streetViewControl,
30217 locationName: this.locationName,
30218 draggable: this.draggable,
30219 enableAutocomplete: this.enableAutocomplete,
30220 enableReverseGeocode: this.enableReverseGeocode
30223 var _marker = new google.maps.Marker({
30224 position: position,
30226 title: this.markerTitle,
30227 draggable: this.draggable
30234 location: position,
30235 radius: this.radius,
30236 locationName: this.locationName,
30237 addressComponents: {
30238 formatted_address: null,
30239 addressLine1: null,
30240 addressLine2: null,
30242 streetNumber: null,
30246 stateOrProvince: null
30249 domContainer: this.el.dom,
30250 geodecoder: new google.maps.Geocoder()
30254 drawCircle: function(center, radius, options)
30256 if (this.gMapContext.circle != null) {
30257 this.gMapContext.circle.setMap(null);
30261 options = Roo.apply({}, options, {
30262 strokeColor: "#0000FF",
30263 strokeOpacity: .35,
30265 fillColor: "#0000FF",
30269 options.map = this.gMapContext.map;
30270 options.radius = radius;
30271 options.center = center;
30272 this.gMapContext.circle = new google.maps.Circle(options);
30273 return this.gMapContext.circle;
30279 setPosition: function(location)
30281 this.gMapContext.location = location;
30282 this.gMapContext.marker.setPosition(location);
30283 this.gMapContext.map.panTo(location);
30284 this.drawCircle(location, this.gMapContext.radius, {});
30288 if (this.gMapContext.settings.enableReverseGeocode) {
30289 this.gMapContext.geodecoder.geocode({
30290 latLng: this.gMapContext.location
30291 }, function(results, status) {
30293 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30294 _this.gMapContext.locationName = results[0].formatted_address;
30295 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30297 _this.fireEvent('positionchanged', this, location);
30304 this.fireEvent('positionchanged', this, location);
30309 google.maps.event.trigger(this.gMapContext.map, "resize");
30311 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30313 this.fireEvent('resize', this);
30316 setPositionByLatLng: function(latitude, longitude)
30318 this.setPosition(new google.maps.LatLng(latitude, longitude));
30321 getCurrentPosition: function()
30324 latitude: this.gMapContext.location.lat(),
30325 longitude: this.gMapContext.location.lng()
30329 getAddressName: function()
30331 return this.gMapContext.locationName;
30334 getAddressComponents: function()
30336 return this.gMapContext.addressComponents;
30339 address_component_from_google_geocode: function(address_components)
30343 for (var i = 0; i < address_components.length; i++) {
30344 var component = address_components[i];
30345 if (component.types.indexOf("postal_code") >= 0) {
30346 result.postalCode = component.short_name;
30347 } else if (component.types.indexOf("street_number") >= 0) {
30348 result.streetNumber = component.short_name;
30349 } else if (component.types.indexOf("route") >= 0) {
30350 result.streetName = component.short_name;
30351 } else if (component.types.indexOf("neighborhood") >= 0) {
30352 result.city = component.short_name;
30353 } else if (component.types.indexOf("locality") >= 0) {
30354 result.city = component.short_name;
30355 } else if (component.types.indexOf("sublocality") >= 0) {
30356 result.district = component.short_name;
30357 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30358 result.stateOrProvince = component.short_name;
30359 } else if (component.types.indexOf("country") >= 0) {
30360 result.country = component.short_name;
30364 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30365 result.addressLine2 = "";
30369 setZoomLevel: function(zoom)
30371 this.gMapContext.map.setZoom(zoom);
30384 this.fireEvent('show', this);
30395 this.fireEvent('hide', this);
30400 Roo.apply(Roo.bootstrap.LocationPicker, {
30402 OverlayView : function(map, options)
30404 options = options || {};
30411 * @class Roo.bootstrap.Alert
30412 * @extends Roo.bootstrap.Component
30413 * Bootstrap Alert class - shows an alert area box
30415 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30416 Enter a valid email address
30419 * @cfg {String} title The title of alert
30420 * @cfg {String} html The content of alert
30421 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30422 * @cfg {String} fa font-awesomeicon
30423 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30424 * @cfg {Boolean} close true to show a x closer
30428 * Create a new alert
30429 * @param {Object} config The config object
30433 Roo.bootstrap.Alert = function(config){
30434 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30438 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30444 faicon: false, // BC
30448 getAutoCreate : function()
30460 style : this.close ? '' : 'display:none'
30464 cls : 'roo-alert-icon'
30469 cls : 'roo-alert-title',
30474 cls : 'roo-alert-text',
30481 cfg.cn[0].cls += ' fa ' + this.faicon;
30484 cfg.cn[0].cls += ' fa ' + this.fa;
30488 cfg.cls += ' alert-' + this.weight;
30494 initEvents: function()
30496 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30497 this.titleEl = this.el.select('.roo-alert-title',true).first();
30498 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30499 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30500 if (this.seconds > 0) {
30501 this.hide.defer(this.seconds, this);
30505 * Set the Title Message HTML
30506 * @param {String} html
30508 setTitle : function(str)
30510 this.titleEl.dom.innerHTML = str;
30514 * Set the Body Message HTML
30515 * @param {String} html
30517 setHtml : function(str)
30519 this.htmlEl.dom.innerHTML = str;
30522 * Set the Weight of the alert
30523 * @param {String} (success|info|warning|danger) weight
30526 setWeight : function(weight)
30529 this.el.removeClass('alert-' + this.weight);
30532 this.weight = weight;
30534 this.el.addClass('alert-' + this.weight);
30537 * Set the Icon of the alert
30538 * @param {String} see fontawsome names (name without the 'fa-' bit)
30540 setIcon : function(icon)
30543 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30546 this.faicon = icon;
30548 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30573 * @class Roo.bootstrap.UploadCropbox
30574 * @extends Roo.bootstrap.Component
30575 * Bootstrap UploadCropbox class
30576 * @cfg {String} emptyText show when image has been loaded
30577 * @cfg {String} rotateNotify show when image too small to rotate
30578 * @cfg {Number} errorTimeout default 3000
30579 * @cfg {Number} minWidth default 300
30580 * @cfg {Number} minHeight default 300
30581 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30582 * @cfg {Boolean} isDocument (true|false) default false
30583 * @cfg {String} url action url
30584 * @cfg {String} paramName default 'imageUpload'
30585 * @cfg {String} method default POST
30586 * @cfg {Boolean} loadMask (true|false) default true
30587 * @cfg {Boolean} loadingText default 'Loading...'
30590 * Create a new UploadCropbox
30591 * @param {Object} config The config object
30594 Roo.bootstrap.UploadCropbox = function(config){
30595 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30599 * @event beforeselectfile
30600 * Fire before select file
30601 * @param {Roo.bootstrap.UploadCropbox} this
30603 "beforeselectfile" : true,
30606 * Fire after initEvent
30607 * @param {Roo.bootstrap.UploadCropbox} this
30612 * Fire after initEvent
30613 * @param {Roo.bootstrap.UploadCropbox} this
30614 * @param {String} data
30619 * Fire when preparing the file data
30620 * @param {Roo.bootstrap.UploadCropbox} this
30621 * @param {Object} file
30626 * Fire when get exception
30627 * @param {Roo.bootstrap.UploadCropbox} this
30628 * @param {XMLHttpRequest} xhr
30630 "exception" : true,
30632 * @event beforeloadcanvas
30633 * Fire before load the canvas
30634 * @param {Roo.bootstrap.UploadCropbox} this
30635 * @param {String} src
30637 "beforeloadcanvas" : true,
30640 * Fire when trash image
30641 * @param {Roo.bootstrap.UploadCropbox} this
30646 * Fire when download the image
30647 * @param {Roo.bootstrap.UploadCropbox} this
30651 * @event footerbuttonclick
30652 * Fire when footerbuttonclick
30653 * @param {Roo.bootstrap.UploadCropbox} this
30654 * @param {String} type
30656 "footerbuttonclick" : true,
30660 * @param {Roo.bootstrap.UploadCropbox} this
30665 * Fire when rotate the image
30666 * @param {Roo.bootstrap.UploadCropbox} this
30667 * @param {String} pos
30672 * Fire when inspect the file
30673 * @param {Roo.bootstrap.UploadCropbox} this
30674 * @param {Object} file
30679 * Fire when xhr upload the file
30680 * @param {Roo.bootstrap.UploadCropbox} this
30681 * @param {Object} data
30686 * Fire when arrange the file data
30687 * @param {Roo.bootstrap.UploadCropbox} this
30688 * @param {Object} formData
30693 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30696 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30698 emptyText : 'Click to upload image',
30699 rotateNotify : 'Image is too small to rotate',
30700 errorTimeout : 3000,
30714 cropType : 'image/jpeg',
30716 canvasLoaded : false,
30717 isDocument : false,
30719 paramName : 'imageUpload',
30721 loadingText : 'Loading...',
30724 getAutoCreate : function()
30728 cls : 'roo-upload-cropbox',
30732 cls : 'roo-upload-cropbox-selector',
30737 cls : 'roo-upload-cropbox-body',
30738 style : 'cursor:pointer',
30742 cls : 'roo-upload-cropbox-preview'
30746 cls : 'roo-upload-cropbox-thumb'
30750 cls : 'roo-upload-cropbox-empty-notify',
30751 html : this.emptyText
30755 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30756 html : this.rotateNotify
30762 cls : 'roo-upload-cropbox-footer',
30765 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30775 onRender : function(ct, position)
30777 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30779 if (this.buttons.length) {
30781 Roo.each(this.buttons, function(bb) {
30783 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30785 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30791 this.maskEl = this.el;
30795 initEvents : function()
30797 this.urlAPI = (window.createObjectURL && window) ||
30798 (window.URL && URL.revokeObjectURL && URL) ||
30799 (window.webkitURL && webkitURL);
30801 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30802 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30804 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30805 this.selectorEl.hide();
30807 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30808 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30810 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30811 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30812 this.thumbEl.hide();
30814 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30815 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30817 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30818 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30819 this.errorEl.hide();
30821 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30822 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30823 this.footerEl.hide();
30825 this.setThumbBoxSize();
30831 this.fireEvent('initial', this);
30838 window.addEventListener("resize", function() { _this.resize(); } );
30840 this.bodyEl.on('click', this.beforeSelectFile, this);
30843 this.bodyEl.on('touchstart', this.onTouchStart, this);
30844 this.bodyEl.on('touchmove', this.onTouchMove, this);
30845 this.bodyEl.on('touchend', this.onTouchEnd, this);
30849 this.bodyEl.on('mousedown', this.onMouseDown, this);
30850 this.bodyEl.on('mousemove', this.onMouseMove, this);
30851 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30852 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30853 Roo.get(document).on('mouseup', this.onMouseUp, this);
30856 this.selectorEl.on('change', this.onFileSelected, this);
30862 this.baseScale = 1;
30864 this.baseRotate = 1;
30865 this.dragable = false;
30866 this.pinching = false;
30869 this.cropData = false;
30870 this.notifyEl.dom.innerHTML = this.emptyText;
30872 this.selectorEl.dom.value = '';
30876 resize : function()
30878 if(this.fireEvent('resize', this) != false){
30879 this.setThumbBoxPosition();
30880 this.setCanvasPosition();
30884 onFooterButtonClick : function(e, el, o, type)
30887 case 'rotate-left' :
30888 this.onRotateLeft(e);
30890 case 'rotate-right' :
30891 this.onRotateRight(e);
30894 this.beforeSelectFile(e);
30909 this.fireEvent('footerbuttonclick', this, type);
30912 beforeSelectFile : function(e)
30914 e.preventDefault();
30916 if(this.fireEvent('beforeselectfile', this) != false){
30917 this.selectorEl.dom.click();
30921 onFileSelected : function(e)
30923 e.preventDefault();
30925 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30929 var file = this.selectorEl.dom.files[0];
30931 if(this.fireEvent('inspect', this, file) != false){
30932 this.prepare(file);
30937 trash : function(e)
30939 this.fireEvent('trash', this);
30942 download : function(e)
30944 this.fireEvent('download', this);
30947 loadCanvas : function(src)
30949 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30953 this.imageEl = document.createElement('img');
30957 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30959 this.imageEl.src = src;
30963 onLoadCanvas : function()
30965 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30966 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30968 this.bodyEl.un('click', this.beforeSelectFile, this);
30970 this.notifyEl.hide();
30971 this.thumbEl.show();
30972 this.footerEl.show();
30974 this.baseRotateLevel();
30976 if(this.isDocument){
30977 this.setThumbBoxSize();
30980 this.setThumbBoxPosition();
30982 this.baseScaleLevel();
30988 this.canvasLoaded = true;
30991 this.maskEl.unmask();
30996 setCanvasPosition : function()
30998 if(!this.canvasEl){
31002 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31003 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31005 this.previewEl.setLeft(pw);
31006 this.previewEl.setTop(ph);
31010 onMouseDown : function(e)
31014 this.dragable = true;
31015 this.pinching = false;
31017 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31018 this.dragable = false;
31022 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31023 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31027 onMouseMove : function(e)
31031 if(!this.canvasLoaded){
31035 if (!this.dragable){
31039 var minX = Math.ceil(this.thumbEl.getLeft(true));
31040 var minY = Math.ceil(this.thumbEl.getTop(true));
31042 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31043 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31045 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31046 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31048 x = x - this.mouseX;
31049 y = y - this.mouseY;
31051 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31052 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31054 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31055 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31057 this.previewEl.setLeft(bgX);
31058 this.previewEl.setTop(bgY);
31060 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31061 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31064 onMouseUp : function(e)
31068 this.dragable = false;
31071 onMouseWheel : function(e)
31075 this.startScale = this.scale;
31077 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31079 if(!this.zoomable()){
31080 this.scale = this.startScale;
31089 zoomable : function()
31091 var minScale = this.thumbEl.getWidth() / this.minWidth;
31093 if(this.minWidth < this.minHeight){
31094 minScale = this.thumbEl.getHeight() / this.minHeight;
31097 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31098 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31102 (this.rotate == 0 || this.rotate == 180) &&
31104 width > this.imageEl.OriginWidth ||
31105 height > this.imageEl.OriginHeight ||
31106 (width < this.minWidth && height < this.minHeight)
31114 (this.rotate == 90 || this.rotate == 270) &&
31116 width > this.imageEl.OriginWidth ||
31117 height > this.imageEl.OriginHeight ||
31118 (width < this.minHeight && height < this.minWidth)
31125 !this.isDocument &&
31126 (this.rotate == 0 || this.rotate == 180) &&
31128 width < this.minWidth ||
31129 width > this.imageEl.OriginWidth ||
31130 height < this.minHeight ||
31131 height > this.imageEl.OriginHeight
31138 !this.isDocument &&
31139 (this.rotate == 90 || this.rotate == 270) &&
31141 width < this.minHeight ||
31142 width > this.imageEl.OriginWidth ||
31143 height < this.minWidth ||
31144 height > this.imageEl.OriginHeight
31154 onRotateLeft : function(e)
31156 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31158 var minScale = this.thumbEl.getWidth() / this.minWidth;
31160 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31161 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31163 this.startScale = this.scale;
31165 while (this.getScaleLevel() < minScale){
31167 this.scale = this.scale + 1;
31169 if(!this.zoomable()){
31174 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31175 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31180 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31187 this.scale = this.startScale;
31189 this.onRotateFail();
31194 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31196 if(this.isDocument){
31197 this.setThumbBoxSize();
31198 this.setThumbBoxPosition();
31199 this.setCanvasPosition();
31204 this.fireEvent('rotate', this, 'left');
31208 onRotateRight : function(e)
31210 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31212 var minScale = this.thumbEl.getWidth() / this.minWidth;
31214 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31215 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31217 this.startScale = this.scale;
31219 while (this.getScaleLevel() < minScale){
31221 this.scale = this.scale + 1;
31223 if(!this.zoomable()){
31228 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31229 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31234 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31241 this.scale = this.startScale;
31243 this.onRotateFail();
31248 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31250 if(this.isDocument){
31251 this.setThumbBoxSize();
31252 this.setThumbBoxPosition();
31253 this.setCanvasPosition();
31258 this.fireEvent('rotate', this, 'right');
31261 onRotateFail : function()
31263 this.errorEl.show(true);
31267 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31272 this.previewEl.dom.innerHTML = '';
31274 var canvasEl = document.createElement("canvas");
31276 var contextEl = canvasEl.getContext("2d");
31278 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31279 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31280 var center = this.imageEl.OriginWidth / 2;
31282 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31283 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31284 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31285 center = this.imageEl.OriginHeight / 2;
31288 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31290 contextEl.translate(center, center);
31291 contextEl.rotate(this.rotate * Math.PI / 180);
31293 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31295 this.canvasEl = document.createElement("canvas");
31297 this.contextEl = this.canvasEl.getContext("2d");
31299 switch (this.rotate) {
31302 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31303 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31305 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31310 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31311 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31313 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31314 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);
31318 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31323 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31324 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31326 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31327 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);
31331 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);
31336 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31337 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31339 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31340 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31344 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);
31351 this.previewEl.appendChild(this.canvasEl);
31353 this.setCanvasPosition();
31358 if(!this.canvasLoaded){
31362 var imageCanvas = document.createElement("canvas");
31364 var imageContext = imageCanvas.getContext("2d");
31366 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31367 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31369 var center = imageCanvas.width / 2;
31371 imageContext.translate(center, center);
31373 imageContext.rotate(this.rotate * Math.PI / 180);
31375 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31377 var canvas = document.createElement("canvas");
31379 var context = canvas.getContext("2d");
31381 canvas.width = this.minWidth;
31382 canvas.height = this.minHeight;
31384 switch (this.rotate) {
31387 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31388 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31390 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31391 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31393 var targetWidth = this.minWidth - 2 * x;
31394 var targetHeight = this.minHeight - 2 * y;
31398 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31399 scale = targetWidth / width;
31402 if(x > 0 && y == 0){
31403 scale = targetHeight / height;
31406 if(x > 0 && y > 0){
31407 scale = targetWidth / width;
31409 if(width < height){
31410 scale = targetHeight / height;
31414 context.scale(scale, scale);
31416 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31417 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31419 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31420 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31422 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31427 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31428 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31430 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31431 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31433 var targetWidth = this.minWidth - 2 * x;
31434 var targetHeight = this.minHeight - 2 * y;
31438 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31439 scale = targetWidth / width;
31442 if(x > 0 && y == 0){
31443 scale = targetHeight / height;
31446 if(x > 0 && y > 0){
31447 scale = targetWidth / width;
31449 if(width < height){
31450 scale = targetHeight / height;
31454 context.scale(scale, scale);
31456 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31457 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31459 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31460 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31462 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31464 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31469 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31470 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31472 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31473 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31475 var targetWidth = this.minWidth - 2 * x;
31476 var targetHeight = this.minHeight - 2 * y;
31480 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31481 scale = targetWidth / width;
31484 if(x > 0 && y == 0){
31485 scale = targetHeight / height;
31488 if(x > 0 && y > 0){
31489 scale = targetWidth / width;
31491 if(width < height){
31492 scale = targetHeight / height;
31496 context.scale(scale, scale);
31498 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31499 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31501 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31502 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31504 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31505 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31507 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31512 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31513 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31515 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31516 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31518 var targetWidth = this.minWidth - 2 * x;
31519 var targetHeight = this.minHeight - 2 * y;
31523 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31524 scale = targetWidth / width;
31527 if(x > 0 && y == 0){
31528 scale = targetHeight / height;
31531 if(x > 0 && y > 0){
31532 scale = targetWidth / width;
31534 if(width < height){
31535 scale = targetHeight / height;
31539 context.scale(scale, scale);
31541 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31542 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31544 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31545 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31547 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31549 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31556 this.cropData = canvas.toDataURL(this.cropType);
31558 if(this.fireEvent('crop', this, this.cropData) !== false){
31559 this.process(this.file, this.cropData);
31566 setThumbBoxSize : function()
31570 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31571 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31572 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31574 this.minWidth = width;
31575 this.minHeight = height;
31577 if(this.rotate == 90 || this.rotate == 270){
31578 this.minWidth = height;
31579 this.minHeight = width;
31584 width = Math.ceil(this.minWidth * height / this.minHeight);
31586 if(this.minWidth > this.minHeight){
31588 height = Math.ceil(this.minHeight * width / this.minWidth);
31591 this.thumbEl.setStyle({
31592 width : width + 'px',
31593 height : height + 'px'
31600 setThumbBoxPosition : function()
31602 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31603 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31605 this.thumbEl.setLeft(x);
31606 this.thumbEl.setTop(y);
31610 baseRotateLevel : function()
31612 this.baseRotate = 1;
31615 typeof(this.exif) != 'undefined' &&
31616 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31617 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31619 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31622 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31626 baseScaleLevel : function()
31630 if(this.isDocument){
31632 if(this.baseRotate == 6 || this.baseRotate == 8){
31634 height = this.thumbEl.getHeight();
31635 this.baseScale = height / this.imageEl.OriginWidth;
31637 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31638 width = this.thumbEl.getWidth();
31639 this.baseScale = width / this.imageEl.OriginHeight;
31645 height = this.thumbEl.getHeight();
31646 this.baseScale = height / this.imageEl.OriginHeight;
31648 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31649 width = this.thumbEl.getWidth();
31650 this.baseScale = width / this.imageEl.OriginWidth;
31656 if(this.baseRotate == 6 || this.baseRotate == 8){
31658 width = this.thumbEl.getHeight();
31659 this.baseScale = width / this.imageEl.OriginHeight;
31661 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31662 height = this.thumbEl.getWidth();
31663 this.baseScale = height / this.imageEl.OriginHeight;
31666 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31667 height = this.thumbEl.getWidth();
31668 this.baseScale = height / this.imageEl.OriginHeight;
31670 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31671 width = this.thumbEl.getHeight();
31672 this.baseScale = width / this.imageEl.OriginWidth;
31679 width = this.thumbEl.getWidth();
31680 this.baseScale = width / this.imageEl.OriginWidth;
31682 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31683 height = this.thumbEl.getHeight();
31684 this.baseScale = height / this.imageEl.OriginHeight;
31687 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31689 height = this.thumbEl.getHeight();
31690 this.baseScale = height / this.imageEl.OriginHeight;
31692 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31693 width = this.thumbEl.getWidth();
31694 this.baseScale = width / this.imageEl.OriginWidth;
31702 getScaleLevel : function()
31704 return this.baseScale * Math.pow(1.1, this.scale);
31707 onTouchStart : function(e)
31709 if(!this.canvasLoaded){
31710 this.beforeSelectFile(e);
31714 var touches = e.browserEvent.touches;
31720 if(touches.length == 1){
31721 this.onMouseDown(e);
31725 if(touches.length != 2){
31731 for(var i = 0, finger; finger = touches[i]; i++){
31732 coords.push(finger.pageX, finger.pageY);
31735 var x = Math.pow(coords[0] - coords[2], 2);
31736 var y = Math.pow(coords[1] - coords[3], 2);
31738 this.startDistance = Math.sqrt(x + y);
31740 this.startScale = this.scale;
31742 this.pinching = true;
31743 this.dragable = false;
31747 onTouchMove : function(e)
31749 if(!this.pinching && !this.dragable){
31753 var touches = e.browserEvent.touches;
31760 this.onMouseMove(e);
31766 for(var i = 0, finger; finger = touches[i]; i++){
31767 coords.push(finger.pageX, finger.pageY);
31770 var x = Math.pow(coords[0] - coords[2], 2);
31771 var y = Math.pow(coords[1] - coords[3], 2);
31773 this.endDistance = Math.sqrt(x + y);
31775 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31777 if(!this.zoomable()){
31778 this.scale = this.startScale;
31786 onTouchEnd : function(e)
31788 this.pinching = false;
31789 this.dragable = false;
31793 process : function(file, crop)
31796 this.maskEl.mask(this.loadingText);
31799 this.xhr = new XMLHttpRequest();
31801 file.xhr = this.xhr;
31803 this.xhr.open(this.method, this.url, true);
31806 "Accept": "application/json",
31807 "Cache-Control": "no-cache",
31808 "X-Requested-With": "XMLHttpRequest"
31811 for (var headerName in headers) {
31812 var headerValue = headers[headerName];
31814 this.xhr.setRequestHeader(headerName, headerValue);
31820 this.xhr.onload = function()
31822 _this.xhrOnLoad(_this.xhr);
31825 this.xhr.onerror = function()
31827 _this.xhrOnError(_this.xhr);
31830 var formData = new FormData();
31832 formData.append('returnHTML', 'NO');
31835 formData.append('crop', crop);
31838 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31839 formData.append(this.paramName, file, file.name);
31842 if(typeof(file.filename) != 'undefined'){
31843 formData.append('filename', file.filename);
31846 if(typeof(file.mimetype) != 'undefined'){
31847 formData.append('mimetype', file.mimetype);
31850 if(this.fireEvent('arrange', this, formData) != false){
31851 this.xhr.send(formData);
31855 xhrOnLoad : function(xhr)
31858 this.maskEl.unmask();
31861 if (xhr.readyState !== 4) {
31862 this.fireEvent('exception', this, xhr);
31866 var response = Roo.decode(xhr.responseText);
31868 if(!response.success){
31869 this.fireEvent('exception', this, xhr);
31873 var response = Roo.decode(xhr.responseText);
31875 this.fireEvent('upload', this, response);
31879 xhrOnError : function()
31882 this.maskEl.unmask();
31885 Roo.log('xhr on error');
31887 var response = Roo.decode(xhr.responseText);
31893 prepare : function(file)
31896 this.maskEl.mask(this.loadingText);
31902 if(typeof(file) === 'string'){
31903 this.loadCanvas(file);
31907 if(!file || !this.urlAPI){
31912 this.cropType = file.type;
31916 if(this.fireEvent('prepare', this, this.file) != false){
31918 var reader = new FileReader();
31920 reader.onload = function (e) {
31921 if (e.target.error) {
31922 Roo.log(e.target.error);
31926 var buffer = e.target.result,
31927 dataView = new DataView(buffer),
31929 maxOffset = dataView.byteLength - 4,
31933 if (dataView.getUint16(0) === 0xffd8) {
31934 while (offset < maxOffset) {
31935 markerBytes = dataView.getUint16(offset);
31937 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31938 markerLength = dataView.getUint16(offset + 2) + 2;
31939 if (offset + markerLength > dataView.byteLength) {
31940 Roo.log('Invalid meta data: Invalid segment size.');
31944 if(markerBytes == 0xffe1){
31945 _this.parseExifData(
31952 offset += markerLength;
31962 var url = _this.urlAPI.createObjectURL(_this.file);
31964 _this.loadCanvas(url);
31969 reader.readAsArrayBuffer(this.file);
31975 parseExifData : function(dataView, offset, length)
31977 var tiffOffset = offset + 10,
31981 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31982 // No Exif data, might be XMP data instead
31986 // Check for the ASCII code for "Exif" (0x45786966):
31987 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31988 // No Exif data, might be XMP data instead
31991 if (tiffOffset + 8 > dataView.byteLength) {
31992 Roo.log('Invalid Exif data: Invalid segment size.');
31995 // Check for the two null bytes:
31996 if (dataView.getUint16(offset + 8) !== 0x0000) {
31997 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32000 // Check the byte alignment:
32001 switch (dataView.getUint16(tiffOffset)) {
32003 littleEndian = true;
32006 littleEndian = false;
32009 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32012 // Check for the TIFF tag marker (0x002A):
32013 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32014 Roo.log('Invalid Exif data: Missing TIFF marker.');
32017 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32018 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32020 this.parseExifTags(
32023 tiffOffset + dirOffset,
32028 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32033 if (dirOffset + 6 > dataView.byteLength) {
32034 Roo.log('Invalid Exif data: Invalid directory offset.');
32037 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32038 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32039 if (dirEndOffset + 4 > dataView.byteLength) {
32040 Roo.log('Invalid Exif data: Invalid directory size.');
32043 for (i = 0; i < tagsNumber; i += 1) {
32047 dirOffset + 2 + 12 * i, // tag offset
32051 // Return the offset to the next directory:
32052 return dataView.getUint32(dirEndOffset, littleEndian);
32055 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32057 var tag = dataView.getUint16(offset, littleEndian);
32059 this.exif[tag] = this.getExifValue(
32063 dataView.getUint16(offset + 2, littleEndian), // tag type
32064 dataView.getUint32(offset + 4, littleEndian), // tag length
32069 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32071 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32080 Roo.log('Invalid Exif data: Invalid tag type.');
32084 tagSize = tagType.size * length;
32085 // Determine if the value is contained in the dataOffset bytes,
32086 // or if the value at the dataOffset is a pointer to the actual data:
32087 dataOffset = tagSize > 4 ?
32088 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32089 if (dataOffset + tagSize > dataView.byteLength) {
32090 Roo.log('Invalid Exif data: Invalid data offset.');
32093 if (length === 1) {
32094 return tagType.getValue(dataView, dataOffset, littleEndian);
32097 for (i = 0; i < length; i += 1) {
32098 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32101 if (tagType.ascii) {
32103 // Concatenate the chars:
32104 for (i = 0; i < values.length; i += 1) {
32106 // Ignore the terminating NULL byte(s):
32107 if (c === '\u0000') {
32119 Roo.apply(Roo.bootstrap.UploadCropbox, {
32121 'Orientation': 0x0112
32125 1: 0, //'top-left',
32127 3: 180, //'bottom-right',
32128 // 4: 'bottom-left',
32130 6: 90, //'right-top',
32131 // 7: 'right-bottom',
32132 8: 270 //'left-bottom'
32136 // byte, 8-bit unsigned int:
32138 getValue: function (dataView, dataOffset) {
32139 return dataView.getUint8(dataOffset);
32143 // ascii, 8-bit byte:
32145 getValue: function (dataView, dataOffset) {
32146 return String.fromCharCode(dataView.getUint8(dataOffset));
32151 // short, 16 bit int:
32153 getValue: function (dataView, dataOffset, littleEndian) {
32154 return dataView.getUint16(dataOffset, littleEndian);
32158 // long, 32 bit int:
32160 getValue: function (dataView, dataOffset, littleEndian) {
32161 return dataView.getUint32(dataOffset, littleEndian);
32165 // rational = two long values, first is numerator, second is denominator:
32167 getValue: function (dataView, dataOffset, littleEndian) {
32168 return dataView.getUint32(dataOffset, littleEndian) /
32169 dataView.getUint32(dataOffset + 4, littleEndian);
32173 // slong, 32 bit signed int:
32175 getValue: function (dataView, dataOffset, littleEndian) {
32176 return dataView.getInt32(dataOffset, littleEndian);
32180 // srational, two slongs, first is numerator, second is denominator:
32182 getValue: function (dataView, dataOffset, littleEndian) {
32183 return dataView.getInt32(dataOffset, littleEndian) /
32184 dataView.getInt32(dataOffset + 4, littleEndian);
32194 cls : 'btn-group roo-upload-cropbox-rotate-left',
32195 action : 'rotate-left',
32199 cls : 'btn btn-default',
32200 html : '<i class="fa fa-undo"></i>'
32206 cls : 'btn-group roo-upload-cropbox-picture',
32207 action : 'picture',
32211 cls : 'btn btn-default',
32212 html : '<i class="fa fa-picture-o"></i>'
32218 cls : 'btn-group roo-upload-cropbox-rotate-right',
32219 action : 'rotate-right',
32223 cls : 'btn btn-default',
32224 html : '<i class="fa fa-repeat"></i>'
32232 cls : 'btn-group roo-upload-cropbox-rotate-left',
32233 action : 'rotate-left',
32237 cls : 'btn btn-default',
32238 html : '<i class="fa fa-undo"></i>'
32244 cls : 'btn-group roo-upload-cropbox-download',
32245 action : 'download',
32249 cls : 'btn btn-default',
32250 html : '<i class="fa fa-download"></i>'
32256 cls : 'btn-group roo-upload-cropbox-crop',
32261 cls : 'btn btn-default',
32262 html : '<i class="fa fa-crop"></i>'
32268 cls : 'btn-group roo-upload-cropbox-trash',
32273 cls : 'btn btn-default',
32274 html : '<i class="fa fa-trash"></i>'
32280 cls : 'btn-group roo-upload-cropbox-rotate-right',
32281 action : 'rotate-right',
32285 cls : 'btn btn-default',
32286 html : '<i class="fa fa-repeat"></i>'
32294 cls : 'btn-group roo-upload-cropbox-rotate-left',
32295 action : 'rotate-left',
32299 cls : 'btn btn-default',
32300 html : '<i class="fa fa-undo"></i>'
32306 cls : 'btn-group roo-upload-cropbox-rotate-right',
32307 action : 'rotate-right',
32311 cls : 'btn btn-default',
32312 html : '<i class="fa fa-repeat"></i>'
32325 * @class Roo.bootstrap.DocumentManager
32326 * @extends Roo.bootstrap.Component
32327 * Bootstrap DocumentManager class
32328 * @cfg {String} paramName default 'imageUpload'
32329 * @cfg {String} toolTipName default 'filename'
32330 * @cfg {String} method default POST
32331 * @cfg {String} url action url
32332 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32333 * @cfg {Boolean} multiple multiple upload default true
32334 * @cfg {Number} thumbSize default 300
32335 * @cfg {String} fieldLabel
32336 * @cfg {Number} labelWidth default 4
32337 * @cfg {String} labelAlign (left|top) default left
32338 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32339 * @cfg {Number} labellg set the width of label (1-12)
32340 * @cfg {Number} labelmd set the width of label (1-12)
32341 * @cfg {Number} labelsm set the width of label (1-12)
32342 * @cfg {Number} labelxs set the width of label (1-12)
32345 * Create a new DocumentManager
32346 * @param {Object} config The config object
32349 Roo.bootstrap.DocumentManager = function(config){
32350 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32353 this.delegates = [];
32358 * Fire when initial the DocumentManager
32359 * @param {Roo.bootstrap.DocumentManager} this
32364 * inspect selected file
32365 * @param {Roo.bootstrap.DocumentManager} this
32366 * @param {File} file
32371 * Fire when xhr load exception
32372 * @param {Roo.bootstrap.DocumentManager} this
32373 * @param {XMLHttpRequest} xhr
32375 "exception" : true,
32377 * @event afterupload
32378 * Fire when xhr load exception
32379 * @param {Roo.bootstrap.DocumentManager} this
32380 * @param {XMLHttpRequest} xhr
32382 "afterupload" : true,
32385 * prepare the form data
32386 * @param {Roo.bootstrap.DocumentManager} this
32387 * @param {Object} formData
32392 * Fire when remove the file
32393 * @param {Roo.bootstrap.DocumentManager} this
32394 * @param {Object} file
32399 * Fire after refresh the file
32400 * @param {Roo.bootstrap.DocumentManager} this
32405 * Fire after click the image
32406 * @param {Roo.bootstrap.DocumentManager} this
32407 * @param {Object} file
32412 * Fire when upload a image and editable set to true
32413 * @param {Roo.bootstrap.DocumentManager} this
32414 * @param {Object} file
32418 * @event beforeselectfile
32419 * Fire before select file
32420 * @param {Roo.bootstrap.DocumentManager} this
32422 "beforeselectfile" : true,
32425 * Fire before process file
32426 * @param {Roo.bootstrap.DocumentManager} this
32427 * @param {Object} file
32431 * @event previewrendered
32432 * Fire when preview rendered
32433 * @param {Roo.bootstrap.DocumentManager} this
32434 * @param {Object} file
32436 "previewrendered" : true,
32439 "previewResize" : true
32444 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32453 paramName : 'imageUpload',
32454 toolTipName : 'filename',
32457 labelAlign : 'left',
32467 getAutoCreate : function()
32469 var managerWidget = {
32471 cls : 'roo-document-manager',
32475 cls : 'roo-document-manager-selector',
32480 cls : 'roo-document-manager-uploader',
32484 cls : 'roo-document-manager-upload-btn',
32485 html : '<i class="fa fa-plus"></i>'
32496 cls : 'column col-md-12',
32501 if(this.fieldLabel.length){
32506 cls : 'column col-md-12',
32507 html : this.fieldLabel
32511 cls : 'column col-md-12',
32516 if(this.labelAlign == 'left'){
32521 html : this.fieldLabel
32530 if(this.labelWidth > 12){
32531 content[0].style = "width: " + this.labelWidth + 'px';
32534 if(this.labelWidth < 13 && this.labelmd == 0){
32535 this.labelmd = this.labelWidth;
32538 if(this.labellg > 0){
32539 content[0].cls += ' col-lg-' + this.labellg;
32540 content[1].cls += ' col-lg-' + (12 - this.labellg);
32543 if(this.labelmd > 0){
32544 content[0].cls += ' col-md-' + this.labelmd;
32545 content[1].cls += ' col-md-' + (12 - this.labelmd);
32548 if(this.labelsm > 0){
32549 content[0].cls += ' col-sm-' + this.labelsm;
32550 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32553 if(this.labelxs > 0){
32554 content[0].cls += ' col-xs-' + this.labelxs;
32555 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32563 cls : 'row clearfix',
32571 initEvents : function()
32573 this.managerEl = this.el.select('.roo-document-manager', true).first();
32574 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32576 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32577 this.selectorEl.hide();
32580 this.selectorEl.attr('multiple', 'multiple');
32583 this.selectorEl.on('change', this.onFileSelected, this);
32585 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32586 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32588 this.uploader.on('click', this.onUploaderClick, this);
32590 this.renderProgressDialog();
32594 window.addEventListener("resize", function() { _this.refresh(); } );
32596 this.fireEvent('initial', this);
32599 renderProgressDialog : function()
32603 this.progressDialog = new Roo.bootstrap.Modal({
32604 cls : 'roo-document-manager-progress-dialog',
32605 allow_close : false,
32616 btnclick : function() {
32617 _this.uploadCancel();
32623 this.progressDialog.render(Roo.get(document.body));
32625 this.progress = new Roo.bootstrap.Progress({
32626 cls : 'roo-document-manager-progress',
32631 this.progress.render(this.progressDialog.getChildContainer());
32633 this.progressBar = new Roo.bootstrap.ProgressBar({
32634 cls : 'roo-document-manager-progress-bar',
32637 aria_valuemax : 12,
32641 this.progressBar.render(this.progress.getChildContainer());
32644 onUploaderClick : function(e)
32646 e.preventDefault();
32648 if(this.fireEvent('beforeselectfile', this) != false){
32649 this.selectorEl.dom.click();
32654 onFileSelected : function(e)
32656 e.preventDefault();
32658 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32662 Roo.each(this.selectorEl.dom.files, function(file){
32663 if(this.fireEvent('inspect', this, file) != false){
32664 this.files.push(file);
32674 this.selectorEl.dom.value = '';
32676 if(!this.files || !this.files.length){
32680 if(this.boxes > 0 && this.files.length > this.boxes){
32681 this.files = this.files.slice(0, this.boxes);
32684 this.uploader.show();
32686 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32687 this.uploader.hide();
32696 Roo.each(this.files, function(file){
32698 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32699 var f = this.renderPreview(file);
32704 if(file.type.indexOf('image') != -1){
32705 this.delegates.push(
32707 _this.process(file);
32708 }).createDelegate(this)
32716 _this.process(file);
32717 }).createDelegate(this)
32722 this.files = files;
32724 this.delegates = this.delegates.concat(docs);
32726 if(!this.delegates.length){
32731 this.progressBar.aria_valuemax = this.delegates.length;
32738 arrange : function()
32740 if(!this.delegates.length){
32741 this.progressDialog.hide();
32746 var delegate = this.delegates.shift();
32748 this.progressDialog.show();
32750 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32752 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32757 refresh : function()
32759 this.uploader.show();
32761 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32762 this.uploader.hide();
32765 Roo.isTouch ? this.closable(false) : this.closable(true);
32767 this.fireEvent('refresh', this);
32770 onRemove : function(e, el, o)
32772 e.preventDefault();
32774 this.fireEvent('remove', this, o);
32778 remove : function(o)
32782 Roo.each(this.files, function(file){
32783 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32792 this.files = files;
32799 Roo.each(this.files, function(file){
32804 file.target.remove();
32813 onClick : function(e, el, o)
32815 e.preventDefault();
32817 this.fireEvent('click', this, o);
32821 closable : function(closable)
32823 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32825 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32837 xhrOnLoad : function(xhr)
32839 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32843 if (xhr.readyState !== 4) {
32845 this.fireEvent('exception', this, xhr);
32849 var response = Roo.decode(xhr.responseText);
32851 if(!response.success){
32853 this.fireEvent('exception', this, xhr);
32857 var file = this.renderPreview(response.data);
32859 this.files.push(file);
32863 this.fireEvent('afterupload', this, xhr);
32867 xhrOnError : function(xhr)
32869 Roo.log('xhr on error');
32871 var response = Roo.decode(xhr.responseText);
32878 process : function(file)
32880 if(this.fireEvent('process', this, file) !== false){
32881 if(this.editable && file.type.indexOf('image') != -1){
32882 this.fireEvent('edit', this, file);
32886 this.uploadStart(file, false);
32893 uploadStart : function(file, crop)
32895 this.xhr = new XMLHttpRequest();
32897 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32902 file.xhr = this.xhr;
32904 this.managerEl.createChild({
32906 cls : 'roo-document-manager-loading',
32910 tooltip : file.name,
32911 cls : 'roo-document-manager-thumb',
32912 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32918 this.xhr.open(this.method, this.url, true);
32921 "Accept": "application/json",
32922 "Cache-Control": "no-cache",
32923 "X-Requested-With": "XMLHttpRequest"
32926 for (var headerName in headers) {
32927 var headerValue = headers[headerName];
32929 this.xhr.setRequestHeader(headerName, headerValue);
32935 this.xhr.onload = function()
32937 _this.xhrOnLoad(_this.xhr);
32940 this.xhr.onerror = function()
32942 _this.xhrOnError(_this.xhr);
32945 var formData = new FormData();
32947 formData.append('returnHTML', 'NO');
32950 formData.append('crop', crop);
32953 formData.append(this.paramName, file, file.name);
32960 if(this.fireEvent('prepare', this, formData, options) != false){
32962 if(options.manually){
32966 this.xhr.send(formData);
32970 this.uploadCancel();
32973 uploadCancel : function()
32979 this.delegates = [];
32981 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32988 renderPreview : function(file)
32990 if(typeof(file.target) != 'undefined' && file.target){
32994 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32996 var previewEl = this.managerEl.createChild({
32998 cls : 'roo-document-manager-preview',
33002 tooltip : file[this.toolTipName],
33003 cls : 'roo-document-manager-thumb',
33004 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33009 html : '<i class="fa fa-times-circle"></i>'
33014 var close = previewEl.select('button.close', true).first();
33016 close.on('click', this.onRemove, this, file);
33018 file.target = previewEl;
33020 var image = previewEl.select('img', true).first();
33024 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33026 image.on('click', this.onClick, this, file);
33028 this.fireEvent('previewrendered', this, file);
33034 onPreviewLoad : function(file, image)
33036 if(typeof(file.target) == 'undefined' || !file.target){
33040 var width = image.dom.naturalWidth || image.dom.width;
33041 var height = image.dom.naturalHeight || image.dom.height;
33043 if(!this.previewResize) {
33047 if(width > height){
33048 file.target.addClass('wide');
33052 file.target.addClass('tall');
33057 uploadFromSource : function(file, crop)
33059 this.xhr = new XMLHttpRequest();
33061 this.managerEl.createChild({
33063 cls : 'roo-document-manager-loading',
33067 tooltip : file.name,
33068 cls : 'roo-document-manager-thumb',
33069 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33075 this.xhr.open(this.method, this.url, true);
33078 "Accept": "application/json",
33079 "Cache-Control": "no-cache",
33080 "X-Requested-With": "XMLHttpRequest"
33083 for (var headerName in headers) {
33084 var headerValue = headers[headerName];
33086 this.xhr.setRequestHeader(headerName, headerValue);
33092 this.xhr.onload = function()
33094 _this.xhrOnLoad(_this.xhr);
33097 this.xhr.onerror = function()
33099 _this.xhrOnError(_this.xhr);
33102 var formData = new FormData();
33104 formData.append('returnHTML', 'NO');
33106 formData.append('crop', crop);
33108 if(typeof(file.filename) != 'undefined'){
33109 formData.append('filename', file.filename);
33112 if(typeof(file.mimetype) != 'undefined'){
33113 formData.append('mimetype', file.mimetype);
33118 if(this.fireEvent('prepare', this, formData) != false){
33119 this.xhr.send(formData);
33129 * @class Roo.bootstrap.DocumentViewer
33130 * @extends Roo.bootstrap.Component
33131 * Bootstrap DocumentViewer class
33132 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33133 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33136 * Create a new DocumentViewer
33137 * @param {Object} config The config object
33140 Roo.bootstrap.DocumentViewer = function(config){
33141 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33146 * Fire after initEvent
33147 * @param {Roo.bootstrap.DocumentViewer} this
33153 * @param {Roo.bootstrap.DocumentViewer} this
33158 * Fire after download button
33159 * @param {Roo.bootstrap.DocumentViewer} this
33164 * Fire after trash button
33165 * @param {Roo.bootstrap.DocumentViewer} this
33172 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33174 showDownload : true,
33178 getAutoCreate : function()
33182 cls : 'roo-document-viewer',
33186 cls : 'roo-document-viewer-body',
33190 cls : 'roo-document-viewer-thumb',
33194 cls : 'roo-document-viewer-image'
33202 cls : 'roo-document-viewer-footer',
33205 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33209 cls : 'btn-group roo-document-viewer-download',
33213 cls : 'btn btn-default',
33214 html : '<i class="fa fa-download"></i>'
33220 cls : 'btn-group roo-document-viewer-trash',
33224 cls : 'btn btn-default',
33225 html : '<i class="fa fa-trash"></i>'
33238 initEvents : function()
33240 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33241 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33243 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33244 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33246 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33247 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33249 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33250 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33252 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33253 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33255 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33256 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33258 this.bodyEl.on('click', this.onClick, this);
33259 this.downloadBtn.on('click', this.onDownload, this);
33260 this.trashBtn.on('click', this.onTrash, this);
33262 this.downloadBtn.hide();
33263 this.trashBtn.hide();
33265 if(this.showDownload){
33266 this.downloadBtn.show();
33269 if(this.showTrash){
33270 this.trashBtn.show();
33273 if(!this.showDownload && !this.showTrash) {
33274 this.footerEl.hide();
33279 initial : function()
33281 this.fireEvent('initial', this);
33285 onClick : function(e)
33287 e.preventDefault();
33289 this.fireEvent('click', this);
33292 onDownload : function(e)
33294 e.preventDefault();
33296 this.fireEvent('download', this);
33299 onTrash : function(e)
33301 e.preventDefault();
33303 this.fireEvent('trash', this);
33315 * @class Roo.bootstrap.NavProgressBar
33316 * @extends Roo.bootstrap.Component
33317 * Bootstrap NavProgressBar class
33320 * Create a new nav progress bar
33321 * @param {Object} config The config object
33324 Roo.bootstrap.NavProgressBar = function(config){
33325 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33327 this.bullets = this.bullets || [];
33329 // Roo.bootstrap.NavProgressBar.register(this);
33333 * Fires when the active item changes
33334 * @param {Roo.bootstrap.NavProgressBar} this
33335 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33336 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33343 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33348 getAutoCreate : function()
33350 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33354 cls : 'roo-navigation-bar-group',
33358 cls : 'roo-navigation-top-bar'
33362 cls : 'roo-navigation-bullets-bar',
33366 cls : 'roo-navigation-bar'
33373 cls : 'roo-navigation-bottom-bar'
33383 initEvents: function()
33388 onRender : function(ct, position)
33390 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33392 if(this.bullets.length){
33393 Roo.each(this.bullets, function(b){
33402 addItem : function(cfg)
33404 var item = new Roo.bootstrap.NavProgressItem(cfg);
33406 item.parentId = this.id;
33407 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33410 var top = new Roo.bootstrap.Element({
33412 cls : 'roo-navigation-bar-text'
33415 var bottom = new Roo.bootstrap.Element({
33417 cls : 'roo-navigation-bar-text'
33420 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33421 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33423 var topText = new Roo.bootstrap.Element({
33425 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33428 var bottomText = new Roo.bootstrap.Element({
33430 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33433 topText.onRender(top.el, null);
33434 bottomText.onRender(bottom.el, null);
33437 item.bottomEl = bottom;
33440 this.barItems.push(item);
33445 getActive : function()
33447 var active = false;
33449 Roo.each(this.barItems, function(v){
33451 if (!v.isActive()) {
33463 setActiveItem : function(item)
33467 Roo.each(this.barItems, function(v){
33468 if (v.rid == item.rid) {
33472 if (v.isActive()) {
33473 v.setActive(false);
33478 item.setActive(true);
33480 this.fireEvent('changed', this, item, prev);
33483 getBarItem: function(rid)
33487 Roo.each(this.barItems, function(e) {
33488 if (e.rid != rid) {
33499 indexOfItem : function(item)
33503 Roo.each(this.barItems, function(v, i){
33505 if (v.rid != item.rid) {
33516 setActiveNext : function()
33518 var i = this.indexOfItem(this.getActive());
33520 if (i > this.barItems.length) {
33524 this.setActiveItem(this.barItems[i+1]);
33527 setActivePrev : function()
33529 var i = this.indexOfItem(this.getActive());
33535 this.setActiveItem(this.barItems[i-1]);
33538 format : function()
33540 if(!this.barItems.length){
33544 var width = 100 / this.barItems.length;
33546 Roo.each(this.barItems, function(i){
33547 i.el.setStyle('width', width + '%');
33548 i.topEl.el.setStyle('width', width + '%');
33549 i.bottomEl.el.setStyle('width', width + '%');
33558 * Nav Progress Item
33563 * @class Roo.bootstrap.NavProgressItem
33564 * @extends Roo.bootstrap.Component
33565 * Bootstrap NavProgressItem class
33566 * @cfg {String} rid the reference id
33567 * @cfg {Boolean} active (true|false) Is item active default false
33568 * @cfg {Boolean} disabled (true|false) Is item active default false
33569 * @cfg {String} html
33570 * @cfg {String} position (top|bottom) text position default bottom
33571 * @cfg {String} icon show icon instead of number
33574 * Create a new NavProgressItem
33575 * @param {Object} config The config object
33577 Roo.bootstrap.NavProgressItem = function(config){
33578 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33583 * The raw click event for the entire grid.
33584 * @param {Roo.bootstrap.NavProgressItem} this
33585 * @param {Roo.EventObject} e
33592 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33598 position : 'bottom',
33601 getAutoCreate : function()
33603 var iconCls = 'roo-navigation-bar-item-icon';
33605 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33609 cls: 'roo-navigation-bar-item',
33619 cfg.cls += ' active';
33622 cfg.cls += ' disabled';
33628 disable : function()
33630 this.setDisabled(true);
33633 enable : function()
33635 this.setDisabled(false);
33638 initEvents: function()
33640 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33642 this.iconEl.on('click', this.onClick, this);
33645 onClick : function(e)
33647 e.preventDefault();
33653 if(this.fireEvent('click', this, e) === false){
33657 this.parent().setActiveItem(this);
33660 isActive: function ()
33662 return this.active;
33665 setActive : function(state)
33667 if(this.active == state){
33671 this.active = state;
33674 this.el.addClass('active');
33678 this.el.removeClass('active');
33683 setDisabled : function(state)
33685 if(this.disabled == state){
33689 this.disabled = state;
33692 this.el.addClass('disabled');
33696 this.el.removeClass('disabled');
33699 tooltipEl : function()
33701 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33714 * @class Roo.bootstrap.FieldLabel
33715 * @extends Roo.bootstrap.Component
33716 * Bootstrap FieldLabel class
33717 * @cfg {String} html contents of the element
33718 * @cfg {String} tag tag of the element default label
33719 * @cfg {String} cls class of the element
33720 * @cfg {String} target label target
33721 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33722 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33723 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33724 * @cfg {String} iconTooltip default "This field is required"
33725 * @cfg {String} indicatorpos (left|right) default left
33728 * Create a new FieldLabel
33729 * @param {Object} config The config object
33732 Roo.bootstrap.FieldLabel = function(config){
33733 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33738 * Fires after the field has been marked as invalid.
33739 * @param {Roo.form.FieldLabel} this
33740 * @param {String} msg The validation message
33745 * Fires after the field has been validated with no errors.
33746 * @param {Roo.form.FieldLabel} this
33752 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33759 invalidClass : 'has-warning',
33760 validClass : 'has-success',
33761 iconTooltip : 'This field is required',
33762 indicatorpos : 'left',
33764 getAutoCreate : function(){
33767 if (!this.allowBlank) {
33773 cls : 'roo-bootstrap-field-label ' + this.cls,
33778 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33779 tooltip : this.iconTooltip
33788 if(this.indicatorpos == 'right'){
33791 cls : 'roo-bootstrap-field-label ' + this.cls,
33800 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33801 tooltip : this.iconTooltip
33810 initEvents: function()
33812 Roo.bootstrap.Element.superclass.initEvents.call(this);
33814 this.indicator = this.indicatorEl();
33816 if(this.indicator){
33817 this.indicator.removeClass('visible');
33818 this.indicator.addClass('invisible');
33821 Roo.bootstrap.FieldLabel.register(this);
33824 indicatorEl : function()
33826 var indicator = this.el.select('i.roo-required-indicator',true).first();
33837 * Mark this field as valid
33839 markValid : function()
33841 if(this.indicator){
33842 this.indicator.removeClass('visible');
33843 this.indicator.addClass('invisible');
33845 if (Roo.bootstrap.version == 3) {
33846 this.el.removeClass(this.invalidClass);
33847 this.el.addClass(this.validClass);
33849 this.el.removeClass('is-invalid');
33850 this.el.addClass('is-valid');
33854 this.fireEvent('valid', this);
33858 * Mark this field as invalid
33859 * @param {String} msg The validation message
33861 markInvalid : function(msg)
33863 if(this.indicator){
33864 this.indicator.removeClass('invisible');
33865 this.indicator.addClass('visible');
33867 if (Roo.bootstrap.version == 3) {
33868 this.el.removeClass(this.validClass);
33869 this.el.addClass(this.invalidClass);
33871 this.el.removeClass('is-valid');
33872 this.el.addClass('is-invalid');
33876 this.fireEvent('invalid', this, msg);
33882 Roo.apply(Roo.bootstrap.FieldLabel, {
33887 * register a FieldLabel Group
33888 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33890 register : function(label)
33892 if(this.groups.hasOwnProperty(label.target)){
33896 this.groups[label.target] = label;
33900 * fetch a FieldLabel Group based on the target
33901 * @param {string} target
33902 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33904 get: function(target) {
33905 if (typeof(this.groups[target]) == 'undefined') {
33909 return this.groups[target] ;
33918 * page DateSplitField.
33924 * @class Roo.bootstrap.DateSplitField
33925 * @extends Roo.bootstrap.Component
33926 * Bootstrap DateSplitField class
33927 * @cfg {string} fieldLabel - the label associated
33928 * @cfg {Number} labelWidth set the width of label (0-12)
33929 * @cfg {String} labelAlign (top|left)
33930 * @cfg {Boolean} dayAllowBlank (true|false) default false
33931 * @cfg {Boolean} monthAllowBlank (true|false) default false
33932 * @cfg {Boolean} yearAllowBlank (true|false) default false
33933 * @cfg {string} dayPlaceholder
33934 * @cfg {string} monthPlaceholder
33935 * @cfg {string} yearPlaceholder
33936 * @cfg {string} dayFormat default 'd'
33937 * @cfg {string} monthFormat default 'm'
33938 * @cfg {string} yearFormat default 'Y'
33939 * @cfg {Number} labellg set the width of label (1-12)
33940 * @cfg {Number} labelmd set the width of label (1-12)
33941 * @cfg {Number} labelsm set the width of label (1-12)
33942 * @cfg {Number} labelxs set the width of label (1-12)
33946 * Create a new DateSplitField
33947 * @param {Object} config The config object
33950 Roo.bootstrap.DateSplitField = function(config){
33951 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33957 * getting the data of years
33958 * @param {Roo.bootstrap.DateSplitField} this
33959 * @param {Object} years
33964 * getting the data of days
33965 * @param {Roo.bootstrap.DateSplitField} this
33966 * @param {Object} days
33971 * Fires after the field has been marked as invalid.
33972 * @param {Roo.form.Field} this
33973 * @param {String} msg The validation message
33978 * Fires after the field has been validated with no errors.
33979 * @param {Roo.form.Field} this
33985 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33988 labelAlign : 'top',
33990 dayAllowBlank : false,
33991 monthAllowBlank : false,
33992 yearAllowBlank : false,
33993 dayPlaceholder : '',
33994 monthPlaceholder : '',
33995 yearPlaceholder : '',
33999 isFormField : true,
34005 getAutoCreate : function()
34009 cls : 'row roo-date-split-field-group',
34014 cls : 'form-hidden-field roo-date-split-field-group-value',
34020 var labelCls = 'col-md-12';
34021 var contentCls = 'col-md-4';
34023 if(this.fieldLabel){
34027 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34031 html : this.fieldLabel
34036 if(this.labelAlign == 'left'){
34038 if(this.labelWidth > 12){
34039 label.style = "width: " + this.labelWidth + 'px';
34042 if(this.labelWidth < 13 && this.labelmd == 0){
34043 this.labelmd = this.labelWidth;
34046 if(this.labellg > 0){
34047 labelCls = ' col-lg-' + this.labellg;
34048 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34051 if(this.labelmd > 0){
34052 labelCls = ' col-md-' + this.labelmd;
34053 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34056 if(this.labelsm > 0){
34057 labelCls = ' col-sm-' + this.labelsm;
34058 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34061 if(this.labelxs > 0){
34062 labelCls = ' col-xs-' + this.labelxs;
34063 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34067 label.cls += ' ' + labelCls;
34069 cfg.cn.push(label);
34072 Roo.each(['day', 'month', 'year'], function(t){
34075 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34082 inputEl: function ()
34084 return this.el.select('.roo-date-split-field-group-value', true).first();
34087 onRender : function(ct, position)
34091 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34093 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34095 this.dayField = new Roo.bootstrap.ComboBox({
34096 allowBlank : this.dayAllowBlank,
34097 alwaysQuery : true,
34098 displayField : 'value',
34101 forceSelection : true,
34103 placeholder : this.dayPlaceholder,
34104 selectOnFocus : true,
34105 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34106 triggerAction : 'all',
34108 valueField : 'value',
34109 store : new Roo.data.SimpleStore({
34110 data : (function() {
34112 _this.fireEvent('days', _this, days);
34115 fields : [ 'value' ]
34118 select : function (_self, record, index)
34120 _this.setValue(_this.getValue());
34125 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34127 this.monthField = new Roo.bootstrap.MonthField({
34128 after : '<i class=\"fa fa-calendar\"></i>',
34129 allowBlank : this.monthAllowBlank,
34130 placeholder : this.monthPlaceholder,
34133 render : function (_self)
34135 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34136 e.preventDefault();
34140 select : function (_self, oldvalue, newvalue)
34142 _this.setValue(_this.getValue());
34147 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34149 this.yearField = new Roo.bootstrap.ComboBox({
34150 allowBlank : this.yearAllowBlank,
34151 alwaysQuery : true,
34152 displayField : 'value',
34155 forceSelection : true,
34157 placeholder : this.yearPlaceholder,
34158 selectOnFocus : true,
34159 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34160 triggerAction : 'all',
34162 valueField : 'value',
34163 store : new Roo.data.SimpleStore({
34164 data : (function() {
34166 _this.fireEvent('years', _this, years);
34169 fields : [ 'value' ]
34172 select : function (_self, record, index)
34174 _this.setValue(_this.getValue());
34179 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34182 setValue : function(v, format)
34184 this.inputEl.dom.value = v;
34186 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34188 var d = Date.parseDate(v, f);
34195 this.setDay(d.format(this.dayFormat));
34196 this.setMonth(d.format(this.monthFormat));
34197 this.setYear(d.format(this.yearFormat));
34204 setDay : function(v)
34206 this.dayField.setValue(v);
34207 this.inputEl.dom.value = this.getValue();
34212 setMonth : function(v)
34214 this.monthField.setValue(v, true);
34215 this.inputEl.dom.value = this.getValue();
34220 setYear : function(v)
34222 this.yearField.setValue(v);
34223 this.inputEl.dom.value = this.getValue();
34228 getDay : function()
34230 return this.dayField.getValue();
34233 getMonth : function()
34235 return this.monthField.getValue();
34238 getYear : function()
34240 return this.yearField.getValue();
34243 getValue : function()
34245 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34247 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34257 this.inputEl.dom.value = '';
34262 validate : function()
34264 var d = this.dayField.validate();
34265 var m = this.monthField.validate();
34266 var y = this.yearField.validate();
34271 (!this.dayAllowBlank && !d) ||
34272 (!this.monthAllowBlank && !m) ||
34273 (!this.yearAllowBlank && !y)
34278 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34287 this.markInvalid();
34292 markValid : function()
34295 var label = this.el.select('label', true).first();
34296 var icon = this.el.select('i.fa-star', true).first();
34302 this.fireEvent('valid', this);
34306 * Mark this field as invalid
34307 * @param {String} msg The validation message
34309 markInvalid : function(msg)
34312 var label = this.el.select('label', true).first();
34313 var icon = this.el.select('i.fa-star', true).first();
34315 if(label && !icon){
34316 this.el.select('.roo-date-split-field-label', true).createChild({
34318 cls : 'text-danger fa fa-lg fa-star',
34319 tooltip : 'This field is required',
34320 style : 'margin-right:5px;'
34324 this.fireEvent('invalid', this, msg);
34327 clearInvalid : function()
34329 var label = this.el.select('label', true).first();
34330 var icon = this.el.select('i.fa-star', true).first();
34336 this.fireEvent('valid', this);
34339 getName: function()
34349 * http://masonry.desandro.com
34351 * The idea is to render all the bricks based on vertical width...
34353 * The original code extends 'outlayer' - we might need to use that....
34359 * @class Roo.bootstrap.LayoutMasonry
34360 * @extends Roo.bootstrap.Component
34361 * Bootstrap Layout Masonry class
34364 * Create a new Element
34365 * @param {Object} config The config object
34368 Roo.bootstrap.LayoutMasonry = function(config){
34370 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34374 Roo.bootstrap.LayoutMasonry.register(this);
34380 * Fire after layout the items
34381 * @param {Roo.bootstrap.LayoutMasonry} this
34382 * @param {Roo.EventObject} e
34389 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34392 * @cfg {Boolean} isLayoutInstant = no animation?
34394 isLayoutInstant : false, // needed?
34397 * @cfg {Number} boxWidth width of the columns
34402 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34407 * @cfg {Number} padWidth padding below box..
34412 * @cfg {Number} gutter gutter width..
34417 * @cfg {Number} maxCols maximum number of columns
34423 * @cfg {Boolean} isAutoInitial defalut true
34425 isAutoInitial : true,
34430 * @cfg {Boolean} isHorizontal defalut false
34432 isHorizontal : false,
34434 currentSize : null,
34440 bricks: null, //CompositeElement
34444 _isLayoutInited : false,
34446 // isAlternative : false, // only use for vertical layout...
34449 * @cfg {Number} alternativePadWidth padding below box..
34451 alternativePadWidth : 50,
34453 selectedBrick : [],
34455 getAutoCreate : function(){
34457 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34461 cls: 'blog-masonary-wrapper ' + this.cls,
34463 cls : 'mas-boxes masonary'
34470 getChildContainer: function( )
34472 if (this.boxesEl) {
34473 return this.boxesEl;
34476 this.boxesEl = this.el.select('.mas-boxes').first();
34478 return this.boxesEl;
34482 initEvents : function()
34486 if(this.isAutoInitial){
34487 Roo.log('hook children rendered');
34488 this.on('childrenrendered', function() {
34489 Roo.log('children rendered');
34495 initial : function()
34497 this.selectedBrick = [];
34499 this.currentSize = this.el.getBox(true);
34501 Roo.EventManager.onWindowResize(this.resize, this);
34503 if(!this.isAutoInitial){
34511 //this.layout.defer(500,this);
34515 resize : function()
34517 var cs = this.el.getBox(true);
34520 this.currentSize.width == cs.width &&
34521 this.currentSize.x == cs.x &&
34522 this.currentSize.height == cs.height &&
34523 this.currentSize.y == cs.y
34525 Roo.log("no change in with or X or Y");
34529 this.currentSize = cs;
34535 layout : function()
34537 this._resetLayout();
34539 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34541 this.layoutItems( isInstant );
34543 this._isLayoutInited = true;
34545 this.fireEvent('layout', this);
34549 _resetLayout : function()
34551 if(this.isHorizontal){
34552 this.horizontalMeasureColumns();
34556 this.verticalMeasureColumns();
34560 verticalMeasureColumns : function()
34562 this.getContainerWidth();
34564 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34565 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34569 var boxWidth = this.boxWidth + this.padWidth;
34571 if(this.containerWidth < this.boxWidth){
34572 boxWidth = this.containerWidth
34575 var containerWidth = this.containerWidth;
34577 var cols = Math.floor(containerWidth / boxWidth);
34579 this.cols = Math.max( cols, 1 );
34581 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34583 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34585 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34587 this.colWidth = boxWidth + avail - this.padWidth;
34589 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34590 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34593 horizontalMeasureColumns : function()
34595 this.getContainerWidth();
34597 var boxWidth = this.boxWidth;
34599 if(this.containerWidth < boxWidth){
34600 boxWidth = this.containerWidth;
34603 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34605 this.el.setHeight(boxWidth);
34609 getContainerWidth : function()
34611 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34614 layoutItems : function( isInstant )
34616 Roo.log(this.bricks);
34618 var items = Roo.apply([], this.bricks);
34620 if(this.isHorizontal){
34621 this._horizontalLayoutItems( items , isInstant );
34625 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34626 // this._verticalAlternativeLayoutItems( items , isInstant );
34630 this._verticalLayoutItems( items , isInstant );
34634 _verticalLayoutItems : function ( items , isInstant)
34636 if ( !items || !items.length ) {
34641 ['xs', 'xs', 'xs', 'tall'],
34642 ['xs', 'xs', 'tall'],
34643 ['xs', 'xs', 'sm'],
34644 ['xs', 'xs', 'xs'],
34650 ['sm', 'xs', 'xs'],
34654 ['tall', 'xs', 'xs', 'xs'],
34655 ['tall', 'xs', 'xs'],
34667 Roo.each(items, function(item, k){
34669 switch (item.size) {
34670 // these layouts take up a full box,
34681 boxes.push([item]);
34704 var filterPattern = function(box, length)
34712 var pattern = box.slice(0, length);
34716 Roo.each(pattern, function(i){
34717 format.push(i.size);
34720 Roo.each(standard, function(s){
34722 if(String(s) != String(format)){
34731 if(!match && length == 1){
34736 filterPattern(box, length - 1);
34740 queue.push(pattern);
34742 box = box.slice(length, box.length);
34744 filterPattern(box, 4);
34750 Roo.each(boxes, function(box, k){
34756 if(box.length == 1){
34761 filterPattern(box, 4);
34765 this._processVerticalLayoutQueue( queue, isInstant );
34769 // _verticalAlternativeLayoutItems : function( items , isInstant )
34771 // if ( !items || !items.length ) {
34775 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34779 _horizontalLayoutItems : function ( items , isInstant)
34781 if ( !items || !items.length || items.length < 3) {
34787 var eItems = items.slice(0, 3);
34789 items = items.slice(3, items.length);
34792 ['xs', 'xs', 'xs', 'wide'],
34793 ['xs', 'xs', 'wide'],
34794 ['xs', 'xs', 'sm'],
34795 ['xs', 'xs', 'xs'],
34801 ['sm', 'xs', 'xs'],
34805 ['wide', 'xs', 'xs', 'xs'],
34806 ['wide', 'xs', 'xs'],
34819 Roo.each(items, function(item, k){
34821 switch (item.size) {
34832 boxes.push([item]);
34856 var filterPattern = function(box, length)
34864 var pattern = box.slice(0, length);
34868 Roo.each(pattern, function(i){
34869 format.push(i.size);
34872 Roo.each(standard, function(s){
34874 if(String(s) != String(format)){
34883 if(!match && length == 1){
34888 filterPattern(box, length - 1);
34892 queue.push(pattern);
34894 box = box.slice(length, box.length);
34896 filterPattern(box, 4);
34902 Roo.each(boxes, function(box, k){
34908 if(box.length == 1){
34913 filterPattern(box, 4);
34920 var pos = this.el.getBox(true);
34924 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34926 var hit_end = false;
34928 Roo.each(queue, function(box){
34932 Roo.each(box, function(b){
34934 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34944 Roo.each(box, function(b){
34946 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34949 mx = Math.max(mx, b.x);
34953 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34957 Roo.each(box, function(b){
34959 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34973 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34976 /** Sets position of item in DOM
34977 * @param {Element} item
34978 * @param {Number} x - horizontal position
34979 * @param {Number} y - vertical position
34980 * @param {Boolean} isInstant - disables transitions
34982 _processVerticalLayoutQueue : function( queue, isInstant )
34984 var pos = this.el.getBox(true);
34989 for (var i = 0; i < this.cols; i++){
34993 Roo.each(queue, function(box, k){
34995 var col = k % this.cols;
34997 Roo.each(box, function(b,kk){
34999 b.el.position('absolute');
35001 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35002 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35004 if(b.size == 'md-left' || b.size == 'md-right'){
35005 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35006 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35009 b.el.setWidth(width);
35010 b.el.setHeight(height);
35012 b.el.select('iframe',true).setSize(width,height);
35016 for (var i = 0; i < this.cols; i++){
35018 if(maxY[i] < maxY[col]){
35023 col = Math.min(col, i);
35027 x = pos.x + col * (this.colWidth + this.padWidth);
35031 var positions = [];
35033 switch (box.length){
35035 positions = this.getVerticalOneBoxColPositions(x, y, box);
35038 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35041 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35044 positions = this.getVerticalFourBoxColPositions(x, y, box);
35050 Roo.each(box, function(b,kk){
35052 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35054 var sz = b.el.getSize();
35056 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35064 for (var i = 0; i < this.cols; i++){
35065 mY = Math.max(mY, maxY[i]);
35068 this.el.setHeight(mY - pos.y);
35072 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35074 // var pos = this.el.getBox(true);
35077 // var maxX = pos.right;
35079 // var maxHeight = 0;
35081 // Roo.each(items, function(item, k){
35085 // item.el.position('absolute');
35087 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35089 // item.el.setWidth(width);
35091 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35093 // item.el.setHeight(height);
35096 // item.el.setXY([x, y], isInstant ? false : true);
35098 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35101 // y = y + height + this.alternativePadWidth;
35103 // maxHeight = maxHeight + height + this.alternativePadWidth;
35107 // this.el.setHeight(maxHeight);
35111 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35113 var pos = this.el.getBox(true);
35118 var maxX = pos.right;
35120 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35122 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35124 Roo.each(queue, function(box, k){
35126 Roo.each(box, function(b, kk){
35128 b.el.position('absolute');
35130 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35131 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35133 if(b.size == 'md-left' || b.size == 'md-right'){
35134 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35135 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35138 b.el.setWidth(width);
35139 b.el.setHeight(height);
35147 var positions = [];
35149 switch (box.length){
35151 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35154 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35157 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35160 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35166 Roo.each(box, function(b,kk){
35168 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35170 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35178 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35180 Roo.each(eItems, function(b,k){
35182 b.size = (k == 0) ? 'sm' : 'xs';
35183 b.x = (k == 0) ? 2 : 1;
35184 b.y = (k == 0) ? 2 : 1;
35186 b.el.position('absolute');
35188 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35190 b.el.setWidth(width);
35192 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35194 b.el.setHeight(height);
35198 var positions = [];
35201 x : maxX - this.unitWidth * 2 - this.gutter,
35206 x : maxX - this.unitWidth,
35207 y : minY + (this.unitWidth + this.gutter) * 2
35211 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35215 Roo.each(eItems, function(b,k){
35217 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35223 getVerticalOneBoxColPositions : function(x, y, box)
35227 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35229 if(box[0].size == 'md-left'){
35233 if(box[0].size == 'md-right'){
35238 x : x + (this.unitWidth + this.gutter) * rand,
35245 getVerticalTwoBoxColPositions : function(x, y, box)
35249 if(box[0].size == 'xs'){
35253 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35257 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35271 x : x + (this.unitWidth + this.gutter) * 2,
35272 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35279 getVerticalThreeBoxColPositions : function(x, y, box)
35283 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35291 x : x + (this.unitWidth + this.gutter) * 1,
35296 x : x + (this.unitWidth + this.gutter) * 2,
35304 if(box[0].size == 'xs' && box[1].size == 'xs'){
35313 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35317 x : x + (this.unitWidth + this.gutter) * 1,
35331 x : x + (this.unitWidth + this.gutter) * 2,
35336 x : x + (this.unitWidth + this.gutter) * 2,
35337 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35344 getVerticalFourBoxColPositions : function(x, y, box)
35348 if(box[0].size == 'xs'){
35357 y : y + (this.unitHeight + this.gutter) * 1
35362 y : y + (this.unitHeight + this.gutter) * 2
35366 x : x + (this.unitWidth + this.gutter) * 1,
35380 x : x + (this.unitWidth + this.gutter) * 2,
35385 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35386 y : y + (this.unitHeight + this.gutter) * 1
35390 x : x + (this.unitWidth + this.gutter) * 2,
35391 y : y + (this.unitWidth + this.gutter) * 2
35398 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35402 if(box[0].size == 'md-left'){
35404 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35411 if(box[0].size == 'md-right'){
35413 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35414 y : minY + (this.unitWidth + this.gutter) * 1
35420 var rand = Math.floor(Math.random() * (4 - box[0].y));
35423 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35424 y : minY + (this.unitWidth + this.gutter) * rand
35431 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35435 if(box[0].size == 'xs'){
35438 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35443 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35444 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35452 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35457 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35458 y : minY + (this.unitWidth + this.gutter) * 2
35465 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35469 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35472 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35477 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35478 y : minY + (this.unitWidth + this.gutter) * 1
35482 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35483 y : minY + (this.unitWidth + this.gutter) * 2
35490 if(box[0].size == 'xs' && box[1].size == 'xs'){
35493 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35498 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35503 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35504 y : minY + (this.unitWidth + this.gutter) * 1
35512 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35517 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35518 y : minY + (this.unitWidth + this.gutter) * 2
35522 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35523 y : minY + (this.unitWidth + this.gutter) * 2
35530 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35534 if(box[0].size == 'xs'){
35537 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35542 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35547 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),
35552 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35553 y : minY + (this.unitWidth + this.gutter) * 1
35561 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35566 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35567 y : minY + (this.unitWidth + this.gutter) * 2
35571 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35572 y : minY + (this.unitWidth + this.gutter) * 2
35576 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),
35577 y : minY + (this.unitWidth + this.gutter) * 2
35585 * remove a Masonry Brick
35586 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35588 removeBrick : function(brick_id)
35594 for (var i = 0; i<this.bricks.length; i++) {
35595 if (this.bricks[i].id == brick_id) {
35596 this.bricks.splice(i,1);
35597 this.el.dom.removeChild(Roo.get(brick_id).dom);
35604 * adds a Masonry Brick
35605 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35607 addBrick : function(cfg)
35609 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35610 //this.register(cn);
35611 cn.parentId = this.id;
35612 cn.render(this.el);
35617 * register a Masonry Brick
35618 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35621 register : function(brick)
35623 this.bricks.push(brick);
35624 brick.masonryId = this.id;
35628 * clear all the Masonry Brick
35630 clearAll : function()
35633 //this.getChildContainer().dom.innerHTML = "";
35634 this.el.dom.innerHTML = '';
35637 getSelected : function()
35639 if (!this.selectedBrick) {
35643 return this.selectedBrick;
35647 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35651 * register a Masonry Layout
35652 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35655 register : function(layout)
35657 this.groups[layout.id] = layout;
35660 * fetch a Masonry Layout based on the masonry layout ID
35661 * @param {string} the masonry layout to add
35662 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35665 get: function(layout_id) {
35666 if (typeof(this.groups[layout_id]) == 'undefined') {
35669 return this.groups[layout_id] ;
35681 * http://masonry.desandro.com
35683 * The idea is to render all the bricks based on vertical width...
35685 * The original code extends 'outlayer' - we might need to use that....
35691 * @class Roo.bootstrap.LayoutMasonryAuto
35692 * @extends Roo.bootstrap.Component
35693 * Bootstrap Layout Masonry class
35696 * Create a new Element
35697 * @param {Object} config The config object
35700 Roo.bootstrap.LayoutMasonryAuto = function(config){
35701 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35704 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35707 * @cfg {Boolean} isFitWidth - resize the width..
35709 isFitWidth : false, // options..
35711 * @cfg {Boolean} isOriginLeft = left align?
35713 isOriginLeft : true,
35715 * @cfg {Boolean} isOriginTop = top align?
35717 isOriginTop : false,
35719 * @cfg {Boolean} isLayoutInstant = no animation?
35721 isLayoutInstant : false, // needed?
35723 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35725 isResizingContainer : true,
35727 * @cfg {Number} columnWidth width of the columns
35733 * @cfg {Number} maxCols maximum number of columns
35738 * @cfg {Number} padHeight padding below box..
35744 * @cfg {Boolean} isAutoInitial defalut true
35747 isAutoInitial : true,
35753 initialColumnWidth : 0,
35754 currentSize : null,
35756 colYs : null, // array.
35763 bricks: null, //CompositeElement
35764 cols : 0, // array?
35765 // element : null, // wrapped now this.el
35766 _isLayoutInited : null,
35769 getAutoCreate : function(){
35773 cls: 'blog-masonary-wrapper ' + this.cls,
35775 cls : 'mas-boxes masonary'
35782 getChildContainer: function( )
35784 if (this.boxesEl) {
35785 return this.boxesEl;
35788 this.boxesEl = this.el.select('.mas-boxes').first();
35790 return this.boxesEl;
35794 initEvents : function()
35798 if(this.isAutoInitial){
35799 Roo.log('hook children rendered');
35800 this.on('childrenrendered', function() {
35801 Roo.log('children rendered');
35808 initial : function()
35810 this.reloadItems();
35812 this.currentSize = this.el.getBox(true);
35814 /// was window resize... - let's see if this works..
35815 Roo.EventManager.onWindowResize(this.resize, this);
35817 if(!this.isAutoInitial){
35822 this.layout.defer(500,this);
35825 reloadItems: function()
35827 this.bricks = this.el.select('.masonry-brick', true);
35829 this.bricks.each(function(b) {
35830 //Roo.log(b.getSize());
35831 if (!b.attr('originalwidth')) {
35832 b.attr('originalwidth', b.getSize().width);
35837 Roo.log(this.bricks.elements.length);
35840 resize : function()
35843 var cs = this.el.getBox(true);
35845 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35846 Roo.log("no change in with or X");
35849 this.currentSize = cs;
35853 layout : function()
35856 this._resetLayout();
35857 //this._manageStamps();
35859 // don't animate first layout
35860 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35861 this.layoutItems( isInstant );
35863 // flag for initalized
35864 this._isLayoutInited = true;
35867 layoutItems : function( isInstant )
35869 //var items = this._getItemsForLayout( this.items );
35870 // original code supports filtering layout items.. we just ignore it..
35872 this._layoutItems( this.bricks , isInstant );
35874 this._postLayout();
35876 _layoutItems : function ( items , isInstant)
35878 //this.fireEvent( 'layout', this, items );
35881 if ( !items || !items.elements.length ) {
35882 // no items, emit event with empty array
35887 items.each(function(item) {
35888 Roo.log("layout item");
35890 // get x/y object from method
35891 var position = this._getItemLayoutPosition( item );
35893 position.item = item;
35894 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35895 queue.push( position );
35898 this._processLayoutQueue( queue );
35900 /** Sets position of item in DOM
35901 * @param {Element} item
35902 * @param {Number} x - horizontal position
35903 * @param {Number} y - vertical position
35904 * @param {Boolean} isInstant - disables transitions
35906 _processLayoutQueue : function( queue )
35908 for ( var i=0, len = queue.length; i < len; i++ ) {
35909 var obj = queue[i];
35910 obj.item.position('absolute');
35911 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35917 * Any logic you want to do after each layout,
35918 * i.e. size the container
35920 _postLayout : function()
35922 this.resizeContainer();
35925 resizeContainer : function()
35927 if ( !this.isResizingContainer ) {
35930 var size = this._getContainerSize();
35932 this.el.setSize(size.width,size.height);
35933 this.boxesEl.setSize(size.width,size.height);
35939 _resetLayout : function()
35941 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35942 this.colWidth = this.el.getWidth();
35943 //this.gutter = this.el.getWidth();
35945 this.measureColumns();
35951 this.colYs.push( 0 );
35957 measureColumns : function()
35959 this.getContainerWidth();
35960 // if columnWidth is 0, default to outerWidth of first item
35961 if ( !this.columnWidth ) {
35962 var firstItem = this.bricks.first();
35963 Roo.log(firstItem);
35964 this.columnWidth = this.containerWidth;
35965 if (firstItem && firstItem.attr('originalwidth') ) {
35966 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35968 // columnWidth fall back to item of first element
35969 Roo.log("set column width?");
35970 this.initialColumnWidth = this.columnWidth ;
35972 // if first elem has no width, default to size of container
35977 if (this.initialColumnWidth) {
35978 this.columnWidth = this.initialColumnWidth;
35983 // column width is fixed at the top - however if container width get's smaller we should
35986 // this bit calcs how man columns..
35988 var columnWidth = this.columnWidth += this.gutter;
35990 // calculate columns
35991 var containerWidth = this.containerWidth + this.gutter;
35993 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35994 // fix rounding errors, typically with gutters
35995 var excess = columnWidth - containerWidth % columnWidth;
35998 // if overshoot is less than a pixel, round up, otherwise floor it
35999 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36000 cols = Math[ mathMethod ]( cols );
36001 this.cols = Math.max( cols, 1 );
36002 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36004 // padding positioning..
36005 var totalColWidth = this.cols * this.columnWidth;
36006 var padavail = this.containerWidth - totalColWidth;
36007 // so for 2 columns - we need 3 'pads'
36009 var padNeeded = (1+this.cols) * this.padWidth;
36011 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36013 this.columnWidth += padExtra
36014 //this.padWidth = Math.floor(padavail / ( this.cols));
36016 // adjust colum width so that padding is fixed??
36018 // we have 3 columns ... total = width * 3
36019 // we have X left over... that should be used by
36021 //if (this.expandC) {
36029 getContainerWidth : function()
36031 /* // container is parent if fit width
36032 var container = this.isFitWidth ? this.element.parentNode : this.element;
36033 // check that this.size and size are there
36034 // IE8 triggers resize on body size change, so they might not be
36036 var size = getSize( container ); //FIXME
36037 this.containerWidth = size && size.innerWidth; //FIXME
36040 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36044 _getItemLayoutPosition : function( item ) // what is item?
36046 // we resize the item to our columnWidth..
36048 item.setWidth(this.columnWidth);
36049 item.autoBoxAdjust = false;
36051 var sz = item.getSize();
36053 // how many columns does this brick span
36054 var remainder = this.containerWidth % this.columnWidth;
36056 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36057 // round if off by 1 pixel, otherwise use ceil
36058 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36059 colSpan = Math.min( colSpan, this.cols );
36061 // normally this should be '1' as we dont' currently allow multi width columns..
36063 var colGroup = this._getColGroup( colSpan );
36064 // get the minimum Y value from the columns
36065 var minimumY = Math.min.apply( Math, colGroup );
36066 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36068 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36070 // position the brick
36072 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36073 y: this.currentSize.y + minimumY + this.padHeight
36077 // apply setHeight to necessary columns
36078 var setHeight = minimumY + sz.height + this.padHeight;
36079 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36081 var setSpan = this.cols + 1 - colGroup.length;
36082 for ( var i = 0; i < setSpan; i++ ) {
36083 this.colYs[ shortColIndex + i ] = setHeight ;
36090 * @param {Number} colSpan - number of columns the element spans
36091 * @returns {Array} colGroup
36093 _getColGroup : function( colSpan )
36095 if ( colSpan < 2 ) {
36096 // if brick spans only one column, use all the column Ys
36101 // how many different places could this brick fit horizontally
36102 var groupCount = this.cols + 1 - colSpan;
36103 // for each group potential horizontal position
36104 for ( var i = 0; i < groupCount; i++ ) {
36105 // make an array of colY values for that one group
36106 var groupColYs = this.colYs.slice( i, i + colSpan );
36107 // and get the max value of the array
36108 colGroup[i] = Math.max.apply( Math, groupColYs );
36113 _manageStamp : function( stamp )
36115 var stampSize = stamp.getSize();
36116 var offset = stamp.getBox();
36117 // get the columns that this stamp affects
36118 var firstX = this.isOriginLeft ? offset.x : offset.right;
36119 var lastX = firstX + stampSize.width;
36120 var firstCol = Math.floor( firstX / this.columnWidth );
36121 firstCol = Math.max( 0, firstCol );
36123 var lastCol = Math.floor( lastX / this.columnWidth );
36124 // lastCol should not go over if multiple of columnWidth #425
36125 lastCol -= lastX % this.columnWidth ? 0 : 1;
36126 lastCol = Math.min( this.cols - 1, lastCol );
36128 // set colYs to bottom of the stamp
36129 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36132 for ( var i = firstCol; i <= lastCol; i++ ) {
36133 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36138 _getContainerSize : function()
36140 this.maxY = Math.max.apply( Math, this.colYs );
36145 if ( this.isFitWidth ) {
36146 size.width = this._getContainerFitWidth();
36152 _getContainerFitWidth : function()
36154 var unusedCols = 0;
36155 // count unused columns
36158 if ( this.colYs[i] !== 0 ) {
36163 // fit container to columns that have been used
36164 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36167 needsResizeLayout : function()
36169 var previousWidth = this.containerWidth;
36170 this.getContainerWidth();
36171 return previousWidth !== this.containerWidth;
36186 * @class Roo.bootstrap.MasonryBrick
36187 * @extends Roo.bootstrap.Component
36188 * Bootstrap MasonryBrick class
36191 * Create a new MasonryBrick
36192 * @param {Object} config The config object
36195 Roo.bootstrap.MasonryBrick = function(config){
36197 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36199 Roo.bootstrap.MasonryBrick.register(this);
36205 * When a MasonryBrick is clcik
36206 * @param {Roo.bootstrap.MasonryBrick} this
36207 * @param {Roo.EventObject} e
36213 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36216 * @cfg {String} title
36220 * @cfg {String} html
36224 * @cfg {String} bgimage
36228 * @cfg {String} videourl
36232 * @cfg {String} cls
36236 * @cfg {String} href
36240 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36245 * @cfg {String} placetitle (center|bottom)
36250 * @cfg {Boolean} isFitContainer defalut true
36252 isFitContainer : true,
36255 * @cfg {Boolean} preventDefault defalut false
36257 preventDefault : false,
36260 * @cfg {Boolean} inverse defalut false
36262 maskInverse : false,
36264 getAutoCreate : function()
36266 if(!this.isFitContainer){
36267 return this.getSplitAutoCreate();
36270 var cls = 'masonry-brick masonry-brick-full';
36272 if(this.href.length){
36273 cls += ' masonry-brick-link';
36276 if(this.bgimage.length){
36277 cls += ' masonry-brick-image';
36280 if(this.maskInverse){
36281 cls += ' mask-inverse';
36284 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36285 cls += ' enable-mask';
36289 cls += ' masonry-' + this.size + '-brick';
36292 if(this.placetitle.length){
36294 switch (this.placetitle) {
36296 cls += ' masonry-center-title';
36299 cls += ' masonry-bottom-title';
36306 if(!this.html.length && !this.bgimage.length){
36307 cls += ' masonry-center-title';
36310 if(!this.html.length && this.bgimage.length){
36311 cls += ' masonry-bottom-title';
36316 cls += ' ' + this.cls;
36320 tag: (this.href.length) ? 'a' : 'div',
36325 cls: 'masonry-brick-mask'
36329 cls: 'masonry-brick-paragraph',
36335 if(this.href.length){
36336 cfg.href = this.href;
36339 var cn = cfg.cn[1].cn;
36341 if(this.title.length){
36344 cls: 'masonry-brick-title',
36349 if(this.html.length){
36352 cls: 'masonry-brick-text',
36357 if (!this.title.length && !this.html.length) {
36358 cfg.cn[1].cls += ' hide';
36361 if(this.bgimage.length){
36364 cls: 'masonry-brick-image-view',
36369 if(this.videourl.length){
36370 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36371 // youtube support only?
36374 cls: 'masonry-brick-image-view',
36377 allowfullscreen : true
36385 getSplitAutoCreate : function()
36387 var cls = 'masonry-brick masonry-brick-split';
36389 if(this.href.length){
36390 cls += ' masonry-brick-link';
36393 if(this.bgimage.length){
36394 cls += ' masonry-brick-image';
36398 cls += ' masonry-' + this.size + '-brick';
36401 switch (this.placetitle) {
36403 cls += ' masonry-center-title';
36406 cls += ' masonry-bottom-title';
36409 if(!this.bgimage.length){
36410 cls += ' masonry-center-title';
36413 if(this.bgimage.length){
36414 cls += ' masonry-bottom-title';
36420 cls += ' ' + this.cls;
36424 tag: (this.href.length) ? 'a' : 'div',
36429 cls: 'masonry-brick-split-head',
36433 cls: 'masonry-brick-paragraph',
36440 cls: 'masonry-brick-split-body',
36446 if(this.href.length){
36447 cfg.href = this.href;
36450 if(this.title.length){
36451 cfg.cn[0].cn[0].cn.push({
36453 cls: 'masonry-brick-title',
36458 if(this.html.length){
36459 cfg.cn[1].cn.push({
36461 cls: 'masonry-brick-text',
36466 if(this.bgimage.length){
36467 cfg.cn[0].cn.push({
36469 cls: 'masonry-brick-image-view',
36474 if(this.videourl.length){
36475 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36476 // youtube support only?
36477 cfg.cn[0].cn.cn.push({
36479 cls: 'masonry-brick-image-view',
36482 allowfullscreen : true
36489 initEvents: function()
36491 switch (this.size) {
36524 this.el.on('touchstart', this.onTouchStart, this);
36525 this.el.on('touchmove', this.onTouchMove, this);
36526 this.el.on('touchend', this.onTouchEnd, this);
36527 this.el.on('contextmenu', this.onContextMenu, this);
36529 this.el.on('mouseenter' ,this.enter, this);
36530 this.el.on('mouseleave', this.leave, this);
36531 this.el.on('click', this.onClick, this);
36534 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36535 this.parent().bricks.push(this);
36540 onClick: function(e, el)
36542 var time = this.endTimer - this.startTimer;
36543 // Roo.log(e.preventDefault());
36546 e.preventDefault();
36551 if(!this.preventDefault){
36555 e.preventDefault();
36557 if (this.activeClass != '') {
36558 this.selectBrick();
36561 this.fireEvent('click', this, e);
36564 enter: function(e, el)
36566 e.preventDefault();
36568 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36572 if(this.bgimage.length && this.html.length){
36573 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36577 leave: function(e, el)
36579 e.preventDefault();
36581 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36585 if(this.bgimage.length && this.html.length){
36586 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36590 onTouchStart: function(e, el)
36592 // e.preventDefault();
36594 this.touchmoved = false;
36596 if(!this.isFitContainer){
36600 if(!this.bgimage.length || !this.html.length){
36604 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36606 this.timer = new Date().getTime();
36610 onTouchMove: function(e, el)
36612 this.touchmoved = true;
36615 onContextMenu : function(e,el)
36617 e.preventDefault();
36618 e.stopPropagation();
36622 onTouchEnd: function(e, el)
36624 // e.preventDefault();
36626 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36633 if(!this.bgimage.length || !this.html.length){
36635 if(this.href.length){
36636 window.location.href = this.href;
36642 if(!this.isFitContainer){
36646 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36648 window.location.href = this.href;
36651 //selection on single brick only
36652 selectBrick : function() {
36654 if (!this.parentId) {
36658 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36659 var index = m.selectedBrick.indexOf(this.id);
36662 m.selectedBrick.splice(index,1);
36663 this.el.removeClass(this.activeClass);
36667 for(var i = 0; i < m.selectedBrick.length; i++) {
36668 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36669 b.el.removeClass(b.activeClass);
36672 m.selectedBrick = [];
36674 m.selectedBrick.push(this.id);
36675 this.el.addClass(this.activeClass);
36679 isSelected : function(){
36680 return this.el.hasClass(this.activeClass);
36685 Roo.apply(Roo.bootstrap.MasonryBrick, {
36688 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36690 * register a Masonry Brick
36691 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36694 register : function(brick)
36696 //this.groups[brick.id] = brick;
36697 this.groups.add(brick.id, brick);
36700 * fetch a masonry brick based on the masonry brick ID
36701 * @param {string} the masonry brick to add
36702 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36705 get: function(brick_id)
36707 // if (typeof(this.groups[brick_id]) == 'undefined') {
36710 // return this.groups[brick_id] ;
36712 if(this.groups.key(brick_id)) {
36713 return this.groups.key(brick_id);
36731 * @class Roo.bootstrap.Brick
36732 * @extends Roo.bootstrap.Component
36733 * Bootstrap Brick class
36736 * Create a new Brick
36737 * @param {Object} config The config object
36740 Roo.bootstrap.Brick = function(config){
36741 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36747 * When a Brick is click
36748 * @param {Roo.bootstrap.Brick} this
36749 * @param {Roo.EventObject} e
36755 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36758 * @cfg {String} title
36762 * @cfg {String} html
36766 * @cfg {String} bgimage
36770 * @cfg {String} cls
36774 * @cfg {String} href
36778 * @cfg {String} video
36782 * @cfg {Boolean} square
36786 getAutoCreate : function()
36788 var cls = 'roo-brick';
36790 if(this.href.length){
36791 cls += ' roo-brick-link';
36794 if(this.bgimage.length){
36795 cls += ' roo-brick-image';
36798 if(!this.html.length && !this.bgimage.length){
36799 cls += ' roo-brick-center-title';
36802 if(!this.html.length && this.bgimage.length){
36803 cls += ' roo-brick-bottom-title';
36807 cls += ' ' + this.cls;
36811 tag: (this.href.length) ? 'a' : 'div',
36816 cls: 'roo-brick-paragraph',
36822 if(this.href.length){
36823 cfg.href = this.href;
36826 var cn = cfg.cn[0].cn;
36828 if(this.title.length){
36831 cls: 'roo-brick-title',
36836 if(this.html.length){
36839 cls: 'roo-brick-text',
36846 if(this.bgimage.length){
36849 cls: 'roo-brick-image-view',
36857 initEvents: function()
36859 if(this.title.length || this.html.length){
36860 this.el.on('mouseenter' ,this.enter, this);
36861 this.el.on('mouseleave', this.leave, this);
36864 Roo.EventManager.onWindowResize(this.resize, this);
36866 if(this.bgimage.length){
36867 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36868 this.imageEl.on('load', this.onImageLoad, this);
36875 onImageLoad : function()
36880 resize : function()
36882 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36884 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36886 if(this.bgimage.length){
36887 var image = this.el.select('.roo-brick-image-view', true).first();
36889 image.setWidth(paragraph.getWidth());
36892 image.setHeight(paragraph.getWidth());
36895 this.el.setHeight(image.getHeight());
36896 paragraph.setHeight(image.getHeight());
36902 enter: function(e, el)
36904 e.preventDefault();
36906 if(this.bgimage.length){
36907 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36908 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36912 leave: function(e, el)
36914 e.preventDefault();
36916 if(this.bgimage.length){
36917 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36918 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36933 * @class Roo.bootstrap.NumberField
36934 * @extends Roo.bootstrap.Input
36935 * Bootstrap NumberField class
36941 * Create a new NumberField
36942 * @param {Object} config The config object
36945 Roo.bootstrap.NumberField = function(config){
36946 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36949 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36952 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36954 allowDecimals : true,
36956 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36958 decimalSeparator : ".",
36960 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36962 decimalPrecision : 2,
36964 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36966 allowNegative : true,
36969 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36973 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36975 minValue : Number.NEGATIVE_INFINITY,
36977 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36979 maxValue : Number.MAX_VALUE,
36981 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36983 minText : "The minimum value for this field is {0}",
36985 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36987 maxText : "The maximum value for this field is {0}",
36989 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36990 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36992 nanText : "{0} is not a valid number",
36994 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36996 thousandsDelimiter : false,
36998 * @cfg {String} valueAlign alignment of value
37000 valueAlign : "left",
37002 getAutoCreate : function()
37004 var hiddenInput = {
37008 cls: 'hidden-number-input'
37012 hiddenInput.name = this.name;
37017 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37019 this.name = hiddenInput.name;
37021 if(cfg.cn.length > 0) {
37022 cfg.cn.push(hiddenInput);
37029 initEvents : function()
37031 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37033 var allowed = "0123456789";
37035 if(this.allowDecimals){
37036 allowed += this.decimalSeparator;
37039 if(this.allowNegative){
37043 if(this.thousandsDelimiter) {
37047 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37049 var keyPress = function(e){
37051 var k = e.getKey();
37053 var c = e.getCharCode();
37056 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37057 allowed.indexOf(String.fromCharCode(c)) === -1
37063 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37067 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37072 this.el.on("keypress", keyPress, this);
37075 validateValue : function(value)
37078 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37082 var num = this.parseValue(value);
37085 this.markInvalid(String.format(this.nanText, value));
37089 if(num < this.minValue){
37090 this.markInvalid(String.format(this.minText, this.minValue));
37094 if(num > this.maxValue){
37095 this.markInvalid(String.format(this.maxText, this.maxValue));
37102 getValue : function()
37104 var v = this.hiddenEl().getValue();
37106 return this.fixPrecision(this.parseValue(v));
37109 parseValue : function(value)
37111 if(this.thousandsDelimiter) {
37113 r = new RegExp(",", "g");
37114 value = value.replace(r, "");
37117 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37118 return isNaN(value) ? '' : value;
37121 fixPrecision : function(value)
37123 if(this.thousandsDelimiter) {
37125 r = new RegExp(",", "g");
37126 value = value.replace(r, "");
37129 var nan = isNaN(value);
37131 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37132 return nan ? '' : value;
37134 return parseFloat(value).toFixed(this.decimalPrecision);
37137 setValue : function(v)
37139 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37145 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37147 this.inputEl().dom.value = (v == '') ? '' :
37148 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37150 if(!this.allowZero && v === '0') {
37151 this.hiddenEl().dom.value = '';
37152 this.inputEl().dom.value = '';
37159 decimalPrecisionFcn : function(v)
37161 return Math.floor(v);
37164 beforeBlur : function()
37166 var v = this.parseValue(this.getRawValue());
37168 if(v || v === 0 || v === ''){
37173 hiddenEl : function()
37175 return this.el.select('input.hidden-number-input',true).first();
37187 * @class Roo.bootstrap.DocumentSlider
37188 * @extends Roo.bootstrap.Component
37189 * Bootstrap DocumentSlider class
37192 * Create a new DocumentViewer
37193 * @param {Object} config The config object
37196 Roo.bootstrap.DocumentSlider = function(config){
37197 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37204 * Fire after initEvent
37205 * @param {Roo.bootstrap.DocumentSlider} this
37210 * Fire after update
37211 * @param {Roo.bootstrap.DocumentSlider} this
37217 * @param {Roo.bootstrap.DocumentSlider} this
37223 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37229 getAutoCreate : function()
37233 cls : 'roo-document-slider',
37237 cls : 'roo-document-slider-header',
37241 cls : 'roo-document-slider-header-title'
37247 cls : 'roo-document-slider-body',
37251 cls : 'roo-document-slider-prev',
37255 cls : 'fa fa-chevron-left'
37261 cls : 'roo-document-slider-thumb',
37265 cls : 'roo-document-slider-image'
37271 cls : 'roo-document-slider-next',
37275 cls : 'fa fa-chevron-right'
37287 initEvents : function()
37289 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37290 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37292 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37293 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37295 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37296 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37298 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37299 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37301 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37302 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37304 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37305 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37307 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37308 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37310 this.thumbEl.on('click', this.onClick, this);
37312 this.prevIndicator.on('click', this.prev, this);
37314 this.nextIndicator.on('click', this.next, this);
37318 initial : function()
37320 if(this.files.length){
37321 this.indicator = 1;
37325 this.fireEvent('initial', this);
37328 update : function()
37330 this.imageEl.attr('src', this.files[this.indicator - 1]);
37332 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37334 this.prevIndicator.show();
37336 if(this.indicator == 1){
37337 this.prevIndicator.hide();
37340 this.nextIndicator.show();
37342 if(this.indicator == this.files.length){
37343 this.nextIndicator.hide();
37346 this.thumbEl.scrollTo('top');
37348 this.fireEvent('update', this);
37351 onClick : function(e)
37353 e.preventDefault();
37355 this.fireEvent('click', this);
37360 e.preventDefault();
37362 this.indicator = Math.max(1, this.indicator - 1);
37369 e.preventDefault();
37371 this.indicator = Math.min(this.files.length, this.indicator + 1);
37385 * @class Roo.bootstrap.RadioSet
37386 * @extends Roo.bootstrap.Input
37387 * Bootstrap RadioSet class
37388 * @cfg {String} indicatorpos (left|right) default left
37389 * @cfg {Boolean} inline (true|false) inline the element (default true)
37390 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37392 * Create a new RadioSet
37393 * @param {Object} config The config object
37396 Roo.bootstrap.RadioSet = function(config){
37398 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37402 Roo.bootstrap.RadioSet.register(this);
37407 * Fires when the element is checked or unchecked.
37408 * @param {Roo.bootstrap.RadioSet} this This radio
37409 * @param {Roo.bootstrap.Radio} item The checked item
37414 * Fires when the element is click.
37415 * @param {Roo.bootstrap.RadioSet} this This radio set
37416 * @param {Roo.bootstrap.Radio} item The checked item
37417 * @param {Roo.EventObject} e The event object
37424 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37432 indicatorpos : 'left',
37434 getAutoCreate : function()
37438 cls : 'roo-radio-set-label',
37442 html : this.fieldLabel
37446 if (Roo.bootstrap.version == 3) {
37449 if(this.indicatorpos == 'left'){
37452 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37453 tooltip : 'This field is required'
37458 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37459 tooltip : 'This field is required'
37465 cls : 'roo-radio-set-items'
37468 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37470 if (align === 'left' && this.fieldLabel.length) {
37473 cls : "roo-radio-set-right",
37479 if(this.labelWidth > 12){
37480 label.style = "width: " + this.labelWidth + 'px';
37483 if(this.labelWidth < 13 && this.labelmd == 0){
37484 this.labelmd = this.labelWidth;
37487 if(this.labellg > 0){
37488 label.cls += ' col-lg-' + this.labellg;
37489 items.cls += ' col-lg-' + (12 - this.labellg);
37492 if(this.labelmd > 0){
37493 label.cls += ' col-md-' + this.labelmd;
37494 items.cls += ' col-md-' + (12 - this.labelmd);
37497 if(this.labelsm > 0){
37498 label.cls += ' col-sm-' + this.labelsm;
37499 items.cls += ' col-sm-' + (12 - this.labelsm);
37502 if(this.labelxs > 0){
37503 label.cls += ' col-xs-' + this.labelxs;
37504 items.cls += ' col-xs-' + (12 - this.labelxs);
37510 cls : 'roo-radio-set',
37514 cls : 'roo-radio-set-input',
37517 value : this.value ? this.value : ''
37524 if(this.weight.length){
37525 cfg.cls += ' roo-radio-' + this.weight;
37529 cfg.cls += ' roo-radio-set-inline';
37533 ['xs','sm','md','lg'].map(function(size){
37534 if (settings[size]) {
37535 cfg.cls += ' col-' + size + '-' + settings[size];
37543 initEvents : function()
37545 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37546 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37548 if(!this.fieldLabel.length){
37549 this.labelEl.hide();
37552 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37553 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37555 this.indicator = this.indicatorEl();
37557 if(this.indicator){
37558 this.indicator.addClass('invisible');
37561 this.originalValue = this.getValue();
37565 inputEl: function ()
37567 return this.el.select('.roo-radio-set-input', true).first();
37570 getChildContainer : function()
37572 return this.itemsEl;
37575 register : function(item)
37577 this.radioes.push(item);
37581 validate : function()
37583 if(this.getVisibilityEl().hasClass('hidden')){
37589 Roo.each(this.radioes, function(i){
37598 if(this.allowBlank) {
37602 if(this.disabled || valid){
37607 this.markInvalid();
37612 markValid : function()
37614 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37615 this.indicatorEl().removeClass('visible');
37616 this.indicatorEl().addClass('invisible');
37620 if (Roo.bootstrap.version == 3) {
37621 this.el.removeClass([this.invalidClass, this.validClass]);
37622 this.el.addClass(this.validClass);
37624 this.el.removeClass(['is-invalid','is-valid']);
37625 this.el.addClass(['is-valid']);
37627 this.fireEvent('valid', this);
37630 markInvalid : function(msg)
37632 if(this.allowBlank || this.disabled){
37636 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37637 this.indicatorEl().removeClass('invisible');
37638 this.indicatorEl().addClass('visible');
37640 if (Roo.bootstrap.version == 3) {
37641 this.el.removeClass([this.invalidClass, this.validClass]);
37642 this.el.addClass(this.invalidClass);
37644 this.el.removeClass(['is-invalid','is-valid']);
37645 this.el.addClass(['is-invalid']);
37648 this.fireEvent('invalid', this, msg);
37652 setValue : function(v, suppressEvent)
37654 if(this.value === v){
37661 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37664 Roo.each(this.radioes, function(i){
37666 i.el.removeClass('checked');
37669 Roo.each(this.radioes, function(i){
37671 if(i.value === v || i.value.toString() === v.toString()){
37673 i.el.addClass('checked');
37675 if(suppressEvent !== true){
37676 this.fireEvent('check', this, i);
37687 clearInvalid : function(){
37689 if(!this.el || this.preventMark){
37693 this.el.removeClass([this.invalidClass]);
37695 this.fireEvent('valid', this);
37700 Roo.apply(Roo.bootstrap.RadioSet, {
37704 register : function(set)
37706 this.groups[set.name] = set;
37709 get: function(name)
37711 if (typeof(this.groups[name]) == 'undefined') {
37715 return this.groups[name] ;
37721 * Ext JS Library 1.1.1
37722 * Copyright(c) 2006-2007, Ext JS, LLC.
37724 * Originally Released Under LGPL - original licence link has changed is not relivant.
37727 * <script type="text/javascript">
37732 * @class Roo.bootstrap.SplitBar
37733 * @extends Roo.util.Observable
37734 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37738 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37739 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37740 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37741 split.minSize = 100;
37742 split.maxSize = 600;
37743 split.animate = true;
37744 split.on('moved', splitterMoved);
37747 * Create a new SplitBar
37748 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37749 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37750 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37751 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37752 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37753 position of the SplitBar).
37755 Roo.bootstrap.SplitBar = function(cfg){
37760 // dragElement : elm
37761 // resizingElement: el,
37763 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37764 // placement : Roo.bootstrap.SplitBar.LEFT ,
37765 // existingProxy ???
37768 this.el = Roo.get(cfg.dragElement, true);
37769 this.el.dom.unselectable = "on";
37771 this.resizingEl = Roo.get(cfg.resizingElement, true);
37775 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37776 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37779 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37782 * The minimum size of the resizing element. (Defaults to 0)
37788 * The maximum size of the resizing element. (Defaults to 2000)
37791 this.maxSize = 2000;
37794 * Whether to animate the transition to the new size
37797 this.animate = false;
37800 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37803 this.useShim = false;
37808 if(!cfg.existingProxy){
37810 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37812 this.proxy = Roo.get(cfg.existingProxy).dom;
37815 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37818 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37821 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37824 this.dragSpecs = {};
37827 * @private The adapter to use to positon and resize elements
37829 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37830 this.adapter.init(this);
37832 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37834 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37835 this.el.addClass("roo-splitbar-h");
37838 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37839 this.el.addClass("roo-splitbar-v");
37845 * Fires when the splitter is moved (alias for {@link #event-moved})
37846 * @param {Roo.bootstrap.SplitBar} this
37847 * @param {Number} newSize the new width or height
37852 * Fires when the splitter is moved
37853 * @param {Roo.bootstrap.SplitBar} this
37854 * @param {Number} newSize the new width or height
37858 * @event beforeresize
37859 * Fires before the splitter is dragged
37860 * @param {Roo.bootstrap.SplitBar} this
37862 "beforeresize" : true,
37864 "beforeapply" : true
37867 Roo.util.Observable.call(this);
37870 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37871 onStartProxyDrag : function(x, y){
37872 this.fireEvent("beforeresize", this);
37874 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37876 o.enableDisplayMode("block");
37877 // all splitbars share the same overlay
37878 Roo.bootstrap.SplitBar.prototype.overlay = o;
37880 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37881 this.overlay.show();
37882 Roo.get(this.proxy).setDisplayed("block");
37883 var size = this.adapter.getElementSize(this);
37884 this.activeMinSize = this.getMinimumSize();;
37885 this.activeMaxSize = this.getMaximumSize();;
37886 var c1 = size - this.activeMinSize;
37887 var c2 = Math.max(this.activeMaxSize - size, 0);
37888 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37889 this.dd.resetConstraints();
37890 this.dd.setXConstraint(
37891 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37892 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37894 this.dd.setYConstraint(0, 0);
37896 this.dd.resetConstraints();
37897 this.dd.setXConstraint(0, 0);
37898 this.dd.setYConstraint(
37899 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37900 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37903 this.dragSpecs.startSize = size;
37904 this.dragSpecs.startPoint = [x, y];
37905 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37909 * @private Called after the drag operation by the DDProxy
37911 onEndProxyDrag : function(e){
37912 Roo.get(this.proxy).setDisplayed(false);
37913 var endPoint = Roo.lib.Event.getXY(e);
37915 this.overlay.hide();
37918 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37919 newSize = this.dragSpecs.startSize +
37920 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37921 endPoint[0] - this.dragSpecs.startPoint[0] :
37922 this.dragSpecs.startPoint[0] - endPoint[0]
37925 newSize = this.dragSpecs.startSize +
37926 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37927 endPoint[1] - this.dragSpecs.startPoint[1] :
37928 this.dragSpecs.startPoint[1] - endPoint[1]
37931 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37932 if(newSize != this.dragSpecs.startSize){
37933 if(this.fireEvent('beforeapply', this, newSize) !== false){
37934 this.adapter.setElementSize(this, newSize);
37935 this.fireEvent("moved", this, newSize);
37936 this.fireEvent("resize", this, newSize);
37942 * Get the adapter this SplitBar uses
37943 * @return The adapter object
37945 getAdapter : function(){
37946 return this.adapter;
37950 * Set the adapter this SplitBar uses
37951 * @param {Object} adapter A SplitBar adapter object
37953 setAdapter : function(adapter){
37954 this.adapter = adapter;
37955 this.adapter.init(this);
37959 * Gets the minimum size for the resizing element
37960 * @return {Number} The minimum size
37962 getMinimumSize : function(){
37963 return this.minSize;
37967 * Sets the minimum size for the resizing element
37968 * @param {Number} minSize The minimum size
37970 setMinimumSize : function(minSize){
37971 this.minSize = minSize;
37975 * Gets the maximum size for the resizing element
37976 * @return {Number} The maximum size
37978 getMaximumSize : function(){
37979 return this.maxSize;
37983 * Sets the maximum size for the resizing element
37984 * @param {Number} maxSize The maximum size
37986 setMaximumSize : function(maxSize){
37987 this.maxSize = maxSize;
37991 * Sets the initialize size for the resizing element
37992 * @param {Number} size The initial size
37994 setCurrentSize : function(size){
37995 var oldAnimate = this.animate;
37996 this.animate = false;
37997 this.adapter.setElementSize(this, size);
37998 this.animate = oldAnimate;
38002 * Destroy this splitbar.
38003 * @param {Boolean} removeEl True to remove the element
38005 destroy : function(removeEl){
38007 this.shim.remove();
38010 this.proxy.parentNode.removeChild(this.proxy);
38018 * @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.
38020 Roo.bootstrap.SplitBar.createProxy = function(dir){
38021 var proxy = new Roo.Element(document.createElement("div"));
38022 proxy.unselectable();
38023 var cls = 'roo-splitbar-proxy';
38024 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38025 document.body.appendChild(proxy.dom);
38030 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38031 * Default Adapter. It assumes the splitter and resizing element are not positioned
38032 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38034 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38037 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38038 // do nothing for now
38039 init : function(s){
38043 * Called before drag operations to get the current size of the resizing element.
38044 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38046 getElementSize : function(s){
38047 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38048 return s.resizingEl.getWidth();
38050 return s.resizingEl.getHeight();
38055 * Called after drag operations to set the size of the resizing element.
38056 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38057 * @param {Number} newSize The new size to set
38058 * @param {Function} onComplete A function to be invoked when resizing is complete
38060 setElementSize : function(s, newSize, onComplete){
38061 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38063 s.resizingEl.setWidth(newSize);
38065 onComplete(s, newSize);
38068 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38073 s.resizingEl.setHeight(newSize);
38075 onComplete(s, newSize);
38078 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38085 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38086 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38087 * Adapter that moves the splitter element to align with the resized sizing element.
38088 * Used with an absolute positioned SplitBar.
38089 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38090 * document.body, make sure you assign an id to the body element.
38092 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38093 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38094 this.container = Roo.get(container);
38097 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38098 init : function(s){
38099 this.basic.init(s);
38102 getElementSize : function(s){
38103 return this.basic.getElementSize(s);
38106 setElementSize : function(s, newSize, onComplete){
38107 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38110 moveSplitter : function(s){
38111 var yes = Roo.bootstrap.SplitBar;
38112 switch(s.placement){
38114 s.el.setX(s.resizingEl.getRight());
38117 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38120 s.el.setY(s.resizingEl.getBottom());
38123 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38130 * Orientation constant - Create a vertical SplitBar
38134 Roo.bootstrap.SplitBar.VERTICAL = 1;
38137 * Orientation constant - Create a horizontal SplitBar
38141 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38144 * Placement constant - The resizing element is to the left of the splitter element
38148 Roo.bootstrap.SplitBar.LEFT = 1;
38151 * Placement constant - The resizing element is to the right of the splitter element
38155 Roo.bootstrap.SplitBar.RIGHT = 2;
38158 * Placement constant - The resizing element is positioned above the splitter element
38162 Roo.bootstrap.SplitBar.TOP = 3;
38165 * Placement constant - The resizing element is positioned under splitter element
38169 Roo.bootstrap.SplitBar.BOTTOM = 4;
38170 Roo.namespace("Roo.bootstrap.layout");/*
38172 * Ext JS Library 1.1.1
38173 * Copyright(c) 2006-2007, Ext JS, LLC.
38175 * Originally Released Under LGPL - original licence link has changed is not relivant.
38178 * <script type="text/javascript">
38182 * @class Roo.bootstrap.layout.Manager
38183 * @extends Roo.bootstrap.Component
38184 * Base class for layout managers.
38186 Roo.bootstrap.layout.Manager = function(config)
38188 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38194 /** false to disable window resize monitoring @type Boolean */
38195 this.monitorWindowResize = true;
38200 * Fires when a layout is performed.
38201 * @param {Roo.LayoutManager} this
38205 * @event regionresized
38206 * Fires when the user resizes a region.
38207 * @param {Roo.LayoutRegion} region The resized region
38208 * @param {Number} newSize The new size (width for east/west, height for north/south)
38210 "regionresized" : true,
38212 * @event regioncollapsed
38213 * Fires when a region is collapsed.
38214 * @param {Roo.LayoutRegion} region The collapsed region
38216 "regioncollapsed" : true,
38218 * @event regionexpanded
38219 * Fires when a region is expanded.
38220 * @param {Roo.LayoutRegion} region The expanded region
38222 "regionexpanded" : true
38224 this.updating = false;
38227 this.el = Roo.get(config.el);
38233 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38238 monitorWindowResize : true,
38244 onRender : function(ct, position)
38247 this.el = Roo.get(ct);
38250 //this.fireEvent('render',this);
38254 initEvents: function()
38258 // ie scrollbar fix
38259 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38260 document.body.scroll = "no";
38261 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38262 this.el.position('relative');
38264 this.id = this.el.id;
38265 this.el.addClass("roo-layout-container");
38266 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38267 if(this.el.dom != document.body ) {
38268 this.el.on('resize', this.layout,this);
38269 this.el.on('show', this.layout,this);
38275 * Returns true if this layout is currently being updated
38276 * @return {Boolean}
38278 isUpdating : function(){
38279 return this.updating;
38283 * Suspend the LayoutManager from doing auto-layouts while
38284 * making multiple add or remove calls
38286 beginUpdate : function(){
38287 this.updating = true;
38291 * Restore auto-layouts and optionally disable the manager from performing a layout
38292 * @param {Boolean} noLayout true to disable a layout update
38294 endUpdate : function(noLayout){
38295 this.updating = false;
38301 layout: function(){
38305 onRegionResized : function(region, newSize){
38306 this.fireEvent("regionresized", region, newSize);
38310 onRegionCollapsed : function(region){
38311 this.fireEvent("regioncollapsed", region);
38314 onRegionExpanded : function(region){
38315 this.fireEvent("regionexpanded", region);
38319 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38320 * performs box-model adjustments.
38321 * @return {Object} The size as an object {width: (the width), height: (the height)}
38323 getViewSize : function()
38326 if(this.el.dom != document.body){
38327 size = this.el.getSize();
38329 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38331 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38332 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38337 * Returns the Element this layout is bound to.
38338 * @return {Roo.Element}
38340 getEl : function(){
38345 * Returns the specified region.
38346 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38347 * @return {Roo.LayoutRegion}
38349 getRegion : function(target){
38350 return this.regions[target.toLowerCase()];
38353 onWindowResize : function(){
38354 if(this.monitorWindowResize){
38361 * Ext JS Library 1.1.1
38362 * Copyright(c) 2006-2007, Ext JS, LLC.
38364 * Originally Released Under LGPL - original licence link has changed is not relivant.
38367 * <script type="text/javascript">
38370 * @class Roo.bootstrap.layout.Border
38371 * @extends Roo.bootstrap.layout.Manager
38372 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38373 * please see: examples/bootstrap/nested.html<br><br>
38375 <b>The container the layout is rendered into can be either the body element or any other element.
38376 If it is not the body element, the container needs to either be an absolute positioned element,
38377 or you will need to add "position:relative" to the css of the container. You will also need to specify
38378 the container size if it is not the body element.</b>
38381 * Create a new Border
38382 * @param {Object} config Configuration options
38384 Roo.bootstrap.layout.Border = function(config){
38385 config = config || {};
38386 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38390 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38391 if(config[region]){
38392 config[region].region = region;
38393 this.addRegion(config[region]);
38399 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38401 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38403 parent : false, // this might point to a 'nest' or a ???
38406 * Creates and adds a new region if it doesn't already exist.
38407 * @param {String} target The target region key (north, south, east, west or center).
38408 * @param {Object} config The regions config object
38409 * @return {BorderLayoutRegion} The new region
38411 addRegion : function(config)
38413 if(!this.regions[config.region]){
38414 var r = this.factory(config);
38415 this.bindRegion(r);
38417 return this.regions[config.region];
38421 bindRegion : function(r){
38422 this.regions[r.config.region] = r;
38424 r.on("visibilitychange", this.layout, this);
38425 r.on("paneladded", this.layout, this);
38426 r.on("panelremoved", this.layout, this);
38427 r.on("invalidated", this.layout, this);
38428 r.on("resized", this.onRegionResized, this);
38429 r.on("collapsed", this.onRegionCollapsed, this);
38430 r.on("expanded", this.onRegionExpanded, this);
38434 * Performs a layout update.
38436 layout : function()
38438 if(this.updating) {
38442 // render all the rebions if they have not been done alreayd?
38443 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38444 if(this.regions[region] && !this.regions[region].bodyEl){
38445 this.regions[region].onRender(this.el)
38449 var size = this.getViewSize();
38450 var w = size.width;
38451 var h = size.height;
38456 //var x = 0, y = 0;
38458 var rs = this.regions;
38459 var north = rs["north"];
38460 var south = rs["south"];
38461 var west = rs["west"];
38462 var east = rs["east"];
38463 var center = rs["center"];
38464 //if(this.hideOnLayout){ // not supported anymore
38465 //c.el.setStyle("display", "none");
38467 if(north && north.isVisible()){
38468 var b = north.getBox();
38469 var m = north.getMargins();
38470 b.width = w - (m.left+m.right);
38473 centerY = b.height + b.y + m.bottom;
38474 centerH -= centerY;
38475 north.updateBox(this.safeBox(b));
38477 if(south && south.isVisible()){
38478 var b = south.getBox();
38479 var m = south.getMargins();
38480 b.width = w - (m.left+m.right);
38482 var totalHeight = (b.height + m.top + m.bottom);
38483 b.y = h - totalHeight + m.top;
38484 centerH -= totalHeight;
38485 south.updateBox(this.safeBox(b));
38487 if(west && west.isVisible()){
38488 var b = west.getBox();
38489 var m = west.getMargins();
38490 b.height = centerH - (m.top+m.bottom);
38492 b.y = centerY + m.top;
38493 var totalWidth = (b.width + m.left + m.right);
38494 centerX += totalWidth;
38495 centerW -= totalWidth;
38496 west.updateBox(this.safeBox(b));
38498 if(east && east.isVisible()){
38499 var b = east.getBox();
38500 var m = east.getMargins();
38501 b.height = centerH - (m.top+m.bottom);
38502 var totalWidth = (b.width + m.left + m.right);
38503 b.x = w - totalWidth + m.left;
38504 b.y = centerY + m.top;
38505 centerW -= totalWidth;
38506 east.updateBox(this.safeBox(b));
38509 var m = center.getMargins();
38511 x: centerX + m.left,
38512 y: centerY + m.top,
38513 width: centerW - (m.left+m.right),
38514 height: centerH - (m.top+m.bottom)
38516 //if(this.hideOnLayout){
38517 //center.el.setStyle("display", "block");
38519 center.updateBox(this.safeBox(centerBox));
38522 this.fireEvent("layout", this);
38526 safeBox : function(box){
38527 box.width = Math.max(0, box.width);
38528 box.height = Math.max(0, box.height);
38533 * Adds a ContentPanel (or subclass) to this layout.
38534 * @param {String} target The target region key (north, south, east, west or center).
38535 * @param {Roo.ContentPanel} panel The panel to add
38536 * @return {Roo.ContentPanel} The added panel
38538 add : function(target, panel){
38540 target = target.toLowerCase();
38541 return this.regions[target].add(panel);
38545 * Remove a ContentPanel (or subclass) to this layout.
38546 * @param {String} target The target region key (north, south, east, west or center).
38547 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38548 * @return {Roo.ContentPanel} The removed panel
38550 remove : function(target, panel){
38551 target = target.toLowerCase();
38552 return this.regions[target].remove(panel);
38556 * Searches all regions for a panel with the specified id
38557 * @param {String} panelId
38558 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38560 findPanel : function(panelId){
38561 var rs = this.regions;
38562 for(var target in rs){
38563 if(typeof rs[target] != "function"){
38564 var p = rs[target].getPanel(panelId);
38574 * Searches all regions for a panel with the specified id and activates (shows) it.
38575 * @param {String/ContentPanel} panelId The panels id or the panel itself
38576 * @return {Roo.ContentPanel} The shown panel or null
38578 showPanel : function(panelId) {
38579 var rs = this.regions;
38580 for(var target in rs){
38581 var r = rs[target];
38582 if(typeof r != "function"){
38583 if(r.hasPanel(panelId)){
38584 return r.showPanel(panelId);
38592 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38593 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38596 restoreState : function(provider){
38598 provider = Roo.state.Manager;
38600 var sm = new Roo.LayoutStateManager();
38601 sm.init(this, provider);
38607 * Adds a xtype elements to the layout.
38611 xtype : 'ContentPanel',
38618 xtype : 'NestedLayoutPanel',
38624 items : [ ... list of content panels or nested layout panels.. ]
38628 * @param {Object} cfg Xtype definition of item to add.
38630 addxtype : function(cfg)
38632 // basically accepts a pannel...
38633 // can accept a layout region..!?!?
38634 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38637 // theory? children can only be panels??
38639 //if (!cfg.xtype.match(/Panel$/)) {
38644 if (typeof(cfg.region) == 'undefined') {
38645 Roo.log("Failed to add Panel, region was not set");
38649 var region = cfg.region;
38655 xitems = cfg.items;
38660 if ( region == 'center') {
38661 Roo.log("Center: " + cfg.title);
38667 case 'Content': // ContentPanel (el, cfg)
38668 case 'Scroll': // ContentPanel (el, cfg)
38670 cfg.autoCreate = cfg.autoCreate || true;
38671 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38673 // var el = this.el.createChild();
38674 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38677 this.add(region, ret);
38681 case 'TreePanel': // our new panel!
38682 cfg.el = this.el.createChild();
38683 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38684 this.add(region, ret);
38689 // create a new Layout (which is a Border Layout...
38691 var clayout = cfg.layout;
38692 clayout.el = this.el.createChild();
38693 clayout.items = clayout.items || [];
38697 // replace this exitems with the clayout ones..
38698 xitems = clayout.items;
38700 // force background off if it's in center...
38701 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38702 cfg.background = false;
38704 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38707 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38708 //console.log('adding nested layout panel ' + cfg.toSource());
38709 this.add(region, ret);
38710 nb = {}; /// find first...
38715 // needs grid and region
38717 //var el = this.getRegion(region).el.createChild();
38719 *var el = this.el.createChild();
38720 // create the grid first...
38721 cfg.grid.container = el;
38722 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38725 if (region == 'center' && this.active ) {
38726 cfg.background = false;
38729 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38731 this.add(region, ret);
38733 if (cfg.background) {
38734 // render grid on panel activation (if panel background)
38735 ret.on('activate', function(gp) {
38736 if (!gp.grid.rendered) {
38737 // gp.grid.render(el);
38741 // cfg.grid.render(el);
38747 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38748 // it was the old xcomponent building that caused this before.
38749 // espeically if border is the top element in the tree.
38759 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38761 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38762 this.add(region, ret);
38766 throw "Can not add '" + cfg.xtype + "' to Border";
38772 this.beginUpdate();
38776 Roo.each(xitems, function(i) {
38777 region = nb && i.region ? i.region : false;
38779 var add = ret.addxtype(i);
38782 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38783 if (!i.background) {
38784 abn[region] = nb[region] ;
38791 // make the last non-background panel active..
38792 //if (nb) { Roo.log(abn); }
38795 for(var r in abn) {
38796 region = this.getRegion(r);
38798 // tried using nb[r], but it does not work..
38800 region.showPanel(abn[r]);
38811 factory : function(cfg)
38814 var validRegions = Roo.bootstrap.layout.Border.regions;
38816 var target = cfg.region;
38819 var r = Roo.bootstrap.layout;
38823 return new r.North(cfg);
38825 return new r.South(cfg);
38827 return new r.East(cfg);
38829 return new r.West(cfg);
38831 return new r.Center(cfg);
38833 throw 'Layout region "'+target+'" not supported.';
38840 * Ext JS Library 1.1.1
38841 * Copyright(c) 2006-2007, Ext JS, LLC.
38843 * Originally Released Under LGPL - original licence link has changed is not relivant.
38846 * <script type="text/javascript">
38850 * @class Roo.bootstrap.layout.Basic
38851 * @extends Roo.util.Observable
38852 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38853 * and does not have a titlebar, tabs or any other features. All it does is size and position
38854 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38855 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38856 * @cfg {string} region the region that it inhabits..
38857 * @cfg {bool} skipConfig skip config?
38861 Roo.bootstrap.layout.Basic = function(config){
38863 this.mgr = config.mgr;
38865 this.position = config.region;
38867 var skipConfig = config.skipConfig;
38871 * @scope Roo.BasicLayoutRegion
38875 * @event beforeremove
38876 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38877 * @param {Roo.LayoutRegion} this
38878 * @param {Roo.ContentPanel} panel The panel
38879 * @param {Object} e The cancel event object
38881 "beforeremove" : true,
38883 * @event invalidated
38884 * Fires when the layout for this region is changed.
38885 * @param {Roo.LayoutRegion} this
38887 "invalidated" : true,
38889 * @event visibilitychange
38890 * Fires when this region is shown or hidden
38891 * @param {Roo.LayoutRegion} this
38892 * @param {Boolean} visibility true or false
38894 "visibilitychange" : true,
38896 * @event paneladded
38897 * Fires when a panel is added.
38898 * @param {Roo.LayoutRegion} this
38899 * @param {Roo.ContentPanel} panel The panel
38901 "paneladded" : true,
38903 * @event panelremoved
38904 * Fires when a panel is removed.
38905 * @param {Roo.LayoutRegion} this
38906 * @param {Roo.ContentPanel} panel The panel
38908 "panelremoved" : true,
38910 * @event beforecollapse
38911 * Fires when this region before collapse.
38912 * @param {Roo.LayoutRegion} this
38914 "beforecollapse" : true,
38917 * Fires when this region is collapsed.
38918 * @param {Roo.LayoutRegion} this
38920 "collapsed" : true,
38923 * Fires when this region is expanded.
38924 * @param {Roo.LayoutRegion} this
38929 * Fires when this region is slid into view.
38930 * @param {Roo.LayoutRegion} this
38932 "slideshow" : true,
38935 * Fires when this region slides out of view.
38936 * @param {Roo.LayoutRegion} this
38938 "slidehide" : true,
38940 * @event panelactivated
38941 * Fires when a panel is activated.
38942 * @param {Roo.LayoutRegion} this
38943 * @param {Roo.ContentPanel} panel The activated panel
38945 "panelactivated" : true,
38948 * Fires when the user resizes this region.
38949 * @param {Roo.LayoutRegion} this
38950 * @param {Number} newSize The new size (width for east/west, height for north/south)
38954 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38955 this.panels = new Roo.util.MixedCollection();
38956 this.panels.getKey = this.getPanelId.createDelegate(this);
38958 this.activePanel = null;
38959 // ensure listeners are added...
38961 if (config.listeners || config.events) {
38962 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38963 listeners : config.listeners || {},
38964 events : config.events || {}
38968 if(skipConfig !== true){
38969 this.applyConfig(config);
38973 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38975 getPanelId : function(p){
38979 applyConfig : function(config){
38980 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38981 this.config = config;
38986 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38987 * the width, for horizontal (north, south) the height.
38988 * @param {Number} newSize The new width or height
38990 resizeTo : function(newSize){
38991 var el = this.el ? this.el :
38992 (this.activePanel ? this.activePanel.getEl() : null);
38994 switch(this.position){
38997 el.setWidth(newSize);
38998 this.fireEvent("resized", this, newSize);
39002 el.setHeight(newSize);
39003 this.fireEvent("resized", this, newSize);
39009 getBox : function(){
39010 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39013 getMargins : function(){
39014 return this.margins;
39017 updateBox : function(box){
39019 var el = this.activePanel.getEl();
39020 el.dom.style.left = box.x + "px";
39021 el.dom.style.top = box.y + "px";
39022 this.activePanel.setSize(box.width, box.height);
39026 * Returns the container element for this region.
39027 * @return {Roo.Element}
39029 getEl : function(){
39030 return this.activePanel;
39034 * Returns true if this region is currently visible.
39035 * @return {Boolean}
39037 isVisible : function(){
39038 return this.activePanel ? true : false;
39041 setActivePanel : function(panel){
39042 panel = this.getPanel(panel);
39043 if(this.activePanel && this.activePanel != panel){
39044 this.activePanel.setActiveState(false);
39045 this.activePanel.getEl().setLeftTop(-10000,-10000);
39047 this.activePanel = panel;
39048 panel.setActiveState(true);
39050 panel.setSize(this.box.width, this.box.height);
39052 this.fireEvent("panelactivated", this, panel);
39053 this.fireEvent("invalidated");
39057 * Show the specified panel.
39058 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39059 * @return {Roo.ContentPanel} The shown panel or null
39061 showPanel : function(panel){
39062 panel = this.getPanel(panel);
39064 this.setActivePanel(panel);
39070 * Get the active panel for this region.
39071 * @return {Roo.ContentPanel} The active panel or null
39073 getActivePanel : function(){
39074 return this.activePanel;
39078 * Add the passed ContentPanel(s)
39079 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39080 * @return {Roo.ContentPanel} The panel added (if only one was added)
39082 add : function(panel){
39083 if(arguments.length > 1){
39084 for(var i = 0, len = arguments.length; i < len; i++) {
39085 this.add(arguments[i]);
39089 if(this.hasPanel(panel)){
39090 this.showPanel(panel);
39093 var el = panel.getEl();
39094 if(el.dom.parentNode != this.mgr.el.dom){
39095 this.mgr.el.dom.appendChild(el.dom);
39097 if(panel.setRegion){
39098 panel.setRegion(this);
39100 this.panels.add(panel);
39101 el.setStyle("position", "absolute");
39102 if(!panel.background){
39103 this.setActivePanel(panel);
39104 if(this.config.initialSize && this.panels.getCount()==1){
39105 this.resizeTo(this.config.initialSize);
39108 this.fireEvent("paneladded", this, panel);
39113 * Returns true if the panel is in this region.
39114 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39115 * @return {Boolean}
39117 hasPanel : function(panel){
39118 if(typeof panel == "object"){ // must be panel obj
39119 panel = panel.getId();
39121 return this.getPanel(panel) ? true : false;
39125 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39126 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39127 * @param {Boolean} preservePanel Overrides the config preservePanel option
39128 * @return {Roo.ContentPanel} The panel that was removed
39130 remove : function(panel, preservePanel){
39131 panel = this.getPanel(panel);
39136 this.fireEvent("beforeremove", this, panel, e);
39137 if(e.cancel === true){
39140 var panelId = panel.getId();
39141 this.panels.removeKey(panelId);
39146 * Returns the panel specified or null if it's not in this region.
39147 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39148 * @return {Roo.ContentPanel}
39150 getPanel : function(id){
39151 if(typeof id == "object"){ // must be panel obj
39154 return this.panels.get(id);
39158 * Returns this regions position (north/south/east/west/center).
39161 getPosition: function(){
39162 return this.position;
39166 * Ext JS Library 1.1.1
39167 * Copyright(c) 2006-2007, Ext JS, LLC.
39169 * Originally Released Under LGPL - original licence link has changed is not relivant.
39172 * <script type="text/javascript">
39176 * @class Roo.bootstrap.layout.Region
39177 * @extends Roo.bootstrap.layout.Basic
39178 * This class represents a region in a layout manager.
39180 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39181 * @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})
39182 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39183 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39184 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39185 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39186 * @cfg {String} title The title for the region (overrides panel titles)
39187 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39188 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39189 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39190 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39191 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39192 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39193 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39194 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39195 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39196 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39198 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39199 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39200 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39201 * @cfg {Number} width For East/West panels
39202 * @cfg {Number} height For North/South panels
39203 * @cfg {Boolean} split To show the splitter
39204 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39206 * @cfg {string} cls Extra CSS classes to add to region
39208 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39209 * @cfg {string} region the region that it inhabits..
39212 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39213 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39215 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39216 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39217 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39219 Roo.bootstrap.layout.Region = function(config)
39221 this.applyConfig(config);
39223 var mgr = config.mgr;
39224 var pos = config.region;
39225 config.skipConfig = true;
39226 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39229 this.onRender(mgr.el);
39232 this.visible = true;
39233 this.collapsed = false;
39234 this.unrendered_panels = [];
39237 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39239 position: '', // set by wrapper (eg. north/south etc..)
39240 unrendered_panels : null, // unrendered panels.
39242 tabPosition : false,
39244 mgr: false, // points to 'Border'
39247 createBody : function(){
39248 /** This region's body element
39249 * @type Roo.Element */
39250 this.bodyEl = this.el.createChild({
39252 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39256 onRender: function(ctr, pos)
39258 var dh = Roo.DomHelper;
39259 /** This region's container element
39260 * @type Roo.Element */
39261 this.el = dh.append(ctr.dom, {
39263 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39265 /** This region's title element
39266 * @type Roo.Element */
39268 this.titleEl = dh.append(this.el.dom, {
39270 unselectable: "on",
39271 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39273 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39274 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39278 this.titleEl.enableDisplayMode();
39279 /** This region's title text element
39280 * @type HTMLElement */
39281 this.titleTextEl = this.titleEl.dom.firstChild;
39282 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39284 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39285 this.closeBtn.enableDisplayMode();
39286 this.closeBtn.on("click", this.closeClicked, this);
39287 this.closeBtn.hide();
39289 this.createBody(this.config);
39290 if(this.config.hideWhenEmpty){
39292 this.on("paneladded", this.validateVisibility, this);
39293 this.on("panelremoved", this.validateVisibility, this);
39295 if(this.autoScroll){
39296 this.bodyEl.setStyle("overflow", "auto");
39298 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39300 //if(c.titlebar !== false){
39301 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39302 this.titleEl.hide();
39304 this.titleEl.show();
39305 if(this.config.title){
39306 this.titleTextEl.innerHTML = this.config.title;
39310 if(this.config.collapsed){
39311 this.collapse(true);
39313 if(this.config.hidden){
39317 if (this.unrendered_panels && this.unrendered_panels.length) {
39318 for (var i =0;i< this.unrendered_panels.length; i++) {
39319 this.add(this.unrendered_panels[i]);
39321 this.unrendered_panels = null;
39327 applyConfig : function(c)
39330 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39331 var dh = Roo.DomHelper;
39332 if(c.titlebar !== false){
39333 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39334 this.collapseBtn.on("click", this.collapse, this);
39335 this.collapseBtn.enableDisplayMode();
39337 if(c.showPin === true || this.showPin){
39338 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39339 this.stickBtn.enableDisplayMode();
39340 this.stickBtn.on("click", this.expand, this);
39341 this.stickBtn.hide();
39346 /** This region's collapsed element
39347 * @type Roo.Element */
39350 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39351 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39354 if(c.floatable !== false){
39355 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39356 this.collapsedEl.on("click", this.collapseClick, this);
39359 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39360 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39361 id: "message", unselectable: "on", style:{"float":"left"}});
39362 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39364 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39365 this.expandBtn.on("click", this.expand, this);
39369 if(this.collapseBtn){
39370 this.collapseBtn.setVisible(c.collapsible == true);
39373 this.cmargins = c.cmargins || this.cmargins ||
39374 (this.position == "west" || this.position == "east" ?
39375 {top: 0, left: 2, right:2, bottom: 0} :
39376 {top: 2, left: 0, right:0, bottom: 2});
39378 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39381 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39383 this.autoScroll = c.autoScroll || false;
39388 this.duration = c.duration || .30;
39389 this.slideDuration = c.slideDuration || .45;
39394 * Returns true if this region is currently visible.
39395 * @return {Boolean}
39397 isVisible : function(){
39398 return this.visible;
39402 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39403 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39405 //setCollapsedTitle : function(title){
39406 // title = title || " ";
39407 // if(this.collapsedTitleTextEl){
39408 // this.collapsedTitleTextEl.innerHTML = title;
39412 getBox : function(){
39414 // if(!this.collapsed){
39415 b = this.el.getBox(false, true);
39417 // b = this.collapsedEl.getBox(false, true);
39422 getMargins : function(){
39423 return this.margins;
39424 //return this.collapsed ? this.cmargins : this.margins;
39427 highlight : function(){
39428 this.el.addClass("x-layout-panel-dragover");
39431 unhighlight : function(){
39432 this.el.removeClass("x-layout-panel-dragover");
39435 updateBox : function(box)
39437 if (!this.bodyEl) {
39438 return; // not rendered yet..
39442 if(!this.collapsed){
39443 this.el.dom.style.left = box.x + "px";
39444 this.el.dom.style.top = box.y + "px";
39445 this.updateBody(box.width, box.height);
39447 this.collapsedEl.dom.style.left = box.x + "px";
39448 this.collapsedEl.dom.style.top = box.y + "px";
39449 this.collapsedEl.setSize(box.width, box.height);
39452 this.tabs.autoSizeTabs();
39456 updateBody : function(w, h)
39459 this.el.setWidth(w);
39460 w -= this.el.getBorderWidth("rl");
39461 if(this.config.adjustments){
39462 w += this.config.adjustments[0];
39465 if(h !== null && h > 0){
39466 this.el.setHeight(h);
39467 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39468 h -= this.el.getBorderWidth("tb");
39469 if(this.config.adjustments){
39470 h += this.config.adjustments[1];
39472 this.bodyEl.setHeight(h);
39474 h = this.tabs.syncHeight(h);
39477 if(this.panelSize){
39478 w = w !== null ? w : this.panelSize.width;
39479 h = h !== null ? h : this.panelSize.height;
39481 if(this.activePanel){
39482 var el = this.activePanel.getEl();
39483 w = w !== null ? w : el.getWidth();
39484 h = h !== null ? h : el.getHeight();
39485 this.panelSize = {width: w, height: h};
39486 this.activePanel.setSize(w, h);
39488 if(Roo.isIE && this.tabs){
39489 this.tabs.el.repaint();
39494 * Returns the container element for this region.
39495 * @return {Roo.Element}
39497 getEl : function(){
39502 * Hides this region.
39505 //if(!this.collapsed){
39506 this.el.dom.style.left = "-2000px";
39509 // this.collapsedEl.dom.style.left = "-2000px";
39510 // this.collapsedEl.hide();
39512 this.visible = false;
39513 this.fireEvent("visibilitychange", this, false);
39517 * Shows this region if it was previously hidden.
39520 //if(!this.collapsed){
39523 // this.collapsedEl.show();
39525 this.visible = true;
39526 this.fireEvent("visibilitychange", this, true);
39529 closeClicked : function(){
39530 if(this.activePanel){
39531 this.remove(this.activePanel);
39535 collapseClick : function(e){
39537 e.stopPropagation();
39540 e.stopPropagation();
39546 * Collapses this region.
39547 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39550 collapse : function(skipAnim, skipCheck = false){
39551 if(this.collapsed) {
39555 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39557 this.collapsed = true;
39559 this.split.el.hide();
39561 if(this.config.animate && skipAnim !== true){
39562 this.fireEvent("invalidated", this);
39563 this.animateCollapse();
39565 this.el.setLocation(-20000,-20000);
39567 this.collapsedEl.show();
39568 this.fireEvent("collapsed", this);
39569 this.fireEvent("invalidated", this);
39575 animateCollapse : function(){
39580 * Expands this region if it was previously collapsed.
39581 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39582 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39585 expand : function(e, skipAnim){
39587 e.stopPropagation();
39589 if(!this.collapsed || this.el.hasActiveFx()) {
39593 this.afterSlideIn();
39596 this.collapsed = false;
39597 if(this.config.animate && skipAnim !== true){
39598 this.animateExpand();
39602 this.split.el.show();
39604 this.collapsedEl.setLocation(-2000,-2000);
39605 this.collapsedEl.hide();
39606 this.fireEvent("invalidated", this);
39607 this.fireEvent("expanded", this);
39611 animateExpand : function(){
39615 initTabs : function()
39617 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39619 var ts = new Roo.bootstrap.panel.Tabs({
39620 el: this.bodyEl.dom,
39622 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39623 disableTooltips: this.config.disableTabTips,
39624 toolbar : this.config.toolbar
39627 if(this.config.hideTabs){
39628 ts.stripWrap.setDisplayed(false);
39631 ts.resizeTabs = this.config.resizeTabs === true;
39632 ts.minTabWidth = this.config.minTabWidth || 40;
39633 ts.maxTabWidth = this.config.maxTabWidth || 250;
39634 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39635 ts.monitorResize = false;
39636 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39637 ts.bodyEl.addClass('roo-layout-tabs-body');
39638 this.panels.each(this.initPanelAsTab, this);
39641 initPanelAsTab : function(panel){
39642 var ti = this.tabs.addTab(
39646 this.config.closeOnTab && panel.isClosable(),
39649 if(panel.tabTip !== undefined){
39650 ti.setTooltip(panel.tabTip);
39652 ti.on("activate", function(){
39653 this.setActivePanel(panel);
39656 if(this.config.closeOnTab){
39657 ti.on("beforeclose", function(t, e){
39659 this.remove(panel);
39663 panel.tabItem = ti;
39668 updatePanelTitle : function(panel, title)
39670 if(this.activePanel == panel){
39671 this.updateTitle(title);
39674 var ti = this.tabs.getTab(panel.getEl().id);
39676 if(panel.tabTip !== undefined){
39677 ti.setTooltip(panel.tabTip);
39682 updateTitle : function(title){
39683 if(this.titleTextEl && !this.config.title){
39684 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39688 setActivePanel : function(panel)
39690 panel = this.getPanel(panel);
39691 if(this.activePanel && this.activePanel != panel){
39692 if(this.activePanel.setActiveState(false) === false){
39696 this.activePanel = panel;
39697 panel.setActiveState(true);
39698 if(this.panelSize){
39699 panel.setSize(this.panelSize.width, this.panelSize.height);
39702 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39704 this.updateTitle(panel.getTitle());
39706 this.fireEvent("invalidated", this);
39708 this.fireEvent("panelactivated", this, panel);
39712 * Shows the specified panel.
39713 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39714 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39716 showPanel : function(panel)
39718 panel = this.getPanel(panel);
39721 var tab = this.tabs.getTab(panel.getEl().id);
39722 if(tab.isHidden()){
39723 this.tabs.unhideTab(tab.id);
39727 this.setActivePanel(panel);
39734 * Get the active panel for this region.
39735 * @return {Roo.ContentPanel} The active panel or null
39737 getActivePanel : function(){
39738 return this.activePanel;
39741 validateVisibility : function(){
39742 if(this.panels.getCount() < 1){
39743 this.updateTitle(" ");
39744 this.closeBtn.hide();
39747 if(!this.isVisible()){
39754 * Adds the passed ContentPanel(s) to this region.
39755 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39756 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39758 add : function(panel)
39760 if(arguments.length > 1){
39761 for(var i = 0, len = arguments.length; i < len; i++) {
39762 this.add(arguments[i]);
39767 // if we have not been rendered yet, then we can not really do much of this..
39768 if (!this.bodyEl) {
39769 this.unrendered_panels.push(panel);
39776 if(this.hasPanel(panel)){
39777 this.showPanel(panel);
39780 panel.setRegion(this);
39781 this.panels.add(panel);
39782 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39783 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39784 // and hide them... ???
39785 this.bodyEl.dom.appendChild(panel.getEl().dom);
39786 if(panel.background !== true){
39787 this.setActivePanel(panel);
39789 this.fireEvent("paneladded", this, panel);
39796 this.initPanelAsTab(panel);
39800 if(panel.background !== true){
39801 this.tabs.activate(panel.getEl().id);
39803 this.fireEvent("paneladded", this, panel);
39808 * Hides the tab for the specified panel.
39809 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39811 hidePanel : function(panel){
39812 if(this.tabs && (panel = this.getPanel(panel))){
39813 this.tabs.hideTab(panel.getEl().id);
39818 * Unhides the tab for a previously hidden panel.
39819 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39821 unhidePanel : function(panel){
39822 if(this.tabs && (panel = this.getPanel(panel))){
39823 this.tabs.unhideTab(panel.getEl().id);
39827 clearPanels : function(){
39828 while(this.panels.getCount() > 0){
39829 this.remove(this.panels.first());
39834 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39835 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39836 * @param {Boolean} preservePanel Overrides the config preservePanel option
39837 * @return {Roo.ContentPanel} The panel that was removed
39839 remove : function(panel, preservePanel)
39841 panel = this.getPanel(panel);
39846 this.fireEvent("beforeremove", this, panel, e);
39847 if(e.cancel === true){
39850 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39851 var panelId = panel.getId();
39852 this.panels.removeKey(panelId);
39854 document.body.appendChild(panel.getEl().dom);
39857 this.tabs.removeTab(panel.getEl().id);
39858 }else if (!preservePanel){
39859 this.bodyEl.dom.removeChild(panel.getEl().dom);
39861 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39862 var p = this.panels.first();
39863 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39864 tempEl.appendChild(p.getEl().dom);
39865 this.bodyEl.update("");
39866 this.bodyEl.dom.appendChild(p.getEl().dom);
39868 this.updateTitle(p.getTitle());
39870 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39871 this.setActivePanel(p);
39873 panel.setRegion(null);
39874 if(this.activePanel == panel){
39875 this.activePanel = null;
39877 if(this.config.autoDestroy !== false && preservePanel !== true){
39878 try{panel.destroy();}catch(e){}
39880 this.fireEvent("panelremoved", this, panel);
39885 * Returns the TabPanel component used by this region
39886 * @return {Roo.TabPanel}
39888 getTabs : function(){
39892 createTool : function(parentEl, className){
39893 var btn = Roo.DomHelper.append(parentEl, {
39895 cls: "x-layout-tools-button",
39898 cls: "roo-layout-tools-button-inner " + className,
39902 btn.addClassOnOver("roo-layout-tools-button-over");
39907 * Ext JS Library 1.1.1
39908 * Copyright(c) 2006-2007, Ext JS, LLC.
39910 * Originally Released Under LGPL - original licence link has changed is not relivant.
39913 * <script type="text/javascript">
39919 * @class Roo.SplitLayoutRegion
39920 * @extends Roo.LayoutRegion
39921 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39923 Roo.bootstrap.layout.Split = function(config){
39924 this.cursor = config.cursor;
39925 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39928 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39930 splitTip : "Drag to resize.",
39931 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39932 useSplitTips : false,
39934 applyConfig : function(config){
39935 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39938 onRender : function(ctr,pos) {
39940 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39941 if(!this.config.split){
39946 var splitEl = Roo.DomHelper.append(ctr.dom, {
39948 id: this.el.id + "-split",
39949 cls: "roo-layout-split roo-layout-split-"+this.position,
39952 /** The SplitBar for this region
39953 * @type Roo.SplitBar */
39954 // does not exist yet...
39955 Roo.log([this.position, this.orientation]);
39957 this.split = new Roo.bootstrap.SplitBar({
39958 dragElement : splitEl,
39959 resizingElement: this.el,
39960 orientation : this.orientation
39963 this.split.on("moved", this.onSplitMove, this);
39964 this.split.useShim = this.config.useShim === true;
39965 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39966 if(this.useSplitTips){
39967 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39969 //if(config.collapsible){
39970 // this.split.el.on("dblclick", this.collapse, this);
39973 if(typeof this.config.minSize != "undefined"){
39974 this.split.minSize = this.config.minSize;
39976 if(typeof this.config.maxSize != "undefined"){
39977 this.split.maxSize = this.config.maxSize;
39979 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39980 this.hideSplitter();
39985 getHMaxSize : function(){
39986 var cmax = this.config.maxSize || 10000;
39987 var center = this.mgr.getRegion("center");
39988 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39991 getVMaxSize : function(){
39992 var cmax = this.config.maxSize || 10000;
39993 var center = this.mgr.getRegion("center");
39994 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39997 onSplitMove : function(split, newSize){
39998 this.fireEvent("resized", this, newSize);
40002 * Returns the {@link Roo.SplitBar} for this region.
40003 * @return {Roo.SplitBar}
40005 getSplitBar : function(){
40010 this.hideSplitter();
40011 Roo.bootstrap.layout.Split.superclass.hide.call(this);
40014 hideSplitter : function(){
40016 this.split.el.setLocation(-2000,-2000);
40017 this.split.el.hide();
40023 this.split.el.show();
40025 Roo.bootstrap.layout.Split.superclass.show.call(this);
40028 beforeSlide: function(){
40029 if(Roo.isGecko){// firefox overflow auto bug workaround
40030 this.bodyEl.clip();
40032 this.tabs.bodyEl.clip();
40034 if(this.activePanel){
40035 this.activePanel.getEl().clip();
40037 if(this.activePanel.beforeSlide){
40038 this.activePanel.beforeSlide();
40044 afterSlide : function(){
40045 if(Roo.isGecko){// firefox overflow auto bug workaround
40046 this.bodyEl.unclip();
40048 this.tabs.bodyEl.unclip();
40050 if(this.activePanel){
40051 this.activePanel.getEl().unclip();
40052 if(this.activePanel.afterSlide){
40053 this.activePanel.afterSlide();
40059 initAutoHide : function(){
40060 if(this.autoHide !== false){
40061 if(!this.autoHideHd){
40062 var st = new Roo.util.DelayedTask(this.slideIn, this);
40063 this.autoHideHd = {
40064 "mouseout": function(e){
40065 if(!e.within(this.el, true)){
40069 "mouseover" : function(e){
40075 this.el.on(this.autoHideHd);
40079 clearAutoHide : function(){
40080 if(this.autoHide !== false){
40081 this.el.un("mouseout", this.autoHideHd.mouseout);
40082 this.el.un("mouseover", this.autoHideHd.mouseover);
40086 clearMonitor : function(){
40087 Roo.get(document).un("click", this.slideInIf, this);
40090 // these names are backwards but not changed for compat
40091 slideOut : function(){
40092 if(this.isSlid || this.el.hasActiveFx()){
40095 this.isSlid = true;
40096 if(this.collapseBtn){
40097 this.collapseBtn.hide();
40099 this.closeBtnState = this.closeBtn.getStyle('display');
40100 this.closeBtn.hide();
40102 this.stickBtn.show();
40105 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40106 this.beforeSlide();
40107 this.el.setStyle("z-index", 10001);
40108 this.el.slideIn(this.getSlideAnchor(), {
40109 callback: function(){
40111 this.initAutoHide();
40112 Roo.get(document).on("click", this.slideInIf, this);
40113 this.fireEvent("slideshow", this);
40120 afterSlideIn : function(){
40121 this.clearAutoHide();
40122 this.isSlid = false;
40123 this.clearMonitor();
40124 this.el.setStyle("z-index", "");
40125 if(this.collapseBtn){
40126 this.collapseBtn.show();
40128 this.closeBtn.setStyle('display', this.closeBtnState);
40130 this.stickBtn.hide();
40132 this.fireEvent("slidehide", this);
40135 slideIn : function(cb){
40136 if(!this.isSlid || this.el.hasActiveFx()){
40140 this.isSlid = false;
40141 this.beforeSlide();
40142 this.el.slideOut(this.getSlideAnchor(), {
40143 callback: function(){
40144 this.el.setLeftTop(-10000, -10000);
40146 this.afterSlideIn();
40154 slideInIf : function(e){
40155 if(!e.within(this.el)){
40160 animateCollapse : function(){
40161 this.beforeSlide();
40162 this.el.setStyle("z-index", 20000);
40163 var anchor = this.getSlideAnchor();
40164 this.el.slideOut(anchor, {
40165 callback : function(){
40166 this.el.setStyle("z-index", "");
40167 this.collapsedEl.slideIn(anchor, {duration:.3});
40169 this.el.setLocation(-10000,-10000);
40171 this.fireEvent("collapsed", this);
40178 animateExpand : function(){
40179 this.beforeSlide();
40180 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40181 this.el.setStyle("z-index", 20000);
40182 this.collapsedEl.hide({
40185 this.el.slideIn(this.getSlideAnchor(), {
40186 callback : function(){
40187 this.el.setStyle("z-index", "");
40190 this.split.el.show();
40192 this.fireEvent("invalidated", this);
40193 this.fireEvent("expanded", this);
40221 getAnchor : function(){
40222 return this.anchors[this.position];
40225 getCollapseAnchor : function(){
40226 return this.canchors[this.position];
40229 getSlideAnchor : function(){
40230 return this.sanchors[this.position];
40233 getAlignAdj : function(){
40234 var cm = this.cmargins;
40235 switch(this.position){
40251 getExpandAdj : function(){
40252 var c = this.collapsedEl, cm = this.cmargins;
40253 switch(this.position){
40255 return [-(cm.right+c.getWidth()+cm.left), 0];
40258 return [cm.right+c.getWidth()+cm.left, 0];
40261 return [0, -(cm.top+cm.bottom+c.getHeight())];
40264 return [0, cm.top+cm.bottom+c.getHeight()];
40270 * Ext JS Library 1.1.1
40271 * Copyright(c) 2006-2007, Ext JS, LLC.
40273 * Originally Released Under LGPL - original licence link has changed is not relivant.
40276 * <script type="text/javascript">
40279 * These classes are private internal classes
40281 Roo.bootstrap.layout.Center = function(config){
40282 config.region = "center";
40283 Roo.bootstrap.layout.Region.call(this, config);
40284 this.visible = true;
40285 this.minWidth = config.minWidth || 20;
40286 this.minHeight = config.minHeight || 20;
40289 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40291 // center panel can't be hidden
40295 // center panel can't be hidden
40298 getMinWidth: function(){
40299 return this.minWidth;
40302 getMinHeight: function(){
40303 return this.minHeight;
40317 Roo.bootstrap.layout.North = function(config)
40319 config.region = 'north';
40320 config.cursor = 'n-resize';
40322 Roo.bootstrap.layout.Split.call(this, config);
40326 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40327 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40328 this.split.el.addClass("roo-layout-split-v");
40330 //var size = config.initialSize || config.height;
40331 //if(this.el && typeof size != "undefined"){
40332 // this.el.setHeight(size);
40335 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40337 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40340 onRender : function(ctr, pos)
40342 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40343 var size = this.config.initialSize || this.config.height;
40344 if(this.el && typeof size != "undefined"){
40345 this.el.setHeight(size);
40350 getBox : function(){
40351 if(this.collapsed){
40352 return this.collapsedEl.getBox();
40354 var box = this.el.getBox();
40356 box.height += this.split.el.getHeight();
40361 updateBox : function(box){
40362 if(this.split && !this.collapsed){
40363 box.height -= this.split.el.getHeight();
40364 this.split.el.setLeft(box.x);
40365 this.split.el.setTop(box.y+box.height);
40366 this.split.el.setWidth(box.width);
40368 if(this.collapsed){
40369 this.updateBody(box.width, null);
40371 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40379 Roo.bootstrap.layout.South = function(config){
40380 config.region = 'south';
40381 config.cursor = 's-resize';
40382 Roo.bootstrap.layout.Split.call(this, config);
40384 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40385 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40386 this.split.el.addClass("roo-layout-split-v");
40391 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40392 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40394 onRender : function(ctr, pos)
40396 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40397 var size = this.config.initialSize || this.config.height;
40398 if(this.el && typeof size != "undefined"){
40399 this.el.setHeight(size);
40404 getBox : function(){
40405 if(this.collapsed){
40406 return this.collapsedEl.getBox();
40408 var box = this.el.getBox();
40410 var sh = this.split.el.getHeight();
40417 updateBox : function(box){
40418 if(this.split && !this.collapsed){
40419 var sh = this.split.el.getHeight();
40422 this.split.el.setLeft(box.x);
40423 this.split.el.setTop(box.y-sh);
40424 this.split.el.setWidth(box.width);
40426 if(this.collapsed){
40427 this.updateBody(box.width, null);
40429 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40433 Roo.bootstrap.layout.East = function(config){
40434 config.region = "east";
40435 config.cursor = "e-resize";
40436 Roo.bootstrap.layout.Split.call(this, config);
40438 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40439 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40440 this.split.el.addClass("roo-layout-split-h");
40444 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40445 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40447 onRender : function(ctr, pos)
40449 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40450 var size = this.config.initialSize || this.config.width;
40451 if(this.el && typeof size != "undefined"){
40452 this.el.setWidth(size);
40457 getBox : function(){
40458 if(this.collapsed){
40459 return this.collapsedEl.getBox();
40461 var box = this.el.getBox();
40463 var sw = this.split.el.getWidth();
40470 updateBox : function(box){
40471 if(this.split && !this.collapsed){
40472 var sw = this.split.el.getWidth();
40474 this.split.el.setLeft(box.x);
40475 this.split.el.setTop(box.y);
40476 this.split.el.setHeight(box.height);
40479 if(this.collapsed){
40480 this.updateBody(null, box.height);
40482 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40486 Roo.bootstrap.layout.West = function(config){
40487 config.region = "west";
40488 config.cursor = "w-resize";
40490 Roo.bootstrap.layout.Split.call(this, config);
40492 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40493 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40494 this.split.el.addClass("roo-layout-split-h");
40498 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40499 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40501 onRender: function(ctr, pos)
40503 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40504 var size = this.config.initialSize || this.config.width;
40505 if(typeof size != "undefined"){
40506 this.el.setWidth(size);
40510 getBox : function(){
40511 if(this.collapsed){
40512 return this.collapsedEl.getBox();
40514 var box = this.el.getBox();
40515 if (box.width == 0) {
40516 box.width = this.config.width; // kludge?
40519 box.width += this.split.el.getWidth();
40524 updateBox : function(box){
40525 if(this.split && !this.collapsed){
40526 var sw = this.split.el.getWidth();
40528 this.split.el.setLeft(box.x+box.width);
40529 this.split.el.setTop(box.y);
40530 this.split.el.setHeight(box.height);
40532 if(this.collapsed){
40533 this.updateBody(null, box.height);
40535 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40537 });Roo.namespace("Roo.bootstrap.panel");/*
40539 * Ext JS Library 1.1.1
40540 * Copyright(c) 2006-2007, Ext JS, LLC.
40542 * Originally Released Under LGPL - original licence link has changed is not relivant.
40545 * <script type="text/javascript">
40548 * @class Roo.ContentPanel
40549 * @extends Roo.util.Observable
40550 * A basic ContentPanel element.
40551 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40552 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40553 * @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
40554 * @cfg {Boolean} closable True if the panel can be closed/removed
40555 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40556 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40557 * @cfg {Toolbar} toolbar A toolbar for this panel
40558 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40559 * @cfg {String} title The title for this panel
40560 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40561 * @cfg {String} url Calls {@link #setUrl} with this value
40562 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40563 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40564 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40565 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40566 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40567 * @cfg {Boolean} badges render the badges
40568 * @cfg {String} cls extra classes to use
40569 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40572 * Create a new ContentPanel.
40573 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40574 * @param {String/Object} config A string to set only the title or a config object
40575 * @param {String} content (optional) Set the HTML content for this panel
40576 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40578 Roo.bootstrap.panel.Content = function( config){
40580 this.tpl = config.tpl || false;
40582 var el = config.el;
40583 var content = config.content;
40585 if(config.autoCreate){ // xtype is available if this is called from factory
40588 this.el = Roo.get(el);
40589 if(!this.el && config && config.autoCreate){
40590 if(typeof config.autoCreate == "object"){
40591 if(!config.autoCreate.id){
40592 config.autoCreate.id = config.id||el;
40594 this.el = Roo.DomHelper.append(document.body,
40595 config.autoCreate, true);
40599 cls: (config.cls || '') +
40600 (config.background ? ' bg-' + config.background : '') +
40601 " roo-layout-inactive-content",
40604 if (config.iframe) {
40608 style : 'border: 0px',
40609 src : 'about:blank'
40615 elcfg.html = config.html;
40619 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40620 if (config.iframe) {
40621 this.iframeEl = this.el.select('iframe',true).first();
40626 this.closable = false;
40627 this.loaded = false;
40628 this.active = false;
40631 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40633 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40635 this.wrapEl = this.el; //this.el.wrap();
40637 if (config.toolbar.items) {
40638 ti = config.toolbar.items ;
40639 delete config.toolbar.items ;
40643 this.toolbar.render(this.wrapEl, 'before');
40644 for(var i =0;i < ti.length;i++) {
40645 // Roo.log(['add child', items[i]]);
40646 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40648 this.toolbar.items = nitems;
40649 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40650 delete config.toolbar;
40654 // xtype created footer. - not sure if will work as we normally have to render first..
40655 if (this.footer && !this.footer.el && this.footer.xtype) {
40656 if (!this.wrapEl) {
40657 this.wrapEl = this.el.wrap();
40660 this.footer.container = this.wrapEl.createChild();
40662 this.footer = Roo.factory(this.footer, Roo);
40667 if(typeof config == "string"){
40668 this.title = config;
40670 Roo.apply(this, config);
40674 this.resizeEl = Roo.get(this.resizeEl, true);
40676 this.resizeEl = this.el;
40678 // handle view.xtype
40686 * Fires when this panel is activated.
40687 * @param {Roo.ContentPanel} this
40691 * @event deactivate
40692 * Fires when this panel is activated.
40693 * @param {Roo.ContentPanel} this
40695 "deactivate" : true,
40699 * Fires when this panel is resized if fitToFrame is true.
40700 * @param {Roo.ContentPanel} this
40701 * @param {Number} width The width after any component adjustments
40702 * @param {Number} height The height after any component adjustments
40708 * Fires when this tab is created
40709 * @param {Roo.ContentPanel} this
40715 * Fires when this content is scrolled
40716 * @param {Roo.ContentPanel} this
40717 * @param {Event} scrollEvent
40728 if(this.autoScroll && !this.iframe){
40729 this.resizeEl.setStyle("overflow", "auto");
40730 this.resizeEl.on('scroll', this.onScroll, this);
40732 // fix randome scrolling
40733 //this.el.on('scroll', function() {
40734 // Roo.log('fix random scolling');
40735 // this.scrollTo('top',0);
40738 content = content || this.content;
40740 this.setContent(content);
40742 if(config && config.url){
40743 this.setUrl(this.url, this.params, this.loadOnce);
40748 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40750 if (this.view && typeof(this.view.xtype) != 'undefined') {
40751 this.view.el = this.el.appendChild(document.createElement("div"));
40752 this.view = Roo.factory(this.view);
40753 this.view.render && this.view.render(false, '');
40757 this.fireEvent('render', this);
40760 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40770 /* Resize Element - use this to work out scroll etc. */
40773 setRegion : function(region){
40774 this.region = region;
40775 this.setActiveClass(region && !this.background);
40779 setActiveClass: function(state)
40782 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40783 this.el.setStyle('position','relative');
40785 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40786 this.el.setStyle('position', 'absolute');
40791 * Returns the toolbar for this Panel if one was configured.
40792 * @return {Roo.Toolbar}
40794 getToolbar : function(){
40795 return this.toolbar;
40798 setActiveState : function(active)
40800 this.active = active;
40801 this.setActiveClass(active);
40803 if(this.fireEvent("deactivate", this) === false){
40808 this.fireEvent("activate", this);
40812 * Updates this panel's element (not for iframe)
40813 * @param {String} content The new content
40814 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40816 setContent : function(content, loadScripts){
40821 this.el.update(content, loadScripts);
40824 ignoreResize : function(w, h){
40825 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40828 this.lastSize = {width: w, height: h};
40833 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40834 * @return {Roo.UpdateManager} The UpdateManager
40836 getUpdateManager : function(){
40840 return this.el.getUpdateManager();
40843 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40844 * Does not work with IFRAME contents
40845 * @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:
40848 url: "your-url.php",
40849 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40850 callback: yourFunction,
40851 scope: yourObject, //(optional scope)
40854 text: "Loading...",
40860 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40861 * 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.
40862 * @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}
40863 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40864 * @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.
40865 * @return {Roo.ContentPanel} this
40873 var um = this.el.getUpdateManager();
40874 um.update.apply(um, arguments);
40880 * 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.
40881 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40882 * @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)
40883 * @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)
40884 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40886 setUrl : function(url, params, loadOnce){
40888 this.iframeEl.dom.src = url;
40892 if(this.refreshDelegate){
40893 this.removeListener("activate", this.refreshDelegate);
40895 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40896 this.on("activate", this.refreshDelegate);
40897 return this.el.getUpdateManager();
40900 _handleRefresh : function(url, params, loadOnce){
40901 if(!loadOnce || !this.loaded){
40902 var updater = this.el.getUpdateManager();
40903 updater.update(url, params, this._setLoaded.createDelegate(this));
40907 _setLoaded : function(){
40908 this.loaded = true;
40912 * Returns this panel's id
40915 getId : function(){
40920 * Returns this panel's element - used by regiosn to add.
40921 * @return {Roo.Element}
40923 getEl : function(){
40924 return this.wrapEl || this.el;
40929 adjustForComponents : function(width, height)
40931 //Roo.log('adjustForComponents ');
40932 if(this.resizeEl != this.el){
40933 width -= this.el.getFrameWidth('lr');
40934 height -= this.el.getFrameWidth('tb');
40937 var te = this.toolbar.getEl();
40938 te.setWidth(width);
40939 height -= te.getHeight();
40942 var te = this.footer.getEl();
40943 te.setWidth(width);
40944 height -= te.getHeight();
40948 if(this.adjustments){
40949 width += this.adjustments[0];
40950 height += this.adjustments[1];
40952 return {"width": width, "height": height};
40955 setSize : function(width, height){
40956 if(this.fitToFrame && !this.ignoreResize(width, height)){
40957 if(this.fitContainer && this.resizeEl != this.el){
40958 this.el.setSize(width, height);
40960 var size = this.adjustForComponents(width, height);
40962 this.iframeEl.setSize(width,height);
40965 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40966 this.fireEvent('resize', this, size.width, size.height);
40973 * Returns this panel's title
40976 getTitle : function(){
40978 if (typeof(this.title) != 'object') {
40983 for (var k in this.title) {
40984 if (!this.title.hasOwnProperty(k)) {
40988 if (k.indexOf('-') >= 0) {
40989 var s = k.split('-');
40990 for (var i = 0; i<s.length; i++) {
40991 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40994 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41001 * Set this panel's title
41002 * @param {String} title
41004 setTitle : function(title){
41005 this.title = title;
41007 this.region.updatePanelTitle(this, title);
41012 * Returns true is this panel was configured to be closable
41013 * @return {Boolean}
41015 isClosable : function(){
41016 return this.closable;
41019 beforeSlide : function(){
41021 this.resizeEl.clip();
41024 afterSlide : function(){
41026 this.resizeEl.unclip();
41030 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41031 * Will fail silently if the {@link #setUrl} method has not been called.
41032 * This does not activate the panel, just updates its content.
41034 refresh : function(){
41035 if(this.refreshDelegate){
41036 this.loaded = false;
41037 this.refreshDelegate();
41042 * Destroys this panel
41044 destroy : function(){
41045 this.el.removeAllListeners();
41046 var tempEl = document.createElement("span");
41047 tempEl.appendChild(this.el.dom);
41048 tempEl.innerHTML = "";
41054 * form - if the content panel contains a form - this is a reference to it.
41055 * @type {Roo.form.Form}
41059 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41060 * This contains a reference to it.
41066 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41076 * @param {Object} cfg Xtype definition of item to add.
41080 getChildContainer: function () {
41081 return this.getEl();
41085 onScroll : function(e)
41087 this.fireEvent('scroll', this, e);
41092 var ret = new Roo.factory(cfg);
41097 if (cfg.xtype.match(/^Form$/)) {
41100 //if (this.footer) {
41101 // el = this.footer.container.insertSibling(false, 'before');
41103 el = this.el.createChild();
41106 this.form = new Roo.form.Form(cfg);
41109 if ( this.form.allItems.length) {
41110 this.form.render(el.dom);
41114 // should only have one of theses..
41115 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41116 // views.. should not be just added - used named prop 'view''
41118 cfg.el = this.el.appendChild(document.createElement("div"));
41121 var ret = new Roo.factory(cfg);
41123 ret.render && ret.render(false, ''); // render blank..
41133 * @class Roo.bootstrap.panel.Grid
41134 * @extends Roo.bootstrap.panel.Content
41136 * Create a new GridPanel.
41137 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41138 * @param {Object} config A the config object
41144 Roo.bootstrap.panel.Grid = function(config)
41148 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41149 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41151 config.el = this.wrapper;
41152 //this.el = this.wrapper;
41154 if (config.container) {
41155 // ctor'ed from a Border/panel.grid
41158 this.wrapper.setStyle("overflow", "hidden");
41159 this.wrapper.addClass('roo-grid-container');
41164 if(config.toolbar){
41165 var tool_el = this.wrapper.createChild();
41166 this.toolbar = Roo.factory(config.toolbar);
41168 if (config.toolbar.items) {
41169 ti = config.toolbar.items ;
41170 delete config.toolbar.items ;
41174 this.toolbar.render(tool_el);
41175 for(var i =0;i < ti.length;i++) {
41176 // Roo.log(['add child', items[i]]);
41177 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41179 this.toolbar.items = nitems;
41181 delete config.toolbar;
41184 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41185 config.grid.scrollBody = true;;
41186 config.grid.monitorWindowResize = false; // turn off autosizing
41187 config.grid.autoHeight = false;
41188 config.grid.autoWidth = false;
41190 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41192 if (config.background) {
41193 // render grid on panel activation (if panel background)
41194 this.on('activate', function(gp) {
41195 if (!gp.grid.rendered) {
41196 gp.grid.render(this.wrapper);
41197 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41202 this.grid.render(this.wrapper);
41203 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41206 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41207 // ??? needed ??? config.el = this.wrapper;
41212 // xtype created footer. - not sure if will work as we normally have to render first..
41213 if (this.footer && !this.footer.el && this.footer.xtype) {
41215 var ctr = this.grid.getView().getFooterPanel(true);
41216 this.footer.dataSource = this.grid.dataSource;
41217 this.footer = Roo.factory(this.footer, Roo);
41218 this.footer.render(ctr);
41228 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41229 getId : function(){
41230 return this.grid.id;
41234 * Returns the grid for this panel
41235 * @return {Roo.bootstrap.Table}
41237 getGrid : function(){
41241 setSize : function(width, height){
41242 if(!this.ignoreResize(width, height)){
41243 var grid = this.grid;
41244 var size = this.adjustForComponents(width, height);
41245 // tfoot is not a footer?
41248 var gridel = grid.getGridEl();
41249 gridel.setSize(size.width, size.height);
41251 var tbd = grid.getGridEl().select('tbody', true).first();
41252 var thd = grid.getGridEl().select('thead',true).first();
41253 var tbf= grid.getGridEl().select('tfoot', true).first();
41256 size.height -= tbf.getHeight();
41259 size.height -= thd.getHeight();
41262 tbd.setSize(size.width, size.height );
41263 // this is for the account management tab -seems to work there.
41264 var thd = grid.getGridEl().select('thead',true).first();
41266 // tbd.setSize(size.width, size.height - thd.getHeight());
41275 beforeSlide : function(){
41276 this.grid.getView().scroller.clip();
41279 afterSlide : function(){
41280 this.grid.getView().scroller.unclip();
41283 destroy : function(){
41284 this.grid.destroy();
41286 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41291 * @class Roo.bootstrap.panel.Nest
41292 * @extends Roo.bootstrap.panel.Content
41294 * Create a new Panel, that can contain a layout.Border.
41297 * @param {Roo.BorderLayout} layout The layout for this panel
41298 * @param {String/Object} config A string to set only the title or a config object
41300 Roo.bootstrap.panel.Nest = function(config)
41302 // construct with only one argument..
41303 /* FIXME - implement nicer consturctors
41304 if (layout.layout) {
41306 layout = config.layout;
41307 delete config.layout;
41309 if (layout.xtype && !layout.getEl) {
41310 // then layout needs constructing..
41311 layout = Roo.factory(layout, Roo);
41315 config.el = config.layout.getEl();
41317 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41319 config.layout.monitorWindowResize = false; // turn off autosizing
41320 this.layout = config.layout;
41321 this.layout.getEl().addClass("roo-layout-nested-layout");
41322 this.layout.parent = this;
41329 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41331 setSize : function(width, height){
41332 if(!this.ignoreResize(width, height)){
41333 var size = this.adjustForComponents(width, height);
41334 var el = this.layout.getEl();
41335 if (size.height < 1) {
41336 el.setWidth(size.width);
41338 el.setSize(size.width, size.height);
41340 var touch = el.dom.offsetWidth;
41341 this.layout.layout();
41342 // ie requires a double layout on the first pass
41343 if(Roo.isIE && !this.initialized){
41344 this.initialized = true;
41345 this.layout.layout();
41350 // activate all subpanels if not currently active..
41352 setActiveState : function(active){
41353 this.active = active;
41354 this.setActiveClass(active);
41357 this.fireEvent("deactivate", this);
41361 this.fireEvent("activate", this);
41362 // not sure if this should happen before or after..
41363 if (!this.layout) {
41364 return; // should not happen..
41367 for (var r in this.layout.regions) {
41368 reg = this.layout.getRegion(r);
41369 if (reg.getActivePanel()) {
41370 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41371 reg.setActivePanel(reg.getActivePanel());
41374 if (!reg.panels.length) {
41377 reg.showPanel(reg.getPanel(0));
41386 * Returns the nested BorderLayout for this panel
41387 * @return {Roo.BorderLayout}
41389 getLayout : function(){
41390 return this.layout;
41394 * Adds a xtype elements to the layout of the nested panel
41398 xtype : 'ContentPanel',
41405 xtype : 'NestedLayoutPanel',
41411 items : [ ... list of content panels or nested layout panels.. ]
41415 * @param {Object} cfg Xtype definition of item to add.
41417 addxtype : function(cfg) {
41418 return this.layout.addxtype(cfg);
41423 * Ext JS Library 1.1.1
41424 * Copyright(c) 2006-2007, Ext JS, LLC.
41426 * Originally Released Under LGPL - original licence link has changed is not relivant.
41429 * <script type="text/javascript">
41432 * @class Roo.TabPanel
41433 * @extends Roo.util.Observable
41434 * A lightweight tab container.
41438 // basic tabs 1, built from existing content
41439 var tabs = new Roo.TabPanel("tabs1");
41440 tabs.addTab("script", "View Script");
41441 tabs.addTab("markup", "View Markup");
41442 tabs.activate("script");
41444 // more advanced tabs, built from javascript
41445 var jtabs = new Roo.TabPanel("jtabs");
41446 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41448 // set up the UpdateManager
41449 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41450 var updater = tab2.getUpdateManager();
41451 updater.setDefaultUrl("ajax1.htm");
41452 tab2.on('activate', updater.refresh, updater, true);
41454 // Use setUrl for Ajax loading
41455 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41456 tab3.setUrl("ajax2.htm", null, true);
41459 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41462 jtabs.activate("jtabs-1");
41465 * Create a new TabPanel.
41466 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41467 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41469 Roo.bootstrap.panel.Tabs = function(config){
41471 * The container element for this TabPanel.
41472 * @type Roo.Element
41474 this.el = Roo.get(config.el);
41477 if(typeof config == "boolean"){
41478 this.tabPosition = config ? "bottom" : "top";
41480 Roo.apply(this, config);
41484 if(this.tabPosition == "bottom"){
41485 // if tabs are at the bottom = create the body first.
41486 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41487 this.el.addClass("roo-tabs-bottom");
41489 // next create the tabs holders
41491 if (this.tabPosition == "west"){
41493 var reg = this.region; // fake it..
41495 if (!reg.mgr.parent) {
41498 reg = reg.mgr.parent.region;
41500 Roo.log("got nest?");
41502 if (reg.mgr.getRegion('west')) {
41503 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41504 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41505 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41506 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41507 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41515 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41516 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41517 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41518 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41523 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41526 // finally - if tabs are at the top, then create the body last..
41527 if(this.tabPosition != "bottom"){
41528 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41529 * @type Roo.Element
41531 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41532 this.el.addClass("roo-tabs-top");
41536 this.bodyEl.setStyle("position", "relative");
41538 this.active = null;
41539 this.activateDelegate = this.activate.createDelegate(this);
41544 * Fires when the active tab changes
41545 * @param {Roo.TabPanel} this
41546 * @param {Roo.TabPanelItem} activePanel The new active tab
41550 * @event beforetabchange
41551 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41552 * @param {Roo.TabPanel} this
41553 * @param {Object} e Set cancel to true on this object to cancel the tab change
41554 * @param {Roo.TabPanelItem} tab The tab being changed to
41556 "beforetabchange" : true
41559 Roo.EventManager.onWindowResize(this.onResize, this);
41560 this.cpad = this.el.getPadding("lr");
41561 this.hiddenCount = 0;
41564 // toolbar on the tabbar support...
41565 if (this.toolbar) {
41566 alert("no toolbar support yet");
41567 this.toolbar = false;
41569 var tcfg = this.toolbar;
41570 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41571 this.toolbar = new Roo.Toolbar(tcfg);
41572 if (Roo.isSafari) {
41573 var tbl = tcfg.container.child('table', true);
41574 tbl.setAttribute('width', '100%');
41582 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41585 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41587 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41589 tabPosition : "top",
41591 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41593 currentTabWidth : 0,
41595 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41599 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41603 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41605 preferredTabWidth : 175,
41607 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41609 resizeTabs : false,
41611 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41613 monitorResize : true,
41615 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41617 toolbar : false, // set by caller..
41619 region : false, /// set by caller
41621 disableTooltips : true, // not used yet...
41624 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41625 * @param {String} id The id of the div to use <b>or create</b>
41626 * @param {String} text The text for the tab
41627 * @param {String} content (optional) Content to put in the TabPanelItem body
41628 * @param {Boolean} closable (optional) True to create a close icon on the tab
41629 * @return {Roo.TabPanelItem} The created TabPanelItem
41631 addTab : function(id, text, content, closable, tpl)
41633 var item = new Roo.bootstrap.panel.TabItem({
41637 closable : closable,
41640 this.addTabItem(item);
41642 item.setContent(content);
41648 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41649 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41650 * @return {Roo.TabPanelItem}
41652 getTab : function(id){
41653 return this.items[id];
41657 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41658 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41660 hideTab : function(id){
41661 var t = this.items[id];
41664 this.hiddenCount++;
41665 this.autoSizeTabs();
41670 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41671 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41673 unhideTab : function(id){
41674 var t = this.items[id];
41676 t.setHidden(false);
41677 this.hiddenCount--;
41678 this.autoSizeTabs();
41683 * Adds an existing {@link Roo.TabPanelItem}.
41684 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41686 addTabItem : function(item)
41688 this.items[item.id] = item;
41689 this.items.push(item);
41690 this.autoSizeTabs();
41691 // if(this.resizeTabs){
41692 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41693 // this.autoSizeTabs();
41695 // item.autoSize();
41700 * Removes a {@link Roo.TabPanelItem}.
41701 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41703 removeTab : function(id){
41704 var items = this.items;
41705 var tab = items[id];
41706 if(!tab) { return; }
41707 var index = items.indexOf(tab);
41708 if(this.active == tab && items.length > 1){
41709 var newTab = this.getNextAvailable(index);
41714 this.stripEl.dom.removeChild(tab.pnode.dom);
41715 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41716 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41718 items.splice(index, 1);
41719 delete this.items[tab.id];
41720 tab.fireEvent("close", tab);
41721 tab.purgeListeners();
41722 this.autoSizeTabs();
41725 getNextAvailable : function(start){
41726 var items = this.items;
41728 // look for a next tab that will slide over to
41729 // replace the one being removed
41730 while(index < items.length){
41731 var item = items[++index];
41732 if(item && !item.isHidden()){
41736 // if one isn't found select the previous tab (on the left)
41739 var item = items[--index];
41740 if(item && !item.isHidden()){
41748 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41749 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41751 disableTab : function(id){
41752 var tab = this.items[id];
41753 if(tab && this.active != tab){
41759 * Enables a {@link Roo.TabPanelItem} that is disabled.
41760 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41762 enableTab : function(id){
41763 var tab = this.items[id];
41768 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41769 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41770 * @return {Roo.TabPanelItem} The TabPanelItem.
41772 activate : function(id)
41774 //Roo.log('activite:' + id);
41776 var tab = this.items[id];
41780 if(tab == this.active || tab.disabled){
41784 this.fireEvent("beforetabchange", this, e, tab);
41785 if(e.cancel !== true && !tab.disabled){
41787 this.active.hide();
41789 this.active = this.items[id];
41790 this.active.show();
41791 this.fireEvent("tabchange", this, this.active);
41797 * Gets the active {@link Roo.TabPanelItem}.
41798 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41800 getActiveTab : function(){
41801 return this.active;
41805 * Updates the tab body element to fit the height of the container element
41806 * for overflow scrolling
41807 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41809 syncHeight : function(targetHeight){
41810 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41811 var bm = this.bodyEl.getMargins();
41812 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41813 this.bodyEl.setHeight(newHeight);
41817 onResize : function(){
41818 if(this.monitorResize){
41819 this.autoSizeTabs();
41824 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41826 beginUpdate : function(){
41827 this.updating = true;
41831 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41833 endUpdate : function(){
41834 this.updating = false;
41835 this.autoSizeTabs();
41839 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41841 autoSizeTabs : function()
41843 var count = this.items.length;
41844 var vcount = count - this.hiddenCount;
41847 this.stripEl.hide();
41849 this.stripEl.show();
41852 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41857 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41858 var availWidth = Math.floor(w / vcount);
41859 var b = this.stripBody;
41860 if(b.getWidth() > w){
41861 var tabs = this.items;
41862 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41863 if(availWidth < this.minTabWidth){
41864 /*if(!this.sleft){ // incomplete scrolling code
41865 this.createScrollButtons();
41868 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41871 if(this.currentTabWidth < this.preferredTabWidth){
41872 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41878 * Returns the number of tabs in this TabPanel.
41881 getCount : function(){
41882 return this.items.length;
41886 * Resizes all the tabs to the passed width
41887 * @param {Number} The new width
41889 setTabWidth : function(width){
41890 this.currentTabWidth = width;
41891 for(var i = 0, len = this.items.length; i < len; i++) {
41892 if(!this.items[i].isHidden()) {
41893 this.items[i].setWidth(width);
41899 * Destroys this TabPanel
41900 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41902 destroy : function(removeEl){
41903 Roo.EventManager.removeResizeListener(this.onResize, this);
41904 for(var i = 0, len = this.items.length; i < len; i++){
41905 this.items[i].purgeListeners();
41907 if(removeEl === true){
41908 this.el.update("");
41913 createStrip : function(container)
41915 var strip = document.createElement("nav");
41916 strip.className = Roo.bootstrap.version == 4 ?
41917 "navbar-light bg-light" :
41918 "navbar navbar-default"; //"x-tabs-wrap";
41919 container.appendChild(strip);
41923 createStripList : function(strip)
41925 // div wrapper for retard IE
41926 // returns the "tr" element.
41927 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41928 //'<div class="x-tabs-strip-wrap">'+
41929 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41930 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41931 return strip.firstChild; //.firstChild.firstChild.firstChild;
41933 createBody : function(container)
41935 var body = document.createElement("div");
41936 Roo.id(body, "tab-body");
41937 //Roo.fly(body).addClass("x-tabs-body");
41938 Roo.fly(body).addClass("tab-content");
41939 container.appendChild(body);
41942 createItemBody :function(bodyEl, id){
41943 var body = Roo.getDom(id);
41945 body = document.createElement("div");
41948 //Roo.fly(body).addClass("x-tabs-item-body");
41949 Roo.fly(body).addClass("tab-pane");
41950 bodyEl.insertBefore(body, bodyEl.firstChild);
41954 createStripElements : function(stripEl, text, closable, tpl)
41956 var td = document.createElement("li"); // was td..
41957 td.className = 'nav-item';
41959 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41962 stripEl.appendChild(td);
41964 td.className = "x-tabs-closable";
41965 if(!this.closeTpl){
41966 this.closeTpl = new Roo.Template(
41967 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41968 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41969 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41972 var el = this.closeTpl.overwrite(td, {"text": text});
41973 var close = el.getElementsByTagName("div")[0];
41974 var inner = el.getElementsByTagName("em")[0];
41975 return {"el": el, "close": close, "inner": inner};
41978 // not sure what this is..
41979 // if(!this.tabTpl){
41980 //this.tabTpl = new Roo.Template(
41981 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41982 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41984 // this.tabTpl = new Roo.Template(
41985 // '<a href="#">' +
41986 // '<span unselectable="on"' +
41987 // (this.disableTooltips ? '' : ' title="{text}"') +
41988 // ' >{text}</span></a>'
41994 var template = tpl || this.tabTpl || false;
41997 template = new Roo.Template(
41998 Roo.bootstrap.version == 4 ?
42000 '<a class="nav-link" href="#" unselectable="on"' +
42001 (this.disableTooltips ? '' : ' title="{text}"') +
42004 '<a class="nav-link" href="#">' +
42005 '<span unselectable="on"' +
42006 (this.disableTooltips ? '' : ' title="{text}"') +
42007 ' >{text}</span></a>'
42012 switch (typeof(template)) {
42016 template = new Roo.Template(template);
42022 var el = template.overwrite(td, {"text": text});
42024 var inner = el.getElementsByTagName("span")[0];
42026 return {"el": el, "inner": inner};
42034 * @class Roo.TabPanelItem
42035 * @extends Roo.util.Observable
42036 * Represents an individual item (tab plus body) in a TabPanel.
42037 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42038 * @param {String} id The id of this TabPanelItem
42039 * @param {String} text The text for the tab of this TabPanelItem
42040 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42042 Roo.bootstrap.panel.TabItem = function(config){
42044 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42045 * @type Roo.TabPanel
42047 this.tabPanel = config.panel;
42049 * The id for this TabPanelItem
42052 this.id = config.id;
42054 this.disabled = false;
42056 this.text = config.text;
42058 this.loaded = false;
42059 this.closable = config.closable;
42062 * The body element for this TabPanelItem.
42063 * @type Roo.Element
42065 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42066 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42067 this.bodyEl.setStyle("display", "block");
42068 this.bodyEl.setStyle("zoom", "1");
42069 //this.hideAction();
42071 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42073 this.el = Roo.get(els.el);
42074 this.inner = Roo.get(els.inner, true);
42075 this.textEl = Roo.bootstrap.version == 4 ?
42076 this.el : Roo.get(this.el.dom.firstChild, true);
42078 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42079 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42082 // this.el.on("mousedown", this.onTabMouseDown, this);
42083 this.el.on("click", this.onTabClick, this);
42085 if(config.closable){
42086 var c = Roo.get(els.close, true);
42087 c.dom.title = this.closeText;
42088 c.addClassOnOver("close-over");
42089 c.on("click", this.closeClick, this);
42095 * Fires when this tab becomes the active tab.
42096 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42097 * @param {Roo.TabPanelItem} this
42101 * @event beforeclose
42102 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42103 * @param {Roo.TabPanelItem} this
42104 * @param {Object} e Set cancel to true on this object to cancel the close.
42106 "beforeclose": true,
42109 * Fires when this tab is closed.
42110 * @param {Roo.TabPanelItem} this
42114 * @event deactivate
42115 * Fires when this tab is no longer the active tab.
42116 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42117 * @param {Roo.TabPanelItem} this
42119 "deactivate" : true
42121 this.hidden = false;
42123 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42126 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42128 purgeListeners : function(){
42129 Roo.util.Observable.prototype.purgeListeners.call(this);
42130 this.el.removeAllListeners();
42133 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42136 this.status_node.addClass("active");
42139 this.tabPanel.stripWrap.repaint();
42141 this.fireEvent("activate", this.tabPanel, this);
42145 * Returns true if this tab is the active tab.
42146 * @return {Boolean}
42148 isActive : function(){
42149 return this.tabPanel.getActiveTab() == this;
42153 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42156 this.status_node.removeClass("active");
42158 this.fireEvent("deactivate", this.tabPanel, this);
42161 hideAction : function(){
42162 this.bodyEl.hide();
42163 this.bodyEl.setStyle("position", "absolute");
42164 this.bodyEl.setLeft("-20000px");
42165 this.bodyEl.setTop("-20000px");
42168 showAction : function(){
42169 this.bodyEl.setStyle("position", "relative");
42170 this.bodyEl.setTop("");
42171 this.bodyEl.setLeft("");
42172 this.bodyEl.show();
42176 * Set the tooltip for the tab.
42177 * @param {String} tooltip The tab's tooltip
42179 setTooltip : function(text){
42180 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42181 this.textEl.dom.qtip = text;
42182 this.textEl.dom.removeAttribute('title');
42184 this.textEl.dom.title = text;
42188 onTabClick : function(e){
42189 e.preventDefault();
42190 this.tabPanel.activate(this.id);
42193 onTabMouseDown : function(e){
42194 e.preventDefault();
42195 this.tabPanel.activate(this.id);
42198 getWidth : function(){
42199 return this.inner.getWidth();
42202 setWidth : function(width){
42203 var iwidth = width - this.linode.getPadding("lr");
42204 this.inner.setWidth(iwidth);
42205 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42206 this.linode.setWidth(width);
42210 * Show or hide the tab
42211 * @param {Boolean} hidden True to hide or false to show.
42213 setHidden : function(hidden){
42214 this.hidden = hidden;
42215 this.linode.setStyle("display", hidden ? "none" : "");
42219 * Returns true if this tab is "hidden"
42220 * @return {Boolean}
42222 isHidden : function(){
42223 return this.hidden;
42227 * Returns the text for this tab
42230 getText : function(){
42234 autoSize : function(){
42235 //this.el.beginMeasure();
42236 this.textEl.setWidth(1);
42238 * #2804 [new] Tabs in Roojs
42239 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42241 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42242 //this.el.endMeasure();
42246 * Sets the text for the tab (Note: this also sets the tooltip text)
42247 * @param {String} text The tab's text and tooltip
42249 setText : function(text){
42251 this.textEl.update(text);
42252 this.setTooltip(text);
42253 //if(!this.tabPanel.resizeTabs){
42254 // this.autoSize();
42258 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42260 activate : function(){
42261 this.tabPanel.activate(this.id);
42265 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42267 disable : function(){
42268 if(this.tabPanel.active != this){
42269 this.disabled = true;
42270 this.status_node.addClass("disabled");
42275 * Enables this TabPanelItem if it was previously disabled.
42277 enable : function(){
42278 this.disabled = false;
42279 this.status_node.removeClass("disabled");
42283 * Sets the content for this TabPanelItem.
42284 * @param {String} content The content
42285 * @param {Boolean} loadScripts true to look for and load scripts
42287 setContent : function(content, loadScripts){
42288 this.bodyEl.update(content, loadScripts);
42292 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42293 * @return {Roo.UpdateManager} The UpdateManager
42295 getUpdateManager : function(){
42296 return this.bodyEl.getUpdateManager();
42300 * Set a URL to be used to load the content for this TabPanelItem.
42301 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42302 * @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)
42303 * @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)
42304 * @return {Roo.UpdateManager} The UpdateManager
42306 setUrl : function(url, params, loadOnce){
42307 if(this.refreshDelegate){
42308 this.un('activate', this.refreshDelegate);
42310 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42311 this.on("activate", this.refreshDelegate);
42312 return this.bodyEl.getUpdateManager();
42316 _handleRefresh : function(url, params, loadOnce){
42317 if(!loadOnce || !this.loaded){
42318 var updater = this.bodyEl.getUpdateManager();
42319 updater.update(url, params, this._setLoaded.createDelegate(this));
42324 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42325 * Will fail silently if the setUrl method has not been called.
42326 * This does not activate the panel, just updates its content.
42328 refresh : function(){
42329 if(this.refreshDelegate){
42330 this.loaded = false;
42331 this.refreshDelegate();
42336 _setLoaded : function(){
42337 this.loaded = true;
42341 closeClick : function(e){
42344 this.fireEvent("beforeclose", this, o);
42345 if(o.cancel !== true){
42346 this.tabPanel.removeTab(this.id);
42350 * The text displayed in the tooltip for the close icon.
42353 closeText : "Close this tab"
42356 * This script refer to:
42357 * Title: International Telephone Input
42358 * Author: Jack O'Connor
42359 * Code version: v12.1.12
42360 * Availability: https://github.com/jackocnr/intl-tel-input.git
42363 Roo.bootstrap.PhoneInputData = function() {
42366 "Afghanistan (افغانستان)",
42371 "Albania (Shqipëri)",
42376 "Algeria (الجزائر)",
42401 "Antigua and Barbuda",
42411 "Armenia (Հայաստան)",
42427 "Austria (Österreich)",
42432 "Azerbaijan (Azərbaycan)",
42442 "Bahrain (البحرين)",
42447 "Bangladesh (বাংলাদেশ)",
42457 "Belarus (Беларусь)",
42462 "Belgium (België)",
42492 "Bosnia and Herzegovina (Босна и Херцеговина)",
42507 "British Indian Ocean Territory",
42512 "British Virgin Islands",
42522 "Bulgaria (България)",
42532 "Burundi (Uburundi)",
42537 "Cambodia (កម្ពុជា)",
42542 "Cameroon (Cameroun)",
42551 ["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"]
42554 "Cape Verde (Kabu Verdi)",
42559 "Caribbean Netherlands",
42570 "Central African Republic (République centrafricaine)",
42590 "Christmas Island",
42596 "Cocos (Keeling) Islands",
42607 "Comoros (جزر القمر)",
42612 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42617 "Congo (Republic) (Congo-Brazzaville)",
42637 "Croatia (Hrvatska)",
42658 "Czech Republic (Česká republika)",
42663 "Denmark (Danmark)",
42678 "Dominican Republic (República Dominicana)",
42682 ["809", "829", "849"]
42700 "Equatorial Guinea (Guinea Ecuatorial)",
42720 "Falkland Islands (Islas Malvinas)",
42725 "Faroe Islands (Føroyar)",
42746 "French Guiana (Guyane française)",
42751 "French Polynesia (Polynésie française)",
42766 "Georgia (საქართველო)",
42771 "Germany (Deutschland)",
42791 "Greenland (Kalaallit Nunaat)",
42828 "Guinea-Bissau (Guiné Bissau)",
42853 "Hungary (Magyarország)",
42858 "Iceland (Ísland)",
42878 "Iraq (العراق)",
42894 "Israel (ישראל)",
42921 "Jordan (الأردن)",
42926 "Kazakhstan (Казахстан)",
42947 "Kuwait (الكويت)",
42952 "Kyrgyzstan (Кыргызстан)",
42962 "Latvia (Latvija)",
42967 "Lebanon (لبنان)",
42982 "Libya (ليبيا)",
42992 "Lithuania (Lietuva)",
43007 "Macedonia (FYROM) (Македонија)",
43012 "Madagascar (Madagasikara)",
43042 "Marshall Islands",
43052 "Mauritania (موريتانيا)",
43057 "Mauritius (Moris)",
43078 "Moldova (Republica Moldova)",
43088 "Mongolia (Монгол)",
43093 "Montenegro (Crna Gora)",
43103 "Morocco (المغرب)",
43109 "Mozambique (Moçambique)",
43114 "Myanmar (Burma) (မြန်မာ)",
43119 "Namibia (Namibië)",
43134 "Netherlands (Nederland)",
43139 "New Caledonia (Nouvelle-Calédonie)",
43174 "North Korea (조선 민주주의 인민 공화국)",
43179 "Northern Mariana Islands",
43195 "Pakistan (پاکستان)",
43205 "Palestine (فلسطين)",
43215 "Papua New Guinea",
43257 "Réunion (La Réunion)",
43263 "Romania (România)",
43279 "Saint Barthélemy",
43290 "Saint Kitts and Nevis",
43300 "Saint Martin (Saint-Martin (partie française))",
43306 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43311 "Saint Vincent and the Grenadines",
43326 "São Tomé and Príncipe (São Tomé e Príncipe)",
43331 "Saudi Arabia (المملكة العربية السعودية)",
43336 "Senegal (Sénégal)",
43366 "Slovakia (Slovensko)",
43371 "Slovenia (Slovenija)",
43381 "Somalia (Soomaaliya)",
43391 "South Korea (대한민국)",
43396 "South Sudan (جنوب السودان)",
43406 "Sri Lanka (ශ්රී ලංකාව)",
43411 "Sudan (السودان)",
43421 "Svalbard and Jan Mayen",
43432 "Sweden (Sverige)",
43437 "Switzerland (Schweiz)",
43442 "Syria (سوريا)",
43487 "Trinidad and Tobago",
43492 "Tunisia (تونس)",
43497 "Turkey (Türkiye)",
43507 "Turks and Caicos Islands",
43517 "U.S. Virgin Islands",
43527 "Ukraine (Україна)",
43532 "United Arab Emirates (الإمارات العربية المتحدة)",
43554 "Uzbekistan (Oʻzbekiston)",
43564 "Vatican City (Città del Vaticano)",
43575 "Vietnam (Việt Nam)",
43580 "Wallis and Futuna (Wallis-et-Futuna)",
43585 "Western Sahara (الصحراء الغربية)",
43591 "Yemen (اليمن)",
43615 * This script refer to:
43616 * Title: International Telephone Input
43617 * Author: Jack O'Connor
43618 * Code version: v12.1.12
43619 * Availability: https://github.com/jackocnr/intl-tel-input.git
43623 * @class Roo.bootstrap.PhoneInput
43624 * @extends Roo.bootstrap.TriggerField
43625 * An input with International dial-code selection
43627 * @cfg {String} defaultDialCode default '+852'
43628 * @cfg {Array} preferedCountries default []
43631 * Create a new PhoneInput.
43632 * @param {Object} config Configuration options
43635 Roo.bootstrap.PhoneInput = function(config) {
43636 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43639 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43641 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43643 listWidth: undefined,
43645 selectedClass: 'active',
43647 invalidClass : "has-warning",
43649 validClass: 'has-success',
43651 allowed: '0123456789',
43656 * @cfg {String} defaultDialCode The default dial code when initializing the input
43658 defaultDialCode: '+852',
43661 * @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
43663 preferedCountries: false,
43665 getAutoCreate : function()
43667 var data = Roo.bootstrap.PhoneInputData();
43668 var align = this.labelAlign || this.parentLabelAlign();
43671 this.allCountries = [];
43672 this.dialCodeMapping = [];
43674 for (var i = 0; i < data.length; i++) {
43676 this.allCountries[i] = {
43680 priority: c[3] || 0,
43681 areaCodes: c[4] || null
43683 this.dialCodeMapping[c[2]] = {
43686 priority: c[3] || 0,
43687 areaCodes: c[4] || null
43699 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43700 maxlength: this.max_length,
43701 cls : 'form-control tel-input',
43702 autocomplete: 'new-password'
43705 var hiddenInput = {
43708 cls: 'hidden-tel-input'
43712 hiddenInput.name = this.name;
43715 if (this.disabled) {
43716 input.disabled = true;
43719 var flag_container = {
43736 cls: this.hasFeedback ? 'has-feedback' : '',
43742 cls: 'dial-code-holder',
43749 cls: 'roo-select2-container input-group',
43756 if (this.fieldLabel.length) {
43759 tooltip: 'This field is required'
43765 cls: 'control-label',
43771 html: this.fieldLabel
43774 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43780 if(this.indicatorpos == 'right') {
43781 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43788 if(align == 'left') {
43796 if(this.labelWidth > 12){
43797 label.style = "width: " + this.labelWidth + 'px';
43799 if(this.labelWidth < 13 && this.labelmd == 0){
43800 this.labelmd = this.labelWidth;
43802 if(this.labellg > 0){
43803 label.cls += ' col-lg-' + this.labellg;
43804 input.cls += ' col-lg-' + (12 - this.labellg);
43806 if(this.labelmd > 0){
43807 label.cls += ' col-md-' + this.labelmd;
43808 container.cls += ' col-md-' + (12 - this.labelmd);
43810 if(this.labelsm > 0){
43811 label.cls += ' col-sm-' + this.labelsm;
43812 container.cls += ' col-sm-' + (12 - this.labelsm);
43814 if(this.labelxs > 0){
43815 label.cls += ' col-xs-' + this.labelxs;
43816 container.cls += ' col-xs-' + (12 - this.labelxs);
43826 var settings = this;
43828 ['xs','sm','md','lg'].map(function(size){
43829 if (settings[size]) {
43830 cfg.cls += ' col-' + size + '-' + settings[size];
43834 this.store = new Roo.data.Store({
43835 proxy : new Roo.data.MemoryProxy({}),
43836 reader : new Roo.data.JsonReader({
43847 'name' : 'dialCode',
43851 'name' : 'priority',
43855 'name' : 'areaCodes',
43862 if(!this.preferedCountries) {
43863 this.preferedCountries = [
43870 var p = this.preferedCountries.reverse();
43873 for (var i = 0; i < p.length; i++) {
43874 for (var j = 0; j < this.allCountries.length; j++) {
43875 if(this.allCountries[j].iso2 == p[i]) {
43876 var t = this.allCountries[j];
43877 this.allCountries.splice(j,1);
43878 this.allCountries.unshift(t);
43884 this.store.proxy.data = {
43886 data: this.allCountries
43892 initEvents : function()
43895 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43897 this.indicator = this.indicatorEl();
43898 this.flag = this.flagEl();
43899 this.dialCodeHolder = this.dialCodeHolderEl();
43901 this.trigger = this.el.select('div.flag-box',true).first();
43902 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43907 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43908 _this.list.setWidth(lw);
43911 this.list.on('mouseover', this.onViewOver, this);
43912 this.list.on('mousemove', this.onViewMove, this);
43913 this.inputEl().on("keyup", this.onKeyUp, this);
43914 this.inputEl().on("keypress", this.onKeyPress, this);
43916 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43918 this.view = new Roo.View(this.list, this.tpl, {
43919 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43922 this.view.on('click', this.onViewClick, this);
43923 this.setValue(this.defaultDialCode);
43926 onTriggerClick : function(e)
43928 Roo.log('trigger click');
43933 if(this.isExpanded()){
43935 this.hasFocus = false;
43937 this.store.load({});
43938 this.hasFocus = true;
43943 isExpanded : function()
43945 return this.list.isVisible();
43948 collapse : function()
43950 if(!this.isExpanded()){
43954 Roo.get(document).un('mousedown', this.collapseIf, this);
43955 Roo.get(document).un('mousewheel', this.collapseIf, this);
43956 this.fireEvent('collapse', this);
43960 expand : function()
43964 if(this.isExpanded() || !this.hasFocus){
43968 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43969 this.list.setWidth(lw);
43972 this.restrictHeight();
43974 Roo.get(document).on('mousedown', this.collapseIf, this);
43975 Roo.get(document).on('mousewheel', this.collapseIf, this);
43977 this.fireEvent('expand', this);
43980 restrictHeight : function()
43982 this.list.alignTo(this.inputEl(), this.listAlign);
43983 this.list.alignTo(this.inputEl(), this.listAlign);
43986 onViewOver : function(e, t)
43988 if(this.inKeyMode){
43991 var item = this.view.findItemFromChild(t);
43994 var index = this.view.indexOf(item);
43995 this.select(index, false);
44000 onViewClick : function(view, doFocus, el, e)
44002 var index = this.view.getSelectedIndexes()[0];
44004 var r = this.store.getAt(index);
44007 this.onSelect(r, index);
44009 if(doFocus !== false && !this.blockFocus){
44010 this.inputEl().focus();
44014 onViewMove : function(e, t)
44016 this.inKeyMode = false;
44019 select : function(index, scrollIntoView)
44021 this.selectedIndex = index;
44022 this.view.select(index);
44023 if(scrollIntoView !== false){
44024 var el = this.view.getNode(index);
44026 this.list.scrollChildIntoView(el, false);
44031 createList : function()
44033 this.list = Roo.get(document.body).createChild({
44035 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44036 style: 'display:none'
44039 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44042 collapseIf : function(e)
44044 var in_combo = e.within(this.el);
44045 var in_list = e.within(this.list);
44046 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44048 if (in_combo || in_list || is_list) {
44054 onSelect : function(record, index)
44056 if(this.fireEvent('beforeselect', this, record, index) !== false){
44058 this.setFlagClass(record.data.iso2);
44059 this.setDialCode(record.data.dialCode);
44060 this.hasFocus = false;
44062 this.fireEvent('select', this, record, index);
44066 flagEl : function()
44068 var flag = this.el.select('div.flag',true).first();
44075 dialCodeHolderEl : function()
44077 var d = this.el.select('input.dial-code-holder',true).first();
44084 setDialCode : function(v)
44086 this.dialCodeHolder.dom.value = '+'+v;
44089 setFlagClass : function(n)
44091 this.flag.dom.className = 'flag '+n;
44094 getValue : function()
44096 var v = this.inputEl().getValue();
44097 if(this.dialCodeHolder) {
44098 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44103 setValue : function(v)
44105 var d = this.getDialCode(v);
44107 //invalid dial code
44108 if(v.length == 0 || !d || d.length == 0) {
44110 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44111 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44117 this.setFlagClass(this.dialCodeMapping[d].iso2);
44118 this.setDialCode(d);
44119 this.inputEl().dom.value = v.replace('+'+d,'');
44120 this.hiddenEl().dom.value = this.getValue();
44125 getDialCode : function(v)
44129 if (v.length == 0) {
44130 return this.dialCodeHolder.dom.value;
44134 if (v.charAt(0) != "+") {
44137 var numericChars = "";
44138 for (var i = 1; i < v.length; i++) {
44139 var c = v.charAt(i);
44142 if (this.dialCodeMapping[numericChars]) {
44143 dialCode = v.substr(1, i);
44145 if (numericChars.length == 4) {
44155 this.setValue(this.defaultDialCode);
44159 hiddenEl : function()
44161 return this.el.select('input.hidden-tel-input',true).first();
44164 // after setting val
44165 onKeyUp : function(e){
44166 this.setValue(this.getValue());
44169 onKeyPress : function(e){
44170 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44177 * @class Roo.bootstrap.MoneyField
44178 * @extends Roo.bootstrap.ComboBox
44179 * Bootstrap MoneyField class
44182 * Create a new MoneyField.
44183 * @param {Object} config Configuration options
44186 Roo.bootstrap.MoneyField = function(config) {
44188 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44192 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44195 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44197 allowDecimals : true,
44199 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44201 decimalSeparator : ".",
44203 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44205 decimalPrecision : 0,
44207 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44209 allowNegative : true,
44211 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44215 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44217 minValue : Number.NEGATIVE_INFINITY,
44219 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44221 maxValue : Number.MAX_VALUE,
44223 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44225 minText : "The minimum value for this field is {0}",
44227 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44229 maxText : "The maximum value for this field is {0}",
44231 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44232 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44234 nanText : "{0} is not a valid number",
44236 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44240 * @cfg {String} defaults currency of the MoneyField
44241 * value should be in lkey
44243 defaultCurrency : false,
44245 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44247 thousandsDelimiter : false,
44249 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44260 getAutoCreate : function()
44262 var align = this.labelAlign || this.parentLabelAlign();
44274 cls : 'form-control roo-money-amount-input',
44275 autocomplete: 'new-password'
44278 var hiddenInput = {
44282 cls: 'hidden-number-input'
44285 if(this.max_length) {
44286 input.maxlength = this.max_length;
44290 hiddenInput.name = this.name;
44293 if (this.disabled) {
44294 input.disabled = true;
44297 var clg = 12 - this.inputlg;
44298 var cmd = 12 - this.inputmd;
44299 var csm = 12 - this.inputsm;
44300 var cxs = 12 - this.inputxs;
44304 cls : 'row roo-money-field',
44308 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44312 cls: 'roo-select2-container input-group',
44316 cls : 'form-control roo-money-currency-input',
44317 autocomplete: 'new-password',
44319 name : this.currencyName
44323 cls : 'input-group-addon',
44337 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44341 cls: this.hasFeedback ? 'has-feedback' : '',
44352 if (this.fieldLabel.length) {
44355 tooltip: 'This field is required'
44361 cls: 'control-label',
44367 html: this.fieldLabel
44370 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44376 if(this.indicatorpos == 'right') {
44377 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44384 if(align == 'left') {
44392 if(this.labelWidth > 12){
44393 label.style = "width: " + this.labelWidth + 'px';
44395 if(this.labelWidth < 13 && this.labelmd == 0){
44396 this.labelmd = this.labelWidth;
44398 if(this.labellg > 0){
44399 label.cls += ' col-lg-' + this.labellg;
44400 input.cls += ' col-lg-' + (12 - this.labellg);
44402 if(this.labelmd > 0){
44403 label.cls += ' col-md-' + this.labelmd;
44404 container.cls += ' col-md-' + (12 - this.labelmd);
44406 if(this.labelsm > 0){
44407 label.cls += ' col-sm-' + this.labelsm;
44408 container.cls += ' col-sm-' + (12 - this.labelsm);
44410 if(this.labelxs > 0){
44411 label.cls += ' col-xs-' + this.labelxs;
44412 container.cls += ' col-xs-' + (12 - this.labelxs);
44423 var settings = this;
44425 ['xs','sm','md','lg'].map(function(size){
44426 if (settings[size]) {
44427 cfg.cls += ' col-' + size + '-' + settings[size];
44434 initEvents : function()
44436 this.indicator = this.indicatorEl();
44438 this.initCurrencyEvent();
44440 this.initNumberEvent();
44443 initCurrencyEvent : function()
44446 throw "can not find store for combo";
44449 this.store = Roo.factory(this.store, Roo.data);
44450 this.store.parent = this;
44454 this.triggerEl = this.el.select('.input-group-addon', true).first();
44456 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44461 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44462 _this.list.setWidth(lw);
44465 this.list.on('mouseover', this.onViewOver, this);
44466 this.list.on('mousemove', this.onViewMove, this);
44467 this.list.on('scroll', this.onViewScroll, this);
44470 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44473 this.view = new Roo.View(this.list, this.tpl, {
44474 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44477 this.view.on('click', this.onViewClick, this);
44479 this.store.on('beforeload', this.onBeforeLoad, this);
44480 this.store.on('load', this.onLoad, this);
44481 this.store.on('loadexception', this.onLoadException, this);
44483 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44484 "up" : function(e){
44485 this.inKeyMode = true;
44489 "down" : function(e){
44490 if(!this.isExpanded()){
44491 this.onTriggerClick();
44493 this.inKeyMode = true;
44498 "enter" : function(e){
44501 if(this.fireEvent("specialkey", this, e)){
44502 this.onViewClick(false);
44508 "esc" : function(e){
44512 "tab" : function(e){
44515 if(this.fireEvent("specialkey", this, e)){
44516 this.onViewClick(false);
44524 doRelay : function(foo, bar, hname){
44525 if(hname == 'down' || this.scope.isExpanded()){
44526 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44534 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44538 initNumberEvent : function(e)
44540 this.inputEl().on("keydown" , this.fireKey, this);
44541 this.inputEl().on("focus", this.onFocus, this);
44542 this.inputEl().on("blur", this.onBlur, this);
44544 this.inputEl().relayEvent('keyup', this);
44546 if(this.indicator){
44547 this.indicator.addClass('invisible');
44550 this.originalValue = this.getValue();
44552 if(this.validationEvent == 'keyup'){
44553 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44554 this.inputEl().on('keyup', this.filterValidation, this);
44556 else if(this.validationEvent !== false){
44557 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44560 if(this.selectOnFocus){
44561 this.on("focus", this.preFocus, this);
44564 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44565 this.inputEl().on("keypress", this.filterKeys, this);
44567 this.inputEl().relayEvent('keypress', this);
44570 var allowed = "0123456789";
44572 if(this.allowDecimals){
44573 allowed += this.decimalSeparator;
44576 if(this.allowNegative){
44580 if(this.thousandsDelimiter) {
44584 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44586 var keyPress = function(e){
44588 var k = e.getKey();
44590 var c = e.getCharCode();
44593 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44594 allowed.indexOf(String.fromCharCode(c)) === -1
44600 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44604 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44609 this.inputEl().on("keypress", keyPress, this);
44613 onTriggerClick : function(e)
44620 this.loadNext = false;
44622 if(this.isExpanded()){
44627 this.hasFocus = true;
44629 if(this.triggerAction == 'all') {
44630 this.doQuery(this.allQuery, true);
44634 this.doQuery(this.getRawValue());
44637 getCurrency : function()
44639 var v = this.currencyEl().getValue();
44644 restrictHeight : function()
44646 this.list.alignTo(this.currencyEl(), this.listAlign);
44647 this.list.alignTo(this.currencyEl(), this.listAlign);
44650 onViewClick : function(view, doFocus, el, e)
44652 var index = this.view.getSelectedIndexes()[0];
44654 var r = this.store.getAt(index);
44657 this.onSelect(r, index);
44661 onSelect : function(record, index){
44663 if(this.fireEvent('beforeselect', this, record, index) !== false){
44665 this.setFromCurrencyData(index > -1 ? record.data : false);
44669 this.fireEvent('select', this, record, index);
44673 setFromCurrencyData : function(o)
44677 this.lastCurrency = o;
44679 if (this.currencyField) {
44680 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44682 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44685 this.lastSelectionText = currency;
44687 //setting default currency
44688 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44689 this.setCurrency(this.defaultCurrency);
44693 this.setCurrency(currency);
44696 setFromData : function(o)
44700 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44702 this.setFromCurrencyData(c);
44707 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44709 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44712 this.setValue(value);
44716 setCurrency : function(v)
44718 this.currencyValue = v;
44721 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44726 setValue : function(v)
44728 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44734 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44736 this.inputEl().dom.value = (v == '') ? '' :
44737 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44739 if(!this.allowZero && v === '0') {
44740 this.hiddenEl().dom.value = '';
44741 this.inputEl().dom.value = '';
44748 getRawValue : function()
44750 var v = this.inputEl().getValue();
44755 getValue : function()
44757 return this.fixPrecision(this.parseValue(this.getRawValue()));
44760 parseValue : function(value)
44762 if(this.thousandsDelimiter) {
44764 r = new RegExp(",", "g");
44765 value = value.replace(r, "");
44768 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44769 return isNaN(value) ? '' : value;
44773 fixPrecision : function(value)
44775 if(this.thousandsDelimiter) {
44777 r = new RegExp(",", "g");
44778 value = value.replace(r, "");
44781 var nan = isNaN(value);
44783 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44784 return nan ? '' : value;
44786 return parseFloat(value).toFixed(this.decimalPrecision);
44789 decimalPrecisionFcn : function(v)
44791 return Math.floor(v);
44794 validateValue : function(value)
44796 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44800 var num = this.parseValue(value);
44803 this.markInvalid(String.format(this.nanText, value));
44807 if(num < this.minValue){
44808 this.markInvalid(String.format(this.minText, this.minValue));
44812 if(num > this.maxValue){
44813 this.markInvalid(String.format(this.maxText, this.maxValue));
44820 validate : function()
44822 if(this.disabled || this.allowBlank){
44827 var currency = this.getCurrency();
44829 if(this.validateValue(this.getRawValue()) && currency.length){
44834 this.markInvalid();
44838 getName: function()
44843 beforeBlur : function()
44849 var v = this.parseValue(this.getRawValue());
44856 onBlur : function()
44860 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44861 //this.el.removeClass(this.focusClass);
44864 this.hasFocus = false;
44866 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44870 var v = this.getValue();
44872 if(String(v) !== String(this.startValue)){
44873 this.fireEvent('change', this, v, this.startValue);
44876 this.fireEvent("blur", this);
44879 inputEl : function()
44881 return this.el.select('.roo-money-amount-input', true).first();
44884 currencyEl : function()
44886 return this.el.select('.roo-money-currency-input', true).first();
44889 hiddenEl : function()
44891 return this.el.select('input.hidden-number-input',true).first();
44895 * @class Roo.bootstrap.BezierSignature
44896 * @extends Roo.bootstrap.Component
44897 * Bootstrap BezierSignature class
44898 * This script refer to:
44899 * Title: Signature Pad
44901 * Availability: https://github.com/szimek/signature_pad
44904 * Create a new BezierSignature
44905 * @param {Object} config The config object
44908 Roo.bootstrap.BezierSignature = function(config){
44909 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44915 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44922 mouse_btn_down: true,
44925 * @cfg {int} canvas height
44927 canvas_height: '200px',
44930 * @cfg {float|function} Radius of a single dot.
44935 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44940 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44945 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44950 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44955 * @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.
44957 bg_color: 'rgba(0, 0, 0, 0)',
44960 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44962 dot_color: 'black',
44965 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44967 velocity_filter_weight: 0.7,
44970 * @cfg {function} Callback when stroke begin.
44975 * @cfg {function} Callback when stroke end.
44979 getAutoCreate : function()
44981 var cls = 'roo-signature column';
44984 cls += ' ' + this.cls;
44994 for(var i = 0; i < col_sizes.length; i++) {
44995 if(this[col_sizes[i]]) {
44996 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45006 cls: 'roo-signature-body',
45010 cls: 'roo-signature-body-canvas',
45011 height: this.canvas_height,
45012 width: this.canvas_width
45019 style: 'display: none'
45027 initEvents: function()
45029 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45031 var canvas = this.canvasEl();
45033 // mouse && touch event swapping...
45034 canvas.dom.style.touchAction = 'none';
45035 canvas.dom.style.msTouchAction = 'none';
45037 this.mouse_btn_down = false;
45038 canvas.on('mousedown', this._handleMouseDown, this);
45039 canvas.on('mousemove', this._handleMouseMove, this);
45040 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45042 if (window.PointerEvent) {
45043 canvas.on('pointerdown', this._handleMouseDown, this);
45044 canvas.on('pointermove', this._handleMouseMove, this);
45045 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45048 if ('ontouchstart' in window) {
45049 canvas.on('touchstart', this._handleTouchStart, this);
45050 canvas.on('touchmove', this._handleTouchMove, this);
45051 canvas.on('touchend', this._handleTouchEnd, this);
45054 Roo.EventManager.onWindowResize(this.resize, this, true);
45056 // file input event
45057 this.fileEl().on('change', this.uploadImage, this);
45064 resize: function(){
45066 var canvas = this.canvasEl().dom;
45067 var ctx = this.canvasElCtx();
45068 var img_data = false;
45070 if(canvas.width > 0) {
45071 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45073 // setting canvas width will clean img data
45076 var style = window.getComputedStyle ?
45077 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45079 var padding_left = parseInt(style.paddingLeft) || 0;
45080 var padding_right = parseInt(style.paddingRight) || 0;
45082 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45085 ctx.putImageData(img_data, 0, 0);
45089 _handleMouseDown: function(e)
45091 if (e.browserEvent.which === 1) {
45092 this.mouse_btn_down = true;
45093 this.strokeBegin(e);
45097 _handleMouseMove: function (e)
45099 if (this.mouse_btn_down) {
45100 this.strokeMoveUpdate(e);
45104 _handleMouseUp: function (e)
45106 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45107 this.mouse_btn_down = false;
45112 _handleTouchStart: function (e) {
45114 e.preventDefault();
45115 if (e.browserEvent.targetTouches.length === 1) {
45116 // var touch = e.browserEvent.changedTouches[0];
45117 // this.strokeBegin(touch);
45119 this.strokeBegin(e); // assume e catching the correct xy...
45123 _handleTouchMove: function (e) {
45124 e.preventDefault();
45125 // var touch = event.targetTouches[0];
45126 // _this._strokeMoveUpdate(touch);
45127 this.strokeMoveUpdate(e);
45130 _handleTouchEnd: function (e) {
45131 var wasCanvasTouched = e.target === this.canvasEl().dom;
45132 if (wasCanvasTouched) {
45133 e.preventDefault();
45134 // var touch = event.changedTouches[0];
45135 // _this._strokeEnd(touch);
45140 reset: function () {
45141 this._lastPoints = [];
45142 this._lastVelocity = 0;
45143 this._lastWidth = (this.min_width + this.max_width) / 2;
45144 this.canvasElCtx().fillStyle = this.dot_color;
45147 strokeMoveUpdate: function(e)
45149 this.strokeUpdate(e);
45151 if (this.throttle) {
45152 this.throttleStroke(this.strokeUpdate, this.throttle);
45155 this.strokeUpdate(e);
45159 strokeBegin: function(e)
45161 var newPointGroup = {
45162 color: this.dot_color,
45166 if (typeof this.onBegin === 'function') {
45170 this.curve_data.push(newPointGroup);
45172 this.strokeUpdate(e);
45175 strokeUpdate: function(e)
45177 var rect = this.canvasEl().dom.getBoundingClientRect();
45178 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45179 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45180 var lastPoints = lastPointGroup.points;
45181 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45182 var isLastPointTooClose = lastPoint
45183 ? point.distanceTo(lastPoint) <= this.min_distance
45185 var color = lastPointGroup.color;
45186 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45187 var curve = this.addPoint(point);
45189 this.drawDot({color: color, point: point});
45192 this.drawCurve({color: color, curve: curve});
45202 strokeEnd: function(e)
45204 this.strokeUpdate(e);
45205 if (typeof this.onEnd === 'function') {
45210 addPoint: function (point) {
45211 var _lastPoints = this._lastPoints;
45212 _lastPoints.push(point);
45213 if (_lastPoints.length > 2) {
45214 if (_lastPoints.length === 3) {
45215 _lastPoints.unshift(_lastPoints[0]);
45217 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45218 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45219 _lastPoints.shift();
45225 calculateCurveWidths: function (startPoint, endPoint) {
45226 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45227 (1 - this.velocity_filter_weight) * this._lastVelocity;
45229 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45232 start: this._lastWidth
45235 this._lastVelocity = velocity;
45236 this._lastWidth = newWidth;
45240 drawDot: function (_a) {
45241 var color = _a.color, point = _a.point;
45242 var ctx = this.canvasElCtx();
45243 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45245 this.drawCurveSegment(point.x, point.y, width);
45247 ctx.fillStyle = color;
45251 drawCurve: function (_a) {
45252 var color = _a.color, curve = _a.curve;
45253 var ctx = this.canvasElCtx();
45254 var widthDelta = curve.endWidth - curve.startWidth;
45255 var drawSteps = Math.floor(curve.length()) * 2;
45257 ctx.fillStyle = color;
45258 for (var i = 0; i < drawSteps; i += 1) {
45259 var t = i / drawSteps;
45265 var x = uuu * curve.startPoint.x;
45266 x += 3 * uu * t * curve.control1.x;
45267 x += 3 * u * tt * curve.control2.x;
45268 x += ttt * curve.endPoint.x;
45269 var y = uuu * curve.startPoint.y;
45270 y += 3 * uu * t * curve.control1.y;
45271 y += 3 * u * tt * curve.control2.y;
45272 y += ttt * curve.endPoint.y;
45273 var width = curve.startWidth + ttt * widthDelta;
45274 this.drawCurveSegment(x, y, width);
45280 drawCurveSegment: function (x, y, width) {
45281 var ctx = this.canvasElCtx();
45283 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45284 this.is_empty = false;
45289 var ctx = this.canvasElCtx();
45290 var canvas = this.canvasEl().dom;
45291 ctx.fillStyle = this.bg_color;
45292 ctx.clearRect(0, 0, canvas.width, canvas.height);
45293 ctx.fillRect(0, 0, canvas.width, canvas.height);
45294 this.curve_data = [];
45296 this.is_empty = true;
45301 return this.el.select('input',true).first();
45304 canvasEl: function()
45306 return this.el.select('canvas',true).first();
45309 canvasElCtx: function()
45311 return this.el.select('canvas',true).first().dom.getContext('2d');
45314 getImage: function(type)
45316 if(this.is_empty) {
45321 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45324 drawFromImage: function(img_src)
45326 var img = new Image();
45328 img.onload = function(){
45329 this.canvasElCtx().drawImage(img, 0, 0);
45334 this.is_empty = false;
45337 selectImage: function()
45339 this.fileEl().dom.click();
45342 uploadImage: function(e)
45344 var reader = new FileReader();
45346 reader.onload = function(e){
45347 var img = new Image();
45348 img.onload = function(){
45350 this.canvasElCtx().drawImage(img, 0, 0);
45352 img.src = e.target.result;
45355 reader.readAsDataURL(e.target.files[0]);
45358 // Bezier Point Constructor
45359 Point: (function () {
45360 function Point(x, y, time) {
45363 this.time = time || Date.now();
45365 Point.prototype.distanceTo = function (start) {
45366 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45368 Point.prototype.equals = function (other) {
45369 return this.x === other.x && this.y === other.y && this.time === other.time;
45371 Point.prototype.velocityFrom = function (start) {
45372 return this.time !== start.time
45373 ? this.distanceTo(start) / (this.time - start.time)
45380 // Bezier Constructor
45381 Bezier: (function () {
45382 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45383 this.startPoint = startPoint;
45384 this.control2 = control2;
45385 this.control1 = control1;
45386 this.endPoint = endPoint;
45387 this.startWidth = startWidth;
45388 this.endWidth = endWidth;
45390 Bezier.fromPoints = function (points, widths, scope) {
45391 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45392 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45393 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45395 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45396 var dx1 = s1.x - s2.x;
45397 var dy1 = s1.y - s2.y;
45398 var dx2 = s2.x - s3.x;
45399 var dy2 = s2.y - s3.y;
45400 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45401 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45402 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45403 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45404 var dxm = m1.x - m2.x;
45405 var dym = m1.y - m2.y;
45406 var k = l2 / (l1 + l2);
45407 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45408 var tx = s2.x - cm.x;
45409 var ty = s2.y - cm.y;
45411 c1: new scope.Point(m1.x + tx, m1.y + ty),
45412 c2: new scope.Point(m2.x + tx, m2.y + ty)
45415 Bezier.prototype.length = function () {
45420 for (var i = 0; i <= steps; i += 1) {
45422 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45423 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45425 var xdiff = cx - px;
45426 var ydiff = cy - py;
45427 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45434 Bezier.prototype.point = function (t, start, c1, c2, end) {
45435 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45436 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45437 + (3.0 * c2 * (1.0 - t) * t * t)
45438 + (end * t * t * t);
45443 throttleStroke: function(fn, wait) {
45444 if (wait === void 0) { wait = 250; }
45446 var timeout = null;
45450 var later = function () {
45451 previous = Date.now();
45453 result = fn.apply(storedContext, storedArgs);
45455 storedContext = null;
45459 return function wrapper() {
45461 for (var _i = 0; _i < arguments.length; _i++) {
45462 args[_i] = arguments[_i];
45464 var now = Date.now();
45465 var remaining = wait - (now - previous);
45466 storedContext = this;
45468 if (remaining <= 0 || remaining > wait) {
45470 clearTimeout(timeout);
45474 result = fn.apply(storedContext, storedArgs);
45476 storedContext = null;
45480 else if (!timeout) {
45481 timeout = window.setTimeout(later, remaining);