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
15571 * This class is an abstract base class for implementations which provide retrieval of
15572 * unformatted data objects.<br>
15574 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15575 * (of the appropriate type which knows how to parse the data object) to provide a block of
15576 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15578 * Custom implementations must implement the load method as described in
15579 * {@link Roo.data.HttpProxy#load}.
15581 Roo.data.DataProxy = function(){
15584 * @event beforeload
15585 * Fires before a network request is made to retrieve a data object.
15586 * @param {Object} This DataProxy object.
15587 * @param {Object} params The params parameter to the load function.
15592 * Fires before the load method's callback is called.
15593 * @param {Object} This DataProxy object.
15594 * @param {Object} o The data object.
15595 * @param {Object} arg The callback argument object passed to the load function.
15599 * @event loadexception
15600 * Fires if an Exception occurs during data retrieval.
15601 * @param {Object} This DataProxy object.
15602 * @param {Object} o The data object.
15603 * @param {Object} arg The callback argument object passed to the load function.
15604 * @param {Object} e The Exception.
15606 loadexception : true
15608 Roo.data.DataProxy.superclass.constructor.call(this);
15611 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15614 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15618 * Ext JS Library 1.1.1
15619 * Copyright(c) 2006-2007, Ext JS, LLC.
15621 * Originally Released Under LGPL - original licence link has changed is not relivant.
15624 * <script type="text/javascript">
15627 * @class Roo.data.MemoryProxy
15628 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15629 * to the Reader when its load method is called.
15631 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15633 Roo.data.MemoryProxy = function(data){
15637 Roo.data.MemoryProxy.superclass.constructor.call(this);
15641 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15644 * Load data from the requested source (in this case an in-memory
15645 * data object passed to the constructor), read the data object into
15646 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15647 * process that block using the passed callback.
15648 * @param {Object} params This parameter is not used by the MemoryProxy class.
15649 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15650 * object into a block of Roo.data.Records.
15651 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15652 * The function must be passed <ul>
15653 * <li>The Record block object</li>
15654 * <li>The "arg" argument from the load function</li>
15655 * <li>A boolean success indicator</li>
15657 * @param {Object} scope The scope in which to call the callback
15658 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15660 load : function(params, reader, callback, scope, arg){
15661 params = params || {};
15664 result = reader.readRecords(params.data ? params.data :this.data);
15666 this.fireEvent("loadexception", this, arg, null, e);
15667 callback.call(scope, null, arg, false);
15670 callback.call(scope, result, arg, true);
15674 update : function(params, records){
15679 * Ext JS Library 1.1.1
15680 * Copyright(c) 2006-2007, Ext JS, LLC.
15682 * Originally Released Under LGPL - original licence link has changed is not relivant.
15685 * <script type="text/javascript">
15688 * @class Roo.data.HttpProxy
15689 * @extends Roo.data.DataProxy
15690 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15691 * configured to reference a certain URL.<br><br>
15693 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15694 * from which the running page was served.<br><br>
15696 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15698 * Be aware that to enable the browser to parse an XML document, the server must set
15699 * the Content-Type header in the HTTP response to "text/xml".
15701 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15702 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15703 * will be used to make the request.
15705 Roo.data.HttpProxy = function(conn){
15706 Roo.data.HttpProxy.superclass.constructor.call(this);
15707 // is conn a conn config or a real conn?
15709 this.useAjax = !conn || !conn.events;
15713 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15714 // thse are take from connection...
15717 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15720 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15721 * extra parameters to each request made by this object. (defaults to undefined)
15724 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15725 * to each request made by this object. (defaults to undefined)
15728 * @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)
15731 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15734 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15740 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15744 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15745 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15746 * a finer-grained basis than the DataProxy events.
15748 getConnection : function(){
15749 return this.useAjax ? Roo.Ajax : this.conn;
15753 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15754 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15755 * process that block using the passed callback.
15756 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15757 * for the request to the remote server.
15758 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15759 * object into a block of Roo.data.Records.
15760 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15761 * The function must be passed <ul>
15762 * <li>The Record block object</li>
15763 * <li>The "arg" argument from the load function</li>
15764 * <li>A boolean success indicator</li>
15766 * @param {Object} scope The scope in which to call the callback
15767 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15769 load : function(params, reader, callback, scope, arg){
15770 if(this.fireEvent("beforeload", this, params) !== false){
15772 params : params || {},
15774 callback : callback,
15779 callback : this.loadResponse,
15783 Roo.applyIf(o, this.conn);
15784 if(this.activeRequest){
15785 Roo.Ajax.abort(this.activeRequest);
15787 this.activeRequest = Roo.Ajax.request(o);
15789 this.conn.request(o);
15792 callback.call(scope||this, null, arg, false);
15797 loadResponse : function(o, success, response){
15798 delete this.activeRequest;
15800 this.fireEvent("loadexception", this, o, response);
15801 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15806 result = o.reader.read(response);
15808 this.fireEvent("loadexception", this, o, response, e);
15809 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15813 this.fireEvent("load", this, o, o.request.arg);
15814 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15818 update : function(dataSet){
15823 updateResponse : function(dataSet){
15828 * Ext JS Library 1.1.1
15829 * Copyright(c) 2006-2007, Ext JS, LLC.
15831 * Originally Released Under LGPL - original licence link has changed is not relivant.
15834 * <script type="text/javascript">
15838 * @class Roo.data.ScriptTagProxy
15839 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15840 * other than the originating domain of the running page.<br><br>
15842 * <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
15843 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15845 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15846 * source code that is used as the source inside a <script> tag.<br><br>
15848 * In order for the browser to process the returned data, the server must wrap the data object
15849 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15850 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15851 * depending on whether the callback name was passed:
15854 boolean scriptTag = false;
15855 String cb = request.getParameter("callback");
15858 response.setContentType("text/javascript");
15860 response.setContentType("application/x-json");
15862 Writer out = response.getWriter();
15864 out.write(cb + "(");
15866 out.print(dataBlock.toJsonString());
15873 * @param {Object} config A configuration object.
15875 Roo.data.ScriptTagProxy = function(config){
15876 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15877 Roo.apply(this, config);
15878 this.head = document.getElementsByTagName("head")[0];
15881 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15883 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15885 * @cfg {String} url The URL from which to request the data object.
15888 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15892 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15893 * the server the name of the callback function set up by the load call to process the returned data object.
15894 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15895 * javascript output which calls this named function passing the data object as its only parameter.
15897 callbackParam : "callback",
15899 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15900 * name to the request.
15905 * Load data from the configured URL, read the data object into
15906 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15907 * process that block using the passed callback.
15908 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15909 * for the request to the remote server.
15910 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15911 * object into a block of Roo.data.Records.
15912 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15913 * The function must be passed <ul>
15914 * <li>The Record block object</li>
15915 * <li>The "arg" argument from the load function</li>
15916 * <li>A boolean success indicator</li>
15918 * @param {Object} scope The scope in which to call the callback
15919 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15921 load : function(params, reader, callback, scope, arg){
15922 if(this.fireEvent("beforeload", this, params) !== false){
15924 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15926 var url = this.url;
15927 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15929 url += "&_dc=" + (new Date().getTime());
15931 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15934 cb : "stcCallback"+transId,
15935 scriptId : "stcScript"+transId,
15939 callback : callback,
15945 window[trans.cb] = function(o){
15946 conn.handleResponse(o, trans);
15949 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15951 if(this.autoAbort !== false){
15955 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15957 var script = document.createElement("script");
15958 script.setAttribute("src", url);
15959 script.setAttribute("type", "text/javascript");
15960 script.setAttribute("id", trans.scriptId);
15961 this.head.appendChild(script);
15963 this.trans = trans;
15965 callback.call(scope||this, null, arg, false);
15970 isLoading : function(){
15971 return this.trans ? true : false;
15975 * Abort the current server request.
15977 abort : function(){
15978 if(this.isLoading()){
15979 this.destroyTrans(this.trans);
15984 destroyTrans : function(trans, isLoaded){
15985 this.head.removeChild(document.getElementById(trans.scriptId));
15986 clearTimeout(trans.timeoutId);
15988 window[trans.cb] = undefined;
15990 delete window[trans.cb];
15993 // if hasn't been loaded, wait for load to remove it to prevent script error
15994 window[trans.cb] = function(){
15995 window[trans.cb] = undefined;
15997 delete window[trans.cb];
16004 handleResponse : function(o, trans){
16005 this.trans = false;
16006 this.destroyTrans(trans, true);
16009 result = trans.reader.readRecords(o);
16011 this.fireEvent("loadexception", this, o, trans.arg, e);
16012 trans.callback.call(trans.scope||window, null, trans.arg, false);
16015 this.fireEvent("load", this, o, trans.arg);
16016 trans.callback.call(trans.scope||window, result, trans.arg, true);
16020 handleFailure : function(trans){
16021 this.trans = false;
16022 this.destroyTrans(trans, false);
16023 this.fireEvent("loadexception", this, null, trans.arg);
16024 trans.callback.call(trans.scope||window, null, trans.arg, false);
16028 * Ext JS Library 1.1.1
16029 * Copyright(c) 2006-2007, Ext JS, LLC.
16031 * Originally Released Under LGPL - original licence link has changed is not relivant.
16034 * <script type="text/javascript">
16038 * @class Roo.data.JsonReader
16039 * @extends Roo.data.DataReader
16040 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16041 * based on mappings in a provided Roo.data.Record constructor.
16043 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16044 * in the reply previously.
16049 var RecordDef = Roo.data.Record.create([
16050 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16051 {name: 'occupation'} // This field will use "occupation" as the mapping.
16053 var myReader = new Roo.data.JsonReader({
16054 totalProperty: "results", // The property which contains the total dataset size (optional)
16055 root: "rows", // The property which contains an Array of row objects
16056 id: "id" // The property within each row object that provides an ID for the record (optional)
16060 * This would consume a JSON file like this:
16062 { 'results': 2, 'rows': [
16063 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16064 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16067 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16068 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16069 * paged from the remote server.
16070 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16071 * @cfg {String} root name of the property which contains the Array of row objects.
16072 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16073 * @cfg {Array} fields Array of field definition objects
16075 * Create a new JsonReader
16076 * @param {Object} meta Metadata configuration options
16077 * @param {Object} recordType Either an Array of field definition objects,
16078 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16080 Roo.data.JsonReader = function(meta, recordType){
16083 // set some defaults:
16084 Roo.applyIf(meta, {
16085 totalProperty: 'total',
16086 successProperty : 'success',
16091 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16093 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16095 readerType : 'Json',
16098 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16099 * Used by Store query builder to append _requestMeta to params.
16102 metaFromRemote : false,
16104 * This method is only used by a DataProxy which has retrieved data from a remote server.
16105 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16106 * @return {Object} data A data block which is used by an Roo.data.Store object as
16107 * a cache of Roo.data.Records.
16109 read : function(response){
16110 var json = response.responseText;
16112 var o = /* eval:var:o */ eval("("+json+")");
16114 throw {message: "JsonReader.read: Json object not found"};
16120 this.metaFromRemote = true;
16121 this.meta = o.metaData;
16122 this.recordType = Roo.data.Record.create(o.metaData.fields);
16123 this.onMetaChange(this.meta, this.recordType, o);
16125 return this.readRecords(o);
16128 // private function a store will implement
16129 onMetaChange : function(meta, recordType, o){
16136 simpleAccess: function(obj, subsc) {
16143 getJsonAccessor: function(){
16145 return function(expr) {
16147 return(re.test(expr))
16148 ? new Function("obj", "return obj." + expr)
16153 return Roo.emptyFn;
16158 * Create a data block containing Roo.data.Records from an XML document.
16159 * @param {Object} o An object which contains an Array of row objects in the property specified
16160 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16161 * which contains the total size of the dataset.
16162 * @return {Object} data A data block which is used by an Roo.data.Store object as
16163 * a cache of Roo.data.Records.
16165 readRecords : function(o){
16167 * After any data loads, the raw JSON data is available for further custom processing.
16171 var s = this.meta, Record = this.recordType,
16172 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16174 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16176 if(s.totalProperty) {
16177 this.getTotal = this.getJsonAccessor(s.totalProperty);
16179 if(s.successProperty) {
16180 this.getSuccess = this.getJsonAccessor(s.successProperty);
16182 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16184 var g = this.getJsonAccessor(s.id);
16185 this.getId = function(rec) {
16187 return (r === undefined || r === "") ? null : r;
16190 this.getId = function(){return null;};
16193 for(var jj = 0; jj < fl; jj++){
16195 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16196 this.ef[jj] = this.getJsonAccessor(map);
16200 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16201 if(s.totalProperty){
16202 var vt = parseInt(this.getTotal(o), 10);
16207 if(s.successProperty){
16208 var vs = this.getSuccess(o);
16209 if(vs === false || vs === 'false'){
16214 for(var i = 0; i < c; i++){
16217 var id = this.getId(n);
16218 for(var j = 0; j < fl; j++){
16220 var v = this.ef[j](n);
16222 Roo.log('missing convert for ' + f.name);
16226 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16228 var record = new Record(values, id);
16230 records[i] = record;
16236 totalRecords : totalRecords
16239 // used when loading children.. @see loadDataFromChildren
16240 toLoadData: function(rec)
16242 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16243 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16244 return { data : data, total : data.length };
16249 * Ext JS Library 1.1.1
16250 * Copyright(c) 2006-2007, Ext JS, LLC.
16252 * Originally Released Under LGPL - original licence link has changed is not relivant.
16255 * <script type="text/javascript">
16259 * @class Roo.data.ArrayReader
16260 * @extends Roo.data.DataReader
16261 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16262 * Each element of that Array represents a row of data fields. The
16263 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16264 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16268 var RecordDef = Roo.data.Record.create([
16269 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16270 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16272 var myReader = new Roo.data.ArrayReader({
16273 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16277 * This would consume an Array like this:
16279 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16283 * Create a new JsonReader
16284 * @param {Object} meta Metadata configuration options.
16285 * @param {Object|Array} recordType Either an Array of field definition objects
16287 * @cfg {Array} fields Array of field definition objects
16288 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16289 * as specified to {@link Roo.data.Record#create},
16290 * or an {@link Roo.data.Record} object
16293 * created using {@link Roo.data.Record#create}.
16295 Roo.data.ArrayReader = function(meta, recordType)
16297 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16300 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16303 * Create a data block containing Roo.data.Records from an XML document.
16304 * @param {Object} o An Array of row objects which represents the dataset.
16305 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16306 * a cache of Roo.data.Records.
16308 readRecords : function(o)
16310 var sid = this.meta ? this.meta.id : null;
16311 var recordType = this.recordType, fields = recordType.prototype.fields;
16314 for(var i = 0; i < root.length; i++){
16317 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16318 for(var j = 0, jlen = fields.length; j < jlen; j++){
16319 var f = fields.items[j];
16320 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16321 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16323 values[f.name] = v;
16325 var record = new recordType(values, id);
16327 records[records.length] = record;
16331 totalRecords : records.length
16334 // used when loading children.. @see loadDataFromChildren
16335 toLoadData: function(rec)
16337 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16338 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16349 * @class Roo.bootstrap.ComboBox
16350 * @extends Roo.bootstrap.TriggerField
16351 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16352 * @cfg {Boolean} append (true|false) default false
16353 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16354 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16355 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16356 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16357 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16358 * @cfg {Boolean} animate default true
16359 * @cfg {Boolean} emptyResultText only for touch device
16360 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16361 * @cfg {String} emptyTitle default ''
16362 * @cfg {Number} width fixed with? experimental
16364 * Create a new ComboBox.
16365 * @param {Object} config Configuration options
16367 Roo.bootstrap.ComboBox = function(config){
16368 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16372 * Fires when the dropdown list is expanded
16373 * @param {Roo.bootstrap.ComboBox} combo This combo box
16378 * Fires when the dropdown list is collapsed
16379 * @param {Roo.bootstrap.ComboBox} combo This combo box
16383 * @event beforeselect
16384 * Fires before a list item is selected. Return false to cancel the selection.
16385 * @param {Roo.bootstrap.ComboBox} combo This combo box
16386 * @param {Roo.data.Record} record The data record returned from the underlying store
16387 * @param {Number} index The index of the selected item in the dropdown list
16389 'beforeselect' : true,
16392 * Fires when a list item is selected
16393 * @param {Roo.bootstrap.ComboBox} combo This combo box
16394 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16395 * @param {Number} index The index of the selected item in the dropdown list
16399 * @event beforequery
16400 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16401 * The event object passed has these properties:
16402 * @param {Roo.bootstrap.ComboBox} combo This combo box
16403 * @param {String} query The query
16404 * @param {Boolean} forceAll true to force "all" query
16405 * @param {Boolean} cancel true to cancel the query
16406 * @param {Object} e The query event object
16408 'beforequery': true,
16411 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16412 * @param {Roo.bootstrap.ComboBox} combo This combo box
16417 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16418 * @param {Roo.bootstrap.ComboBox} combo This combo box
16419 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16424 * Fires when the remove value from the combobox array
16425 * @param {Roo.bootstrap.ComboBox} combo This combo box
16429 * @event afterremove
16430 * Fires when the remove value from the combobox array
16431 * @param {Roo.bootstrap.ComboBox} combo This combo box
16433 'afterremove' : true,
16435 * @event specialfilter
16436 * Fires when specialfilter
16437 * @param {Roo.bootstrap.ComboBox} combo This combo box
16439 'specialfilter' : true,
16442 * Fires when tick the element
16443 * @param {Roo.bootstrap.ComboBox} combo This combo box
16447 * @event touchviewdisplay
16448 * Fires when touch view require special display (default is using displayField)
16449 * @param {Roo.bootstrap.ComboBox} combo This combo box
16450 * @param {Object} cfg set html .
16452 'touchviewdisplay' : true
16457 this.tickItems = [];
16459 this.selectedIndex = -1;
16460 if(this.mode == 'local'){
16461 if(config.queryDelay === undefined){
16462 this.queryDelay = 10;
16464 if(config.minChars === undefined){
16470 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16473 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16474 * rendering into an Roo.Editor, defaults to false)
16477 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16478 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16481 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16484 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16485 * the dropdown list (defaults to undefined, with no header element)
16489 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16493 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16495 listWidth: undefined,
16497 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16498 * mode = 'remote' or 'text' if mode = 'local')
16500 displayField: undefined,
16503 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16504 * mode = 'remote' or 'value' if mode = 'local').
16505 * Note: use of a valueField requires the user make a selection
16506 * in order for a value to be mapped.
16508 valueField: undefined,
16510 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16515 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16516 * field's data value (defaults to the underlying DOM element's name)
16518 hiddenName: undefined,
16520 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16524 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16526 selectedClass: 'active',
16529 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16533 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16534 * anchor positions (defaults to 'tl-bl')
16536 listAlign: 'tl-bl?',
16538 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16542 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16543 * query specified by the allQuery config option (defaults to 'query')
16545 triggerAction: 'query',
16547 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16548 * (defaults to 4, does not apply if editable = false)
16552 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16553 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16557 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16558 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16562 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16563 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16567 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16568 * when editable = true (defaults to false)
16570 selectOnFocus:false,
16572 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16574 queryParam: 'query',
16576 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16577 * when mode = 'remote' (defaults to 'Loading...')
16579 loadingText: 'Loading...',
16581 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16585 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16589 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16590 * traditional select (defaults to true)
16594 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16598 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16602 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16603 * listWidth has a higher value)
16607 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16608 * allow the user to set arbitrary text into the field (defaults to false)
16610 forceSelection:false,
16612 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16613 * if typeAhead = true (defaults to 250)
16615 typeAheadDelay : 250,
16617 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16618 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16620 valueNotFoundText : undefined,
16622 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16624 blockFocus : false,
16627 * @cfg {Boolean} disableClear Disable showing of clear button.
16629 disableClear : false,
16631 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16633 alwaysQuery : false,
16636 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16641 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16643 invalidClass : "has-warning",
16646 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16648 validClass : "has-success",
16651 * @cfg {Boolean} specialFilter (true|false) special filter default false
16653 specialFilter : false,
16656 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16658 mobileTouchView : true,
16661 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16663 useNativeIOS : false,
16666 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16668 mobile_restrict_height : false,
16670 ios_options : false,
16682 btnPosition : 'right',
16683 triggerList : true,
16684 showToggleBtn : true,
16686 emptyResultText: 'Empty',
16687 triggerText : 'Select',
16691 // element that contains real text value.. (when hidden is used..)
16693 getAutoCreate : function()
16698 * Render classic select for iso
16701 if(Roo.isIOS && this.useNativeIOS){
16702 cfg = this.getAutoCreateNativeIOS();
16710 if(Roo.isTouch && this.mobileTouchView){
16711 cfg = this.getAutoCreateTouchView();
16718 if(!this.tickable){
16719 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16724 * ComboBox with tickable selections
16727 var align = this.labelAlign || this.parentLabelAlign();
16730 cls : 'form-group roo-combobox-tickable' //input-group
16733 var btn_text_select = '';
16734 var btn_text_done = '';
16735 var btn_text_cancel = '';
16737 if (this.btn_text_show) {
16738 btn_text_select = 'Select';
16739 btn_text_done = 'Done';
16740 btn_text_cancel = 'Cancel';
16745 cls : 'tickable-buttons',
16750 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16751 //html : this.triggerText
16752 html: btn_text_select
16758 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16760 html: btn_text_done
16766 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16768 html: btn_text_cancel
16774 buttons.cn.unshift({
16776 cls: 'roo-select2-search-field-input'
16782 Roo.each(buttons.cn, function(c){
16784 c.cls += ' btn-' + _this.size;
16787 if (_this.disabled) {
16794 style : 'display: contents',
16799 cls: 'form-hidden-field'
16803 cls: 'roo-select2-choices',
16807 cls: 'roo-select2-search-field',
16818 cls: 'roo-select2-container input-group roo-select2-container-multi',
16824 // cls: 'typeahead typeahead-long dropdown-menu',
16825 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16830 if(this.hasFeedback && !this.allowBlank){
16834 cls: 'glyphicon form-control-feedback'
16837 combobox.cn.push(feedback);
16844 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16845 tooltip : 'This field is required'
16847 if (Roo.bootstrap.version == 4) {
16850 style : 'display:none'
16853 if (align ==='left' && this.fieldLabel.length) {
16855 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16862 cls : 'control-label col-form-label',
16863 html : this.fieldLabel
16875 var labelCfg = cfg.cn[1];
16876 var contentCfg = cfg.cn[2];
16879 if(this.indicatorpos == 'right'){
16885 cls : 'control-label col-form-label',
16889 html : this.fieldLabel
16905 labelCfg = cfg.cn[0];
16906 contentCfg = cfg.cn[1];
16910 if(this.labelWidth > 12){
16911 labelCfg.style = "width: " + this.labelWidth + 'px';
16913 if(this.width * 1 > 0){
16914 contentCfg.style = "width: " + this.width + 'px';
16916 if(this.labelWidth < 13 && this.labelmd == 0){
16917 this.labelmd = this.labelWidth;
16920 if(this.labellg > 0){
16921 labelCfg.cls += ' col-lg-' + this.labellg;
16922 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16925 if(this.labelmd > 0){
16926 labelCfg.cls += ' col-md-' + this.labelmd;
16927 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16930 if(this.labelsm > 0){
16931 labelCfg.cls += ' col-sm-' + this.labelsm;
16932 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16935 if(this.labelxs > 0){
16936 labelCfg.cls += ' col-xs-' + this.labelxs;
16937 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16941 } else if ( this.fieldLabel.length) {
16942 // Roo.log(" label");
16947 //cls : 'input-group-addon',
16948 html : this.fieldLabel
16953 if(this.indicatorpos == 'right'){
16957 //cls : 'input-group-addon',
16958 html : this.fieldLabel
16968 // Roo.log(" no label && no align");
16975 ['xs','sm','md','lg'].map(function(size){
16976 if (settings[size]) {
16977 cfg.cls += ' col-' + size + '-' + settings[size];
16985 _initEventsCalled : false,
16988 initEvents: function()
16990 if (this._initEventsCalled) { // as we call render... prevent looping...
16993 this._initEventsCalled = true;
16996 throw "can not find store for combo";
16999 this.indicator = this.indicatorEl();
17001 this.store = Roo.factory(this.store, Roo.data);
17002 this.store.parent = this;
17004 // if we are building from html. then this element is so complex, that we can not really
17005 // use the rendered HTML.
17006 // so we have to trash and replace the previous code.
17007 if (Roo.XComponent.build_from_html) {
17008 // remove this element....
17009 var e = this.el.dom, k=0;
17010 while (e ) { e = e.previousSibling; ++k;}
17015 this.rendered = false;
17017 this.render(this.parent().getChildContainer(true), k);
17020 if(Roo.isIOS && this.useNativeIOS){
17021 this.initIOSView();
17029 if(Roo.isTouch && this.mobileTouchView){
17030 this.initTouchView();
17035 this.initTickableEvents();
17039 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17041 if(this.hiddenName){
17043 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17045 this.hiddenField.dom.value =
17046 this.hiddenValue !== undefined ? this.hiddenValue :
17047 this.value !== undefined ? this.value : '';
17049 // prevent input submission
17050 this.el.dom.removeAttribute('name');
17051 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17056 // this.el.dom.setAttribute('autocomplete', 'off');
17059 var cls = 'x-combo-list';
17061 //this.list = new Roo.Layer({
17062 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17068 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17069 _this.list.setWidth(lw);
17072 this.list.on('mouseover', this.onViewOver, this);
17073 this.list.on('mousemove', this.onViewMove, this);
17074 this.list.on('scroll', this.onViewScroll, this);
17077 this.list.swallowEvent('mousewheel');
17078 this.assetHeight = 0;
17081 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17082 this.assetHeight += this.header.getHeight();
17085 this.innerList = this.list.createChild({cls:cls+'-inner'});
17086 this.innerList.on('mouseover', this.onViewOver, this);
17087 this.innerList.on('mousemove', this.onViewMove, this);
17088 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17090 if(this.allowBlank && !this.pageSize && !this.disableClear){
17091 this.footer = this.list.createChild({cls:cls+'-ft'});
17092 this.pageTb = new Roo.Toolbar(this.footer);
17096 this.footer = this.list.createChild({cls:cls+'-ft'});
17097 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17098 {pageSize: this.pageSize});
17102 if (this.pageTb && this.allowBlank && !this.disableClear) {
17104 this.pageTb.add(new Roo.Toolbar.Fill(), {
17105 cls: 'x-btn-icon x-btn-clear',
17107 handler: function()
17110 _this.clearValue();
17111 _this.onSelect(false, -1);
17116 this.assetHeight += this.footer.getHeight();
17121 this.tpl = Roo.bootstrap.version == 4 ?
17122 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17123 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17126 this.view = new Roo.View(this.list, this.tpl, {
17127 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17129 //this.view.wrapEl.setDisplayed(false);
17130 this.view.on('click', this.onViewClick, this);
17133 this.store.on('beforeload', this.onBeforeLoad, this);
17134 this.store.on('load', this.onLoad, this);
17135 this.store.on('loadexception', this.onLoadException, this);
17137 if(this.resizable){
17138 this.resizer = new Roo.Resizable(this.list, {
17139 pinned:true, handles:'se'
17141 this.resizer.on('resize', function(r, w, h){
17142 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17143 this.listWidth = w;
17144 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17145 this.restrictHeight();
17147 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17150 if(!this.editable){
17151 this.editable = true;
17152 this.setEditable(false);
17157 if (typeof(this.events.add.listeners) != 'undefined') {
17159 this.addicon = this.wrap.createChild(
17160 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17162 this.addicon.on('click', function(e) {
17163 this.fireEvent('add', this);
17166 if (typeof(this.events.edit.listeners) != 'undefined') {
17168 this.editicon = this.wrap.createChild(
17169 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17170 if (this.addicon) {
17171 this.editicon.setStyle('margin-left', '40px');
17173 this.editicon.on('click', function(e) {
17175 // we fire even if inothing is selected..
17176 this.fireEvent('edit', this, this.lastData );
17182 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17183 "up" : function(e){
17184 this.inKeyMode = true;
17188 "down" : function(e){
17189 if(!this.isExpanded()){
17190 this.onTriggerClick();
17192 this.inKeyMode = true;
17197 "enter" : function(e){
17198 // this.onViewClick();
17202 if(this.fireEvent("specialkey", this, e)){
17203 this.onViewClick(false);
17209 "esc" : function(e){
17213 "tab" : function(e){
17216 if(this.fireEvent("specialkey", this, e)){
17217 this.onViewClick(false);
17225 doRelay : function(foo, bar, hname){
17226 if(hname == 'down' || this.scope.isExpanded()){
17227 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17236 this.queryDelay = Math.max(this.queryDelay || 10,
17237 this.mode == 'local' ? 10 : 250);
17240 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17242 if(this.typeAhead){
17243 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17245 if(this.editable !== false){
17246 this.inputEl().on("keyup", this.onKeyUp, this);
17248 if(this.forceSelection){
17249 this.inputEl().on('blur', this.doForce, this);
17253 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17254 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17258 initTickableEvents: function()
17262 if(this.hiddenName){
17264 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17266 this.hiddenField.dom.value =
17267 this.hiddenValue !== undefined ? this.hiddenValue :
17268 this.value !== undefined ? this.value : '';
17270 // prevent input submission
17271 this.el.dom.removeAttribute('name');
17272 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17277 // this.list = this.el.select('ul.dropdown-menu',true).first();
17279 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17280 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17281 if(this.triggerList){
17282 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17285 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17286 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17288 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17289 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17291 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17292 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17294 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17295 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17296 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17299 this.cancelBtn.hide();
17304 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17305 _this.list.setWidth(lw);
17308 this.list.on('mouseover', this.onViewOver, this);
17309 this.list.on('mousemove', this.onViewMove, this);
17311 this.list.on('scroll', this.onViewScroll, this);
17314 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17315 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17318 this.view = new Roo.View(this.list, this.tpl, {
17323 selectedClass: this.selectedClass
17326 //this.view.wrapEl.setDisplayed(false);
17327 this.view.on('click', this.onViewClick, this);
17331 this.store.on('beforeload', this.onBeforeLoad, this);
17332 this.store.on('load', this.onLoad, this);
17333 this.store.on('loadexception', this.onLoadException, this);
17336 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17337 "up" : function(e){
17338 this.inKeyMode = true;
17342 "down" : function(e){
17343 this.inKeyMode = true;
17347 "enter" : function(e){
17348 if(this.fireEvent("specialkey", this, e)){
17349 this.onViewClick(false);
17355 "esc" : function(e){
17356 this.onTickableFooterButtonClick(e, false, false);
17359 "tab" : function(e){
17360 this.fireEvent("specialkey", this, e);
17362 this.onTickableFooterButtonClick(e, false, false);
17369 doRelay : function(e, fn, key){
17370 if(this.scope.isExpanded()){
17371 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17380 this.queryDelay = Math.max(this.queryDelay || 10,
17381 this.mode == 'local' ? 10 : 250);
17384 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17386 if(this.typeAhead){
17387 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17390 if(this.editable !== false){
17391 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17394 this.indicator = this.indicatorEl();
17396 if(this.indicator){
17397 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17398 this.indicator.hide();
17403 onDestroy : function(){
17405 this.view.setStore(null);
17406 this.view.el.removeAllListeners();
17407 this.view.el.remove();
17408 this.view.purgeListeners();
17411 this.list.dom.innerHTML = '';
17415 this.store.un('beforeload', this.onBeforeLoad, this);
17416 this.store.un('load', this.onLoad, this);
17417 this.store.un('loadexception', this.onLoadException, this);
17419 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17423 fireKey : function(e){
17424 if(e.isNavKeyPress() && !this.list.isVisible()){
17425 this.fireEvent("specialkey", this, e);
17430 onResize: function(w, h)
17434 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17436 // if(typeof w != 'number'){
17437 // // we do not handle it!?!?
17440 // var tw = this.trigger.getWidth();
17441 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17442 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17444 // this.inputEl().setWidth( this.adjustWidth('input', x));
17446 // //this.trigger.setStyle('left', x+'px');
17448 // if(this.list && this.listWidth === undefined){
17449 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17450 // this.list.setWidth(lw);
17451 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17459 * Allow or prevent the user from directly editing the field text. If false is passed,
17460 * the user will only be able to select from the items defined in the dropdown list. This method
17461 * is the runtime equivalent of setting the 'editable' config option at config time.
17462 * @param {Boolean} value True to allow the user to directly edit the field text
17464 setEditable : function(value){
17465 if(value == this.editable){
17468 this.editable = value;
17470 this.inputEl().dom.setAttribute('readOnly', true);
17471 this.inputEl().on('mousedown', this.onTriggerClick, this);
17472 this.inputEl().addClass('x-combo-noedit');
17474 this.inputEl().dom.removeAttribute('readOnly');
17475 this.inputEl().un('mousedown', this.onTriggerClick, this);
17476 this.inputEl().removeClass('x-combo-noedit');
17482 onBeforeLoad : function(combo,opts){
17483 if(!this.hasFocus){
17487 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17489 this.restrictHeight();
17490 this.selectedIndex = -1;
17494 onLoad : function(){
17496 this.hasQuery = false;
17498 if(!this.hasFocus){
17502 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17503 this.loading.hide();
17506 if(this.store.getCount() > 0){
17509 this.restrictHeight();
17510 if(this.lastQuery == this.allQuery){
17511 if(this.editable && !this.tickable){
17512 this.inputEl().dom.select();
17516 !this.selectByValue(this.value, true) &&
17519 !this.store.lastOptions ||
17520 typeof(this.store.lastOptions.add) == 'undefined' ||
17521 this.store.lastOptions.add != true
17524 this.select(0, true);
17527 if(this.autoFocus){
17530 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17531 this.taTask.delay(this.typeAheadDelay);
17535 this.onEmptyResults();
17541 onLoadException : function()
17543 this.hasQuery = false;
17545 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17546 this.loading.hide();
17549 if(this.tickable && this.editable){
17554 // only causes errors at present
17555 //Roo.log(this.store.reader.jsonData);
17556 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17558 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17564 onTypeAhead : function(){
17565 if(this.store.getCount() > 0){
17566 var r = this.store.getAt(0);
17567 var newValue = r.data[this.displayField];
17568 var len = newValue.length;
17569 var selStart = this.getRawValue().length;
17571 if(selStart != len){
17572 this.setRawValue(newValue);
17573 this.selectText(selStart, newValue.length);
17579 onSelect : function(record, index){
17581 if(this.fireEvent('beforeselect', this, record, index) !== false){
17583 this.setFromData(index > -1 ? record.data : false);
17586 this.fireEvent('select', this, record, index);
17591 * Returns the currently selected field value or empty string if no value is set.
17592 * @return {String} value The selected value
17594 getValue : function()
17596 if(Roo.isIOS && this.useNativeIOS){
17597 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17601 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17604 if(this.valueField){
17605 return typeof this.value != 'undefined' ? this.value : '';
17607 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17611 getRawValue : function()
17613 if(Roo.isIOS && this.useNativeIOS){
17614 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17617 var v = this.inputEl().getValue();
17623 * Clears any text/value currently set in the field
17625 clearValue : function(){
17627 if(this.hiddenField){
17628 this.hiddenField.dom.value = '';
17631 this.setRawValue('');
17632 this.lastSelectionText = '';
17633 this.lastData = false;
17635 var close = this.closeTriggerEl();
17646 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17647 * will be displayed in the field. If the value does not match the data value of an existing item,
17648 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17649 * Otherwise the field will be blank (although the value will still be set).
17650 * @param {String} value The value to match
17652 setValue : function(v)
17654 if(Roo.isIOS && this.useNativeIOS){
17655 this.setIOSValue(v);
17665 if(this.valueField){
17666 var r = this.findRecord(this.valueField, v);
17668 text = r.data[this.displayField];
17669 }else if(this.valueNotFoundText !== undefined){
17670 text = this.valueNotFoundText;
17673 this.lastSelectionText = text;
17674 if(this.hiddenField){
17675 this.hiddenField.dom.value = v;
17677 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17680 var close = this.closeTriggerEl();
17683 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17689 * @property {Object} the last set data for the element
17694 * Sets the value of the field based on a object which is related to the record format for the store.
17695 * @param {Object} value the value to set as. or false on reset?
17697 setFromData : function(o){
17704 var dv = ''; // display value
17705 var vv = ''; // value value..
17707 if (this.displayField) {
17708 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17710 // this is an error condition!!!
17711 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17714 if(this.valueField){
17715 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17718 var close = this.closeTriggerEl();
17721 if(dv.length || vv * 1 > 0){
17723 this.blockFocus=true;
17729 if(this.hiddenField){
17730 this.hiddenField.dom.value = vv;
17732 this.lastSelectionText = dv;
17733 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17737 // no hidden field.. - we store the value in 'value', but still display
17738 // display field!!!!
17739 this.lastSelectionText = dv;
17740 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17747 reset : function(){
17748 // overridden so that last data is reset..
17755 this.setValue(this.originalValue);
17756 //this.clearInvalid();
17757 this.lastData = false;
17759 this.view.clearSelections();
17765 findRecord : function(prop, value){
17767 if(this.store.getCount() > 0){
17768 this.store.each(function(r){
17769 if(r.data[prop] == value){
17779 getName: function()
17781 // returns hidden if it's set..
17782 if (!this.rendered) {return ''};
17783 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17787 onViewMove : function(e, t){
17788 this.inKeyMode = false;
17792 onViewOver : function(e, t){
17793 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17796 var item = this.view.findItemFromChild(t);
17799 var index = this.view.indexOf(item);
17800 this.select(index, false);
17805 onViewClick : function(view, doFocus, el, e)
17807 var index = this.view.getSelectedIndexes()[0];
17809 var r = this.store.getAt(index);
17813 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17820 Roo.each(this.tickItems, function(v,k){
17822 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17824 _this.tickItems.splice(k, 1);
17826 if(typeof(e) == 'undefined' && view == false){
17827 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17839 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17840 this.tickItems.push(r.data);
17843 if(typeof(e) == 'undefined' && view == false){
17844 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17851 this.onSelect(r, index);
17853 if(doFocus !== false && !this.blockFocus){
17854 this.inputEl().focus();
17859 restrictHeight : function(){
17860 //this.innerList.dom.style.height = '';
17861 //var inner = this.innerList.dom;
17862 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17863 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17864 //this.list.beginUpdate();
17865 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17866 this.list.alignTo(this.inputEl(), this.listAlign);
17867 this.list.alignTo(this.inputEl(), this.listAlign);
17868 //this.list.endUpdate();
17872 onEmptyResults : function(){
17874 if(this.tickable && this.editable){
17875 this.hasFocus = false;
17876 this.restrictHeight();
17884 * Returns true if the dropdown list is expanded, else false.
17886 isExpanded : function(){
17887 return this.list.isVisible();
17891 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17892 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17893 * @param {String} value The data value of the item to select
17894 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17895 * selected item if it is not currently in view (defaults to true)
17896 * @return {Boolean} True if the value matched an item in the list, else false
17898 selectByValue : function(v, scrollIntoView){
17899 if(v !== undefined && v !== null){
17900 var r = this.findRecord(this.valueField || this.displayField, v);
17902 this.select(this.store.indexOf(r), scrollIntoView);
17910 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17911 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17912 * @param {Number} index The zero-based index of the list item to select
17913 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17914 * selected item if it is not currently in view (defaults to true)
17916 select : function(index, scrollIntoView){
17917 this.selectedIndex = index;
17918 this.view.select(index);
17919 if(scrollIntoView !== false){
17920 var el = this.view.getNode(index);
17922 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17925 this.list.scrollChildIntoView(el, false);
17931 selectNext : function(){
17932 var ct = this.store.getCount();
17934 if(this.selectedIndex == -1){
17936 }else if(this.selectedIndex < ct-1){
17937 this.select(this.selectedIndex+1);
17943 selectPrev : function(){
17944 var ct = this.store.getCount();
17946 if(this.selectedIndex == -1){
17948 }else if(this.selectedIndex != 0){
17949 this.select(this.selectedIndex-1);
17955 onKeyUp : function(e){
17956 if(this.editable !== false && !e.isSpecialKey()){
17957 this.lastKey = e.getKey();
17958 this.dqTask.delay(this.queryDelay);
17963 validateBlur : function(){
17964 return !this.list || !this.list.isVisible();
17968 initQuery : function(){
17970 var v = this.getRawValue();
17972 if(this.tickable && this.editable){
17973 v = this.tickableInputEl().getValue();
17980 doForce : function(){
17981 if(this.inputEl().dom.value.length > 0){
17982 this.inputEl().dom.value =
17983 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17989 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17990 * query allowing the query action to be canceled if needed.
17991 * @param {String} query The SQL query to execute
17992 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17993 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17994 * saved in the current store (defaults to false)
17996 doQuery : function(q, forceAll){
17998 if(q === undefined || q === null){
18003 forceAll: forceAll,
18007 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18012 forceAll = qe.forceAll;
18013 if(forceAll === true || (q.length >= this.minChars)){
18015 this.hasQuery = true;
18017 if(this.lastQuery != q || this.alwaysQuery){
18018 this.lastQuery = q;
18019 if(this.mode == 'local'){
18020 this.selectedIndex = -1;
18022 this.store.clearFilter();
18025 if(this.specialFilter){
18026 this.fireEvent('specialfilter', this);
18031 this.store.filter(this.displayField, q);
18034 this.store.fireEvent("datachanged", this.store);
18041 this.store.baseParams[this.queryParam] = q;
18043 var options = {params : this.getParams(q)};
18046 options.add = true;
18047 options.params.start = this.page * this.pageSize;
18050 this.store.load(options);
18053 * this code will make the page width larger, at the beginning, the list not align correctly,
18054 * we should expand the list on onLoad
18055 * so command out it
18060 this.selectedIndex = -1;
18065 this.loadNext = false;
18069 getParams : function(q){
18071 //p[this.queryParam] = q;
18075 p.limit = this.pageSize;
18081 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18083 collapse : function(){
18084 if(!this.isExpanded()){
18090 this.hasFocus = false;
18094 this.cancelBtn.hide();
18095 this.trigger.show();
18098 this.tickableInputEl().dom.value = '';
18099 this.tickableInputEl().blur();
18104 Roo.get(document).un('mousedown', this.collapseIf, this);
18105 Roo.get(document).un('mousewheel', this.collapseIf, this);
18106 if (!this.editable) {
18107 Roo.get(document).un('keydown', this.listKeyPress, this);
18109 this.fireEvent('collapse', this);
18115 collapseIf : function(e){
18116 var in_combo = e.within(this.el);
18117 var in_list = e.within(this.list);
18118 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18120 if (in_combo || in_list || is_list) {
18121 //e.stopPropagation();
18126 this.onTickableFooterButtonClick(e, false, false);
18134 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18136 expand : function(){
18138 if(this.isExpanded() || !this.hasFocus){
18142 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18143 this.list.setWidth(lw);
18149 this.restrictHeight();
18153 this.tickItems = Roo.apply([], this.item);
18156 this.cancelBtn.show();
18157 this.trigger.hide();
18160 this.tickableInputEl().focus();
18165 Roo.get(document).on('mousedown', this.collapseIf, this);
18166 Roo.get(document).on('mousewheel', this.collapseIf, this);
18167 if (!this.editable) {
18168 Roo.get(document).on('keydown', this.listKeyPress, this);
18171 this.fireEvent('expand', this);
18175 // Implements the default empty TriggerField.onTriggerClick function
18176 onTriggerClick : function(e)
18178 Roo.log('trigger click');
18180 if(this.disabled || !this.triggerList){
18185 this.loadNext = false;
18187 if(this.isExpanded()){
18189 if (!this.blockFocus) {
18190 this.inputEl().focus();
18194 this.hasFocus = true;
18195 if(this.triggerAction == 'all') {
18196 this.doQuery(this.allQuery, true);
18198 this.doQuery(this.getRawValue());
18200 if (!this.blockFocus) {
18201 this.inputEl().focus();
18206 onTickableTriggerClick : function(e)
18213 this.loadNext = false;
18214 this.hasFocus = true;
18216 if(this.triggerAction == 'all') {
18217 this.doQuery(this.allQuery, true);
18219 this.doQuery(this.getRawValue());
18223 onSearchFieldClick : function(e)
18225 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18226 this.onTickableFooterButtonClick(e, false, false);
18230 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18235 this.loadNext = false;
18236 this.hasFocus = true;
18238 if(this.triggerAction == 'all') {
18239 this.doQuery(this.allQuery, true);
18241 this.doQuery(this.getRawValue());
18245 listKeyPress : function(e)
18247 //Roo.log('listkeypress');
18248 // scroll to first matching element based on key pres..
18249 if (e.isSpecialKey()) {
18252 var k = String.fromCharCode(e.getKey()).toUpperCase();
18255 var csel = this.view.getSelectedNodes();
18256 var cselitem = false;
18258 var ix = this.view.indexOf(csel[0]);
18259 cselitem = this.store.getAt(ix);
18260 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18266 this.store.each(function(v) {
18268 // start at existing selection.
18269 if (cselitem.id == v.id) {
18275 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18276 match = this.store.indexOf(v);
18282 if (match === false) {
18283 return true; // no more action?
18286 this.view.select(match);
18287 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18288 sn.scrollIntoView(sn.dom.parentNode, false);
18291 onViewScroll : function(e, t){
18293 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){
18297 this.hasQuery = true;
18299 this.loading = this.list.select('.loading', true).first();
18301 if(this.loading === null){
18302 this.list.createChild({
18304 cls: 'loading roo-select2-more-results roo-select2-active',
18305 html: 'Loading more results...'
18308 this.loading = this.list.select('.loading', true).first();
18310 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18312 this.loading.hide();
18315 this.loading.show();
18320 this.loadNext = true;
18322 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18327 addItem : function(o)
18329 var dv = ''; // display value
18331 if (this.displayField) {
18332 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18334 // this is an error condition!!!
18335 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18342 var choice = this.choices.createChild({
18344 cls: 'roo-select2-search-choice',
18353 cls: 'roo-select2-search-choice-close fa fa-times',
18358 }, this.searchField);
18360 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18362 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18370 this.inputEl().dom.value = '';
18375 onRemoveItem : function(e, _self, o)
18377 e.preventDefault();
18379 this.lastItem = Roo.apply([], this.item);
18381 var index = this.item.indexOf(o.data) * 1;
18384 Roo.log('not this item?!');
18388 this.item.splice(index, 1);
18393 this.fireEvent('remove', this, e);
18399 syncValue : function()
18401 if(!this.item.length){
18408 Roo.each(this.item, function(i){
18409 if(_this.valueField){
18410 value.push(i[_this.valueField]);
18417 this.value = value.join(',');
18419 if(this.hiddenField){
18420 this.hiddenField.dom.value = this.value;
18423 this.store.fireEvent("datachanged", this.store);
18428 clearItem : function()
18430 if(!this.multiple){
18436 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18444 if(this.tickable && !Roo.isTouch){
18445 this.view.refresh();
18449 inputEl: function ()
18451 if(Roo.isIOS && this.useNativeIOS){
18452 return this.el.select('select.roo-ios-select', true).first();
18455 if(Roo.isTouch && this.mobileTouchView){
18456 return this.el.select('input.form-control',true).first();
18460 return this.searchField;
18463 return this.el.select('input.form-control',true).first();
18466 onTickableFooterButtonClick : function(e, btn, el)
18468 e.preventDefault();
18470 this.lastItem = Roo.apply([], this.item);
18472 if(btn && btn.name == 'cancel'){
18473 this.tickItems = Roo.apply([], this.item);
18482 Roo.each(this.tickItems, function(o){
18490 validate : function()
18492 if(this.getVisibilityEl().hasClass('hidden')){
18496 var v = this.getRawValue();
18499 v = this.getValue();
18502 if(this.disabled || this.allowBlank || v.length){
18507 this.markInvalid();
18511 tickableInputEl : function()
18513 if(!this.tickable || !this.editable){
18514 return this.inputEl();
18517 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18521 getAutoCreateTouchView : function()
18526 cls: 'form-group' //input-group
18532 type : this.inputType,
18533 cls : 'form-control x-combo-noedit',
18534 autocomplete: 'new-password',
18535 placeholder : this.placeholder || '',
18540 input.name = this.name;
18544 input.cls += ' input-' + this.size;
18547 if (this.disabled) {
18548 input.disabled = true;
18552 cls : 'roo-combobox-wrap',
18559 inputblock.cls += ' input-group';
18561 inputblock.cn.unshift({
18563 cls : 'input-group-addon input-group-prepend input-group-text',
18568 if(this.removable && !this.multiple){
18569 inputblock.cls += ' roo-removable';
18571 inputblock.cn.push({
18574 cls : 'roo-combo-removable-btn close'
18578 if(this.hasFeedback && !this.allowBlank){
18580 inputblock.cls += ' has-feedback';
18582 inputblock.cn.push({
18584 cls: 'glyphicon form-control-feedback'
18591 inputblock.cls += (this.before) ? '' : ' input-group';
18593 inputblock.cn.push({
18595 cls : 'input-group-addon input-group-append input-group-text',
18601 var ibwrap = inputblock;
18606 cls: 'roo-select2-choices',
18610 cls: 'roo-select2-search-field',
18623 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18628 cls: 'form-hidden-field'
18634 if(!this.multiple && this.showToggleBtn){
18640 if (this.caret != false) {
18643 cls: 'fa fa-' + this.caret
18650 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18652 Roo.bootstrap.version == 3 ? caret : '',
18655 cls: 'combobox-clear',
18669 combobox.cls += ' roo-select2-container-multi';
18672 var required = this.allowBlank ? {
18674 style: 'display: none'
18677 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18678 tooltip : 'This field is required'
18681 var align = this.labelAlign || this.parentLabelAlign();
18683 if (align ==='left' && this.fieldLabel.length) {
18689 cls : 'control-label col-form-label',
18690 html : this.fieldLabel
18694 cls : 'roo-combobox-wrap ',
18701 var labelCfg = cfg.cn[1];
18702 var contentCfg = cfg.cn[2];
18705 if(this.indicatorpos == 'right'){
18710 cls : 'control-label col-form-label',
18714 html : this.fieldLabel
18720 cls : "roo-combobox-wrap ",
18728 labelCfg = cfg.cn[0];
18729 contentCfg = cfg.cn[1];
18734 if(this.labelWidth > 12){
18735 labelCfg.style = "width: " + this.labelWidth + 'px';
18738 if(this.labelWidth < 13 && this.labelmd == 0){
18739 this.labelmd = this.labelWidth;
18742 if(this.labellg > 0){
18743 labelCfg.cls += ' col-lg-' + this.labellg;
18744 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18747 if(this.labelmd > 0){
18748 labelCfg.cls += ' col-md-' + this.labelmd;
18749 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18752 if(this.labelsm > 0){
18753 labelCfg.cls += ' col-sm-' + this.labelsm;
18754 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18757 if(this.labelxs > 0){
18758 labelCfg.cls += ' col-xs-' + this.labelxs;
18759 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18763 } else if ( this.fieldLabel.length) {
18768 cls : 'control-label',
18769 html : this.fieldLabel
18780 if(this.indicatorpos == 'right'){
18784 cls : 'control-label',
18785 html : this.fieldLabel,
18803 var settings = this;
18805 ['xs','sm','md','lg'].map(function(size){
18806 if (settings[size]) {
18807 cfg.cls += ' col-' + size + '-' + settings[size];
18814 initTouchView : function()
18816 this.renderTouchView();
18818 this.touchViewEl.on('scroll', function(){
18819 this.el.dom.scrollTop = 0;
18822 this.originalValue = this.getValue();
18824 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18826 this.inputEl().on("click", this.showTouchView, this);
18827 if (this.triggerEl) {
18828 this.triggerEl.on("click", this.showTouchView, this);
18832 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18833 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18835 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18837 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18838 this.store.on('load', this.onTouchViewLoad, this);
18839 this.store.on('loadexception', this.onTouchViewLoadException, this);
18841 if(this.hiddenName){
18843 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18845 this.hiddenField.dom.value =
18846 this.hiddenValue !== undefined ? this.hiddenValue :
18847 this.value !== undefined ? this.value : '';
18849 this.el.dom.removeAttribute('name');
18850 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18854 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18855 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18858 if(this.removable && !this.multiple){
18859 var close = this.closeTriggerEl();
18861 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18862 close.on('click', this.removeBtnClick, this, close);
18866 * fix the bug in Safari iOS8
18868 this.inputEl().on("focus", function(e){
18869 document.activeElement.blur();
18872 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18879 renderTouchView : function()
18881 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18882 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18884 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18885 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18887 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18888 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18889 this.touchViewBodyEl.setStyle('overflow', 'auto');
18891 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18892 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18894 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18895 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18899 showTouchView : function()
18905 this.touchViewHeaderEl.hide();
18907 if(this.modalTitle.length){
18908 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18909 this.touchViewHeaderEl.show();
18912 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18913 this.touchViewEl.show();
18915 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18917 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18918 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18920 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18922 if(this.modalTitle.length){
18923 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18926 this.touchViewBodyEl.setHeight(bodyHeight);
18930 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18932 this.touchViewEl.addClass(['in','show']);
18935 if(this._touchViewMask){
18936 Roo.get(document.body).addClass("x-body-masked");
18937 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18938 this._touchViewMask.setStyle('z-index', 10000);
18939 this._touchViewMask.addClass('show');
18942 this.doTouchViewQuery();
18946 hideTouchView : function()
18948 this.touchViewEl.removeClass(['in','show']);
18952 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18954 this.touchViewEl.setStyle('display', 'none');
18957 if(this._touchViewMask){
18958 this._touchViewMask.removeClass('show');
18959 Roo.get(document.body).removeClass("x-body-masked");
18963 setTouchViewValue : function()
18970 Roo.each(this.tickItems, function(o){
18975 this.hideTouchView();
18978 doTouchViewQuery : function()
18987 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18991 if(!this.alwaysQuery || this.mode == 'local'){
18992 this.onTouchViewLoad();
18999 onTouchViewBeforeLoad : function(combo,opts)
19005 onTouchViewLoad : function()
19007 if(this.store.getCount() < 1){
19008 this.onTouchViewEmptyResults();
19012 this.clearTouchView();
19014 var rawValue = this.getRawValue();
19016 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19018 this.tickItems = [];
19020 this.store.data.each(function(d, rowIndex){
19021 var row = this.touchViewListGroup.createChild(template);
19023 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19024 row.addClass(d.data.cls);
19027 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19030 html : d.data[this.displayField]
19033 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19034 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19037 row.removeClass('selected');
19038 if(!this.multiple && this.valueField &&
19039 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19042 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19043 row.addClass('selected');
19046 if(this.multiple && this.valueField &&
19047 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19051 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19052 this.tickItems.push(d.data);
19055 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19059 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19061 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19063 if(this.modalTitle.length){
19064 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19067 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19069 if(this.mobile_restrict_height && listHeight < bodyHeight){
19070 this.touchViewBodyEl.setHeight(listHeight);
19075 if(firstChecked && listHeight > bodyHeight){
19076 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19081 onTouchViewLoadException : function()
19083 this.hideTouchView();
19086 onTouchViewEmptyResults : function()
19088 this.clearTouchView();
19090 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19092 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19096 clearTouchView : function()
19098 this.touchViewListGroup.dom.innerHTML = '';
19101 onTouchViewClick : function(e, el, o)
19103 e.preventDefault();
19106 var rowIndex = o.rowIndex;
19108 var r = this.store.getAt(rowIndex);
19110 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19112 if(!this.multiple){
19113 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19114 c.dom.removeAttribute('checked');
19117 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19119 this.setFromData(r.data);
19121 var close = this.closeTriggerEl();
19127 this.hideTouchView();
19129 this.fireEvent('select', this, r, rowIndex);
19134 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19135 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19136 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19140 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19141 this.addItem(r.data);
19142 this.tickItems.push(r.data);
19146 getAutoCreateNativeIOS : function()
19149 cls: 'form-group' //input-group,
19154 cls : 'roo-ios-select'
19158 combobox.name = this.name;
19161 if (this.disabled) {
19162 combobox.disabled = true;
19165 var settings = this;
19167 ['xs','sm','md','lg'].map(function(size){
19168 if (settings[size]) {
19169 cfg.cls += ' col-' + size + '-' + settings[size];
19179 initIOSView : function()
19181 this.store.on('load', this.onIOSViewLoad, this);
19186 onIOSViewLoad : function()
19188 if(this.store.getCount() < 1){
19192 this.clearIOSView();
19194 if(this.allowBlank) {
19196 var default_text = '-- SELECT --';
19198 if(this.placeholder.length){
19199 default_text = this.placeholder;
19202 if(this.emptyTitle.length){
19203 default_text += ' - ' + this.emptyTitle + ' -';
19206 var opt = this.inputEl().createChild({
19209 html : default_text
19213 o[this.valueField] = 0;
19214 o[this.displayField] = default_text;
19216 this.ios_options.push({
19223 this.store.data.each(function(d, rowIndex){
19227 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19228 html = d.data[this.displayField];
19233 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19234 value = d.data[this.valueField];
19243 if(this.value == d.data[this.valueField]){
19244 option['selected'] = true;
19247 var opt = this.inputEl().createChild(option);
19249 this.ios_options.push({
19256 this.inputEl().on('change', function(){
19257 this.fireEvent('select', this);
19262 clearIOSView: function()
19264 this.inputEl().dom.innerHTML = '';
19266 this.ios_options = [];
19269 setIOSValue: function(v)
19273 if(!this.ios_options){
19277 Roo.each(this.ios_options, function(opts){
19279 opts.el.dom.removeAttribute('selected');
19281 if(opts.data[this.valueField] != v){
19285 opts.el.dom.setAttribute('selected', true);
19291 * @cfg {Boolean} grow
19295 * @cfg {Number} growMin
19299 * @cfg {Number} growMax
19308 Roo.apply(Roo.bootstrap.ComboBox, {
19312 cls: 'modal-header',
19334 cls: 'list-group-item',
19338 cls: 'roo-combobox-list-group-item-value'
19342 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19356 listItemCheckbox : {
19358 cls: 'list-group-item',
19362 cls: 'roo-combobox-list-group-item-value'
19366 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19382 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19387 cls: 'modal-footer',
19395 cls: 'col-xs-6 text-left',
19398 cls: 'btn btn-danger roo-touch-view-cancel',
19404 cls: 'col-xs-6 text-right',
19407 cls: 'btn btn-success roo-touch-view-ok',
19418 Roo.apply(Roo.bootstrap.ComboBox, {
19420 touchViewTemplate : {
19422 cls: 'modal fade roo-combobox-touch-view',
19426 cls: 'modal-dialog',
19427 style : 'position:fixed', // we have to fix position....
19431 cls: 'modal-content',
19433 Roo.bootstrap.ComboBox.header,
19434 Roo.bootstrap.ComboBox.body,
19435 Roo.bootstrap.ComboBox.footer
19444 * Ext JS Library 1.1.1
19445 * Copyright(c) 2006-2007, Ext JS, LLC.
19447 * Originally Released Under LGPL - original licence link has changed is not relivant.
19450 * <script type="text/javascript">
19455 * @extends Roo.util.Observable
19456 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19457 * This class also supports single and multi selection modes. <br>
19458 * Create a data model bound view:
19460 var store = new Roo.data.Store(...);
19462 var view = new Roo.View({
19464 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19466 singleSelect: true,
19467 selectedClass: "ydataview-selected",
19471 // listen for node click?
19472 view.on("click", function(vw, index, node, e){
19473 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19477 dataModel.load("foobar.xml");
19479 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19481 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19482 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19484 * Note: old style constructor is still suported (container, template, config)
19487 * Create a new View
19488 * @param {Object} config The config object
19491 Roo.View = function(config, depreciated_tpl, depreciated_config){
19493 this.parent = false;
19495 if (typeof(depreciated_tpl) == 'undefined') {
19496 // new way.. - universal constructor.
19497 Roo.apply(this, config);
19498 this.el = Roo.get(this.el);
19501 this.el = Roo.get(config);
19502 this.tpl = depreciated_tpl;
19503 Roo.apply(this, depreciated_config);
19505 this.wrapEl = this.el.wrap().wrap();
19506 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19509 if(typeof(this.tpl) == "string"){
19510 this.tpl = new Roo.Template(this.tpl);
19512 // support xtype ctors..
19513 this.tpl = new Roo.factory(this.tpl, Roo);
19517 this.tpl.compile();
19522 * @event beforeclick
19523 * Fires before a click is processed. Returns false to cancel the default action.
19524 * @param {Roo.View} this
19525 * @param {Number} index The index of the target node
19526 * @param {HTMLElement} node The target node
19527 * @param {Roo.EventObject} e The raw event object
19529 "beforeclick" : true,
19532 * Fires when a template node is clicked.
19533 * @param {Roo.View} this
19534 * @param {Number} index The index of the target node
19535 * @param {HTMLElement} node The target node
19536 * @param {Roo.EventObject} e The raw event object
19541 * Fires when a template node is double clicked.
19542 * @param {Roo.View} this
19543 * @param {Number} index The index of the target node
19544 * @param {HTMLElement} node The target node
19545 * @param {Roo.EventObject} e The raw event object
19549 * @event contextmenu
19550 * Fires when a template node is right clicked.
19551 * @param {Roo.View} this
19552 * @param {Number} index The index of the target node
19553 * @param {HTMLElement} node The target node
19554 * @param {Roo.EventObject} e The raw event object
19556 "contextmenu" : true,
19558 * @event selectionchange
19559 * Fires when the selected nodes change.
19560 * @param {Roo.View} this
19561 * @param {Array} selections Array of the selected nodes
19563 "selectionchange" : true,
19566 * @event beforeselect
19567 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19568 * @param {Roo.View} this
19569 * @param {HTMLElement} node The node to be selected
19570 * @param {Array} selections Array of currently selected nodes
19572 "beforeselect" : true,
19574 * @event preparedata
19575 * Fires on every row to render, to allow you to change the data.
19576 * @param {Roo.View} this
19577 * @param {Object} data to be rendered (change this)
19579 "preparedata" : true
19587 "click": this.onClick,
19588 "dblclick": this.onDblClick,
19589 "contextmenu": this.onContextMenu,
19593 this.selections = [];
19595 this.cmp = new Roo.CompositeElementLite([]);
19597 this.store = Roo.factory(this.store, Roo.data);
19598 this.setStore(this.store, true);
19601 if ( this.footer && this.footer.xtype) {
19603 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19605 this.footer.dataSource = this.store;
19606 this.footer.container = fctr;
19607 this.footer = Roo.factory(this.footer, Roo);
19608 fctr.insertFirst(this.el);
19610 // this is a bit insane - as the paging toolbar seems to detach the el..
19611 // dom.parentNode.parentNode.parentNode
19612 // they get detached?
19616 Roo.View.superclass.constructor.call(this);
19621 Roo.extend(Roo.View, Roo.util.Observable, {
19624 * @cfg {Roo.data.Store} store Data store to load data from.
19629 * @cfg {String|Roo.Element} el The container element.
19634 * @cfg {String|Roo.Template} tpl The template used by this View
19638 * @cfg {String} dataName the named area of the template to use as the data area
19639 * Works with domtemplates roo-name="name"
19643 * @cfg {String} selectedClass The css class to add to selected nodes
19645 selectedClass : "x-view-selected",
19647 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19652 * @cfg {String} text to display on mask (default Loading)
19656 * @cfg {Boolean} multiSelect Allow multiple selection
19658 multiSelect : false,
19660 * @cfg {Boolean} singleSelect Allow single selection
19662 singleSelect: false,
19665 * @cfg {Boolean} toggleSelect - selecting
19667 toggleSelect : false,
19670 * @cfg {Boolean} tickable - selecting
19675 * Returns the element this view is bound to.
19676 * @return {Roo.Element}
19678 getEl : function(){
19679 return this.wrapEl;
19685 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19687 refresh : function(){
19688 //Roo.log('refresh');
19691 // if we are using something like 'domtemplate', then
19692 // the what gets used is:
19693 // t.applySubtemplate(NAME, data, wrapping data..)
19694 // the outer template then get' applied with
19695 // the store 'extra data'
19696 // and the body get's added to the
19697 // roo-name="data" node?
19698 // <span class='roo-tpl-{name}'></span> ?????
19702 this.clearSelections();
19703 this.el.update("");
19705 var records = this.store.getRange();
19706 if(records.length < 1) {
19708 // is this valid?? = should it render a template??
19710 this.el.update(this.emptyText);
19714 if (this.dataName) {
19715 this.el.update(t.apply(this.store.meta)); //????
19716 el = this.el.child('.roo-tpl-' + this.dataName);
19719 for(var i = 0, len = records.length; i < len; i++){
19720 var data = this.prepareData(records[i].data, i, records[i]);
19721 this.fireEvent("preparedata", this, data, i, records[i]);
19723 var d = Roo.apply({}, data);
19726 Roo.apply(d, {'roo-id' : Roo.id()});
19730 Roo.each(this.parent.item, function(item){
19731 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19734 Roo.apply(d, {'roo-data-checked' : 'checked'});
19738 html[html.length] = Roo.util.Format.trim(
19740 t.applySubtemplate(this.dataName, d, this.store.meta) :
19747 el.update(html.join(""));
19748 this.nodes = el.dom.childNodes;
19749 this.updateIndexes(0);
19754 * Function to override to reformat the data that is sent to
19755 * the template for each node.
19756 * DEPRICATED - use the preparedata event handler.
19757 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19758 * a JSON object for an UpdateManager bound view).
19760 prepareData : function(data, index, record)
19762 this.fireEvent("preparedata", this, data, index, record);
19766 onUpdate : function(ds, record){
19767 // Roo.log('on update');
19768 this.clearSelections();
19769 var index = this.store.indexOf(record);
19770 var n = this.nodes[index];
19771 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19772 n.parentNode.removeChild(n);
19773 this.updateIndexes(index, index);
19779 onAdd : function(ds, records, index)
19781 //Roo.log(['on Add', ds, records, index] );
19782 this.clearSelections();
19783 if(this.nodes.length == 0){
19787 var n = this.nodes[index];
19788 for(var i = 0, len = records.length; i < len; i++){
19789 var d = this.prepareData(records[i].data, i, records[i]);
19791 this.tpl.insertBefore(n, d);
19794 this.tpl.append(this.el, d);
19797 this.updateIndexes(index);
19800 onRemove : function(ds, record, index){
19801 // Roo.log('onRemove');
19802 this.clearSelections();
19803 var el = this.dataName ?
19804 this.el.child('.roo-tpl-' + this.dataName) :
19807 el.dom.removeChild(this.nodes[index]);
19808 this.updateIndexes(index);
19812 * Refresh an individual node.
19813 * @param {Number} index
19815 refreshNode : function(index){
19816 this.onUpdate(this.store, this.store.getAt(index));
19819 updateIndexes : function(startIndex, endIndex){
19820 var ns = this.nodes;
19821 startIndex = startIndex || 0;
19822 endIndex = endIndex || ns.length - 1;
19823 for(var i = startIndex; i <= endIndex; i++){
19824 ns[i].nodeIndex = i;
19829 * Changes the data store this view uses and refresh the view.
19830 * @param {Store} store
19832 setStore : function(store, initial){
19833 if(!initial && this.store){
19834 this.store.un("datachanged", this.refresh);
19835 this.store.un("add", this.onAdd);
19836 this.store.un("remove", this.onRemove);
19837 this.store.un("update", this.onUpdate);
19838 this.store.un("clear", this.refresh);
19839 this.store.un("beforeload", this.onBeforeLoad);
19840 this.store.un("load", this.onLoad);
19841 this.store.un("loadexception", this.onLoad);
19845 store.on("datachanged", this.refresh, this);
19846 store.on("add", this.onAdd, this);
19847 store.on("remove", this.onRemove, this);
19848 store.on("update", this.onUpdate, this);
19849 store.on("clear", this.refresh, this);
19850 store.on("beforeload", this.onBeforeLoad, this);
19851 store.on("load", this.onLoad, this);
19852 store.on("loadexception", this.onLoad, this);
19860 * onbeforeLoad - masks the loading area.
19863 onBeforeLoad : function(store,opts)
19865 //Roo.log('onBeforeLoad');
19867 this.el.update("");
19869 this.el.mask(this.mask ? this.mask : "Loading" );
19871 onLoad : function ()
19878 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19879 * @param {HTMLElement} node
19880 * @return {HTMLElement} The template node
19882 findItemFromChild : function(node){
19883 var el = this.dataName ?
19884 this.el.child('.roo-tpl-' + this.dataName,true) :
19887 if(!node || node.parentNode == el){
19890 var p = node.parentNode;
19891 while(p && p != el){
19892 if(p.parentNode == el){
19901 onClick : function(e){
19902 var item = this.findItemFromChild(e.getTarget());
19904 var index = this.indexOf(item);
19905 if(this.onItemClick(item, index, e) !== false){
19906 this.fireEvent("click", this, index, item, e);
19909 this.clearSelections();
19914 onContextMenu : function(e){
19915 var item = this.findItemFromChild(e.getTarget());
19917 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19922 onDblClick : function(e){
19923 var item = this.findItemFromChild(e.getTarget());
19925 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19929 onItemClick : function(item, index, e)
19931 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19934 if (this.toggleSelect) {
19935 var m = this.isSelected(item) ? 'unselect' : 'select';
19938 _t[m](item, true, false);
19941 if(this.multiSelect || this.singleSelect){
19942 if(this.multiSelect && e.shiftKey && this.lastSelection){
19943 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19945 this.select(item, this.multiSelect && e.ctrlKey);
19946 this.lastSelection = item;
19949 if(!this.tickable){
19950 e.preventDefault();
19958 * Get the number of selected nodes.
19961 getSelectionCount : function(){
19962 return this.selections.length;
19966 * Get the currently selected nodes.
19967 * @return {Array} An array of HTMLElements
19969 getSelectedNodes : function(){
19970 return this.selections;
19974 * Get the indexes of the selected nodes.
19977 getSelectedIndexes : function(){
19978 var indexes = [], s = this.selections;
19979 for(var i = 0, len = s.length; i < len; i++){
19980 indexes.push(s[i].nodeIndex);
19986 * Clear all selections
19987 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19989 clearSelections : function(suppressEvent){
19990 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19991 this.cmp.elements = this.selections;
19992 this.cmp.removeClass(this.selectedClass);
19993 this.selections = [];
19994 if(!suppressEvent){
19995 this.fireEvent("selectionchange", this, this.selections);
20001 * Returns true if the passed node is selected
20002 * @param {HTMLElement/Number} node The node or node index
20003 * @return {Boolean}
20005 isSelected : function(node){
20006 var s = this.selections;
20010 node = this.getNode(node);
20011 return s.indexOf(node) !== -1;
20016 * @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
20017 * @param {Boolean} keepExisting (optional) true to keep existing selections
20018 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20020 select : function(nodeInfo, keepExisting, suppressEvent){
20021 if(nodeInfo instanceof Array){
20023 this.clearSelections(true);
20025 for(var i = 0, len = nodeInfo.length; i < len; i++){
20026 this.select(nodeInfo[i], true, true);
20030 var node = this.getNode(nodeInfo);
20031 if(!node || this.isSelected(node)){
20032 return; // already selected.
20035 this.clearSelections(true);
20038 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20039 Roo.fly(node).addClass(this.selectedClass);
20040 this.selections.push(node);
20041 if(!suppressEvent){
20042 this.fireEvent("selectionchange", this, this.selections);
20050 * @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
20051 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20052 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20054 unselect : function(nodeInfo, keepExisting, suppressEvent)
20056 if(nodeInfo instanceof Array){
20057 Roo.each(this.selections, function(s) {
20058 this.unselect(s, nodeInfo);
20062 var node = this.getNode(nodeInfo);
20063 if(!node || !this.isSelected(node)){
20064 //Roo.log("not selected");
20065 return; // not selected.
20069 Roo.each(this.selections, function(s) {
20071 Roo.fly(node).removeClass(this.selectedClass);
20078 this.selections= ns;
20079 this.fireEvent("selectionchange", this, this.selections);
20083 * Gets a template node.
20084 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20085 * @return {HTMLElement} The node or null if it wasn't found
20087 getNode : function(nodeInfo){
20088 if(typeof nodeInfo == "string"){
20089 return document.getElementById(nodeInfo);
20090 }else if(typeof nodeInfo == "number"){
20091 return this.nodes[nodeInfo];
20097 * Gets a range template nodes.
20098 * @param {Number} startIndex
20099 * @param {Number} endIndex
20100 * @return {Array} An array of nodes
20102 getNodes : function(start, end){
20103 var ns = this.nodes;
20104 start = start || 0;
20105 end = typeof end == "undefined" ? ns.length - 1 : end;
20108 for(var i = start; i <= end; i++){
20112 for(var i = start; i >= end; i--){
20120 * Finds the index of the passed node
20121 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20122 * @return {Number} The index of the node or -1
20124 indexOf : function(node){
20125 node = this.getNode(node);
20126 if(typeof node.nodeIndex == "number"){
20127 return node.nodeIndex;
20129 var ns = this.nodes;
20130 for(var i = 0, len = ns.length; i < len; i++){
20141 * based on jquery fullcalendar
20145 Roo.bootstrap = Roo.bootstrap || {};
20147 * @class Roo.bootstrap.Calendar
20148 * @extends Roo.bootstrap.Component
20149 * Bootstrap Calendar class
20150 * @cfg {Boolean} loadMask (true|false) default false
20151 * @cfg {Object} header generate the user specific header of the calendar, default false
20154 * Create a new Container
20155 * @param {Object} config The config object
20160 Roo.bootstrap.Calendar = function(config){
20161 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20165 * Fires when a date is selected
20166 * @param {DatePicker} this
20167 * @param {Date} date The selected date
20171 * @event monthchange
20172 * Fires when the displayed month changes
20173 * @param {DatePicker} this
20174 * @param {Date} date The selected month
20176 'monthchange': true,
20178 * @event evententer
20179 * Fires when mouse over an event
20180 * @param {Calendar} this
20181 * @param {event} Event
20183 'evententer': true,
20185 * @event eventleave
20186 * Fires when the mouse leaves an
20187 * @param {Calendar} this
20190 'eventleave': true,
20192 * @event eventclick
20193 * Fires when the mouse click an
20194 * @param {Calendar} this
20203 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20206 * @cfg {Roo.data.Store} store
20207 * The data source for the calendar
20211 * @cfg {Number} startDay
20212 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20220 getAutoCreate : function(){
20223 var fc_button = function(name, corner, style, content ) {
20224 return Roo.apply({},{
20226 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20228 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20231 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20242 style : 'width:100%',
20249 cls : 'fc-header-left',
20251 fc_button('prev', 'left', 'arrow', '‹' ),
20252 fc_button('next', 'right', 'arrow', '›' ),
20253 { tag: 'span', cls: 'fc-header-space' },
20254 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20262 cls : 'fc-header-center',
20266 cls: 'fc-header-title',
20269 html : 'month / year'
20277 cls : 'fc-header-right',
20279 /* fc_button('month', 'left', '', 'month' ),
20280 fc_button('week', '', '', 'week' ),
20281 fc_button('day', 'right', '', 'day' )
20293 header = this.header;
20296 var cal_heads = function() {
20298 // fixme - handle this.
20300 for (var i =0; i < Date.dayNames.length; i++) {
20301 var d = Date.dayNames[i];
20304 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20305 html : d.substring(0,3)
20309 ret[0].cls += ' fc-first';
20310 ret[6].cls += ' fc-last';
20313 var cal_cell = function(n) {
20316 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20321 cls: 'fc-day-number',
20325 cls: 'fc-day-content',
20329 style: 'position: relative;' // height: 17px;
20341 var cal_rows = function() {
20344 for (var r = 0; r < 6; r++) {
20351 for (var i =0; i < Date.dayNames.length; i++) {
20352 var d = Date.dayNames[i];
20353 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20356 row.cn[0].cls+=' fc-first';
20357 row.cn[0].cn[0].style = 'min-height:90px';
20358 row.cn[6].cls+=' fc-last';
20362 ret[0].cls += ' fc-first';
20363 ret[4].cls += ' fc-prev-last';
20364 ret[5].cls += ' fc-last';
20371 cls: 'fc-border-separate',
20372 style : 'width:100%',
20380 cls : 'fc-first fc-last',
20398 cls : 'fc-content',
20399 style : "position: relative;",
20402 cls : 'fc-view fc-view-month fc-grid',
20403 style : 'position: relative',
20404 unselectable : 'on',
20407 cls : 'fc-event-container',
20408 style : 'position:absolute;z-index:8;top:0;left:0;'
20426 initEvents : function()
20429 throw "can not find store for calendar";
20435 style: "text-align:center",
20439 style: "background-color:white;width:50%;margin:250 auto",
20443 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20454 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20456 var size = this.el.select('.fc-content', true).first().getSize();
20457 this.maskEl.setSize(size.width, size.height);
20458 this.maskEl.enableDisplayMode("block");
20459 if(!this.loadMask){
20460 this.maskEl.hide();
20463 this.store = Roo.factory(this.store, Roo.data);
20464 this.store.on('load', this.onLoad, this);
20465 this.store.on('beforeload', this.onBeforeLoad, this);
20469 this.cells = this.el.select('.fc-day',true);
20470 //Roo.log(this.cells);
20471 this.textNodes = this.el.query('.fc-day-number');
20472 this.cells.addClassOnOver('fc-state-hover');
20474 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20475 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20476 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20477 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20479 this.on('monthchange', this.onMonthChange, this);
20481 this.update(new Date().clearTime());
20484 resize : function() {
20485 var sz = this.el.getSize();
20487 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20488 this.el.select('.fc-day-content div',true).setHeight(34);
20493 showPrevMonth : function(e){
20494 this.update(this.activeDate.add("mo", -1));
20496 showToday : function(e){
20497 this.update(new Date().clearTime());
20500 showNextMonth : function(e){
20501 this.update(this.activeDate.add("mo", 1));
20505 showPrevYear : function(){
20506 this.update(this.activeDate.add("y", -1));
20510 showNextYear : function(){
20511 this.update(this.activeDate.add("y", 1));
20516 update : function(date)
20518 var vd = this.activeDate;
20519 this.activeDate = date;
20520 // if(vd && this.el){
20521 // var t = date.getTime();
20522 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20523 // Roo.log('using add remove');
20525 // this.fireEvent('monthchange', this, date);
20527 // this.cells.removeClass("fc-state-highlight");
20528 // this.cells.each(function(c){
20529 // if(c.dateValue == t){
20530 // c.addClass("fc-state-highlight");
20531 // setTimeout(function(){
20532 // try{c.dom.firstChild.focus();}catch(e){}
20542 var days = date.getDaysInMonth();
20544 var firstOfMonth = date.getFirstDateOfMonth();
20545 var startingPos = firstOfMonth.getDay()-this.startDay;
20547 if(startingPos < this.startDay){
20551 var pm = date.add(Date.MONTH, -1);
20552 var prevStart = pm.getDaysInMonth()-startingPos;
20554 this.cells = this.el.select('.fc-day',true);
20555 this.textNodes = this.el.query('.fc-day-number');
20556 this.cells.addClassOnOver('fc-state-hover');
20558 var cells = this.cells.elements;
20559 var textEls = this.textNodes;
20561 Roo.each(cells, function(cell){
20562 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20565 days += startingPos;
20567 // convert everything to numbers so it's fast
20568 var day = 86400000;
20569 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20572 //Roo.log(prevStart);
20574 var today = new Date().clearTime().getTime();
20575 var sel = date.clearTime().getTime();
20576 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20577 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20578 var ddMatch = this.disabledDatesRE;
20579 var ddText = this.disabledDatesText;
20580 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20581 var ddaysText = this.disabledDaysText;
20582 var format = this.format;
20584 var setCellClass = function(cal, cell){
20588 //Roo.log('set Cell Class');
20590 var t = d.getTime();
20594 cell.dateValue = t;
20596 cell.className += " fc-today";
20597 cell.className += " fc-state-highlight";
20598 cell.title = cal.todayText;
20601 // disable highlight in other month..
20602 //cell.className += " fc-state-highlight";
20607 cell.className = " fc-state-disabled";
20608 cell.title = cal.minText;
20612 cell.className = " fc-state-disabled";
20613 cell.title = cal.maxText;
20617 if(ddays.indexOf(d.getDay()) != -1){
20618 cell.title = ddaysText;
20619 cell.className = " fc-state-disabled";
20622 if(ddMatch && format){
20623 var fvalue = d.dateFormat(format);
20624 if(ddMatch.test(fvalue)){
20625 cell.title = ddText.replace("%0", fvalue);
20626 cell.className = " fc-state-disabled";
20630 if (!cell.initialClassName) {
20631 cell.initialClassName = cell.dom.className;
20634 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20639 for(; i < startingPos; i++) {
20640 textEls[i].innerHTML = (++prevStart);
20641 d.setDate(d.getDate()+1);
20643 cells[i].className = "fc-past fc-other-month";
20644 setCellClass(this, cells[i]);
20649 for(; i < days; i++){
20650 intDay = i - startingPos + 1;
20651 textEls[i].innerHTML = (intDay);
20652 d.setDate(d.getDate()+1);
20654 cells[i].className = ''; // "x-date-active";
20655 setCellClass(this, cells[i]);
20659 for(; i < 42; i++) {
20660 textEls[i].innerHTML = (++extraDays);
20661 d.setDate(d.getDate()+1);
20663 cells[i].className = "fc-future fc-other-month";
20664 setCellClass(this, cells[i]);
20667 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20669 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20671 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20672 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20674 if(totalRows != 6){
20675 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20676 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20679 this.fireEvent('monthchange', this, date);
20683 if(!this.internalRender){
20684 var main = this.el.dom.firstChild;
20685 var w = main.offsetWidth;
20686 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20687 Roo.fly(main).setWidth(w);
20688 this.internalRender = true;
20689 // opera does not respect the auto grow header center column
20690 // then, after it gets a width opera refuses to recalculate
20691 // without a second pass
20692 if(Roo.isOpera && !this.secondPass){
20693 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20694 this.secondPass = true;
20695 this.update.defer(10, this, [date]);
20702 findCell : function(dt) {
20703 dt = dt.clearTime().getTime();
20705 this.cells.each(function(c){
20706 //Roo.log("check " +c.dateValue + '?=' + dt);
20707 if(c.dateValue == dt){
20717 findCells : function(ev) {
20718 var s = ev.start.clone().clearTime().getTime();
20720 var e= ev.end.clone().clearTime().getTime();
20723 this.cells.each(function(c){
20724 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20726 if(c.dateValue > e){
20729 if(c.dateValue < s){
20738 // findBestRow: function(cells)
20742 // for (var i =0 ; i < cells.length;i++) {
20743 // ret = Math.max(cells[i].rows || 0,ret);
20750 addItem : function(ev)
20752 // look for vertical location slot in
20753 var cells = this.findCells(ev);
20755 // ev.row = this.findBestRow(cells);
20757 // work out the location.
20761 for(var i =0; i < cells.length; i++) {
20763 cells[i].row = cells[0].row;
20766 cells[i].row = cells[i].row + 1;
20776 if (crow.start.getY() == cells[i].getY()) {
20778 crow.end = cells[i];
20795 cells[0].events.push(ev);
20797 this.calevents.push(ev);
20800 clearEvents: function() {
20802 if(!this.calevents){
20806 Roo.each(this.cells.elements, function(c){
20812 Roo.each(this.calevents, function(e) {
20813 Roo.each(e.els, function(el) {
20814 el.un('mouseenter' ,this.onEventEnter, this);
20815 el.un('mouseleave' ,this.onEventLeave, this);
20820 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20826 renderEvents: function()
20830 this.cells.each(function(c) {
20839 if(c.row != c.events.length){
20840 r = 4 - (4 - (c.row - c.events.length));
20843 c.events = ev.slice(0, r);
20844 c.more = ev.slice(r);
20846 if(c.more.length && c.more.length == 1){
20847 c.events.push(c.more.pop());
20850 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20854 this.cells.each(function(c) {
20856 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20859 for (var e = 0; e < c.events.length; e++){
20860 var ev = c.events[e];
20861 var rows = ev.rows;
20863 for(var i = 0; i < rows.length; i++) {
20865 // how many rows should it span..
20868 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20869 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20871 unselectable : "on",
20874 cls: 'fc-event-inner',
20878 // cls: 'fc-event-time',
20879 // html : cells.length > 1 ? '' : ev.time
20883 cls: 'fc-event-title',
20884 html : String.format('{0}', ev.title)
20891 cls: 'ui-resizable-handle ui-resizable-e',
20892 html : '  '
20899 cfg.cls += ' fc-event-start';
20901 if ((i+1) == rows.length) {
20902 cfg.cls += ' fc-event-end';
20905 var ctr = _this.el.select('.fc-event-container',true).first();
20906 var cg = ctr.createChild(cfg);
20908 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20909 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20911 var r = (c.more.length) ? 1 : 0;
20912 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20913 cg.setWidth(ebox.right - sbox.x -2);
20915 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20916 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20917 cg.on('click', _this.onEventClick, _this, ev);
20928 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20929 style : 'position: absolute',
20930 unselectable : "on",
20933 cls: 'fc-event-inner',
20937 cls: 'fc-event-title',
20945 cls: 'ui-resizable-handle ui-resizable-e',
20946 html : '  '
20952 var ctr = _this.el.select('.fc-event-container',true).first();
20953 var cg = ctr.createChild(cfg);
20955 var sbox = c.select('.fc-day-content',true).first().getBox();
20956 var ebox = c.select('.fc-day-content',true).first().getBox();
20958 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20959 cg.setWidth(ebox.right - sbox.x -2);
20961 cg.on('click', _this.onMoreEventClick, _this, c.more);
20971 onEventEnter: function (e, el,event,d) {
20972 this.fireEvent('evententer', this, el, event);
20975 onEventLeave: function (e, el,event,d) {
20976 this.fireEvent('eventleave', this, el, event);
20979 onEventClick: function (e, el,event,d) {
20980 this.fireEvent('eventclick', this, el, event);
20983 onMonthChange: function () {
20987 onMoreEventClick: function(e, el, more)
20991 this.calpopover.placement = 'right';
20992 this.calpopover.setTitle('More');
20994 this.calpopover.setContent('');
20996 var ctr = this.calpopover.el.select('.popover-content', true).first();
20998 Roo.each(more, function(m){
21000 cls : 'fc-event-hori fc-event-draggable',
21003 var cg = ctr.createChild(cfg);
21005 cg.on('click', _this.onEventClick, _this, m);
21008 this.calpopover.show(el);
21013 onLoad: function ()
21015 this.calevents = [];
21018 if(this.store.getCount() > 0){
21019 this.store.data.each(function(d){
21022 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21023 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21024 time : d.data.start_time,
21025 title : d.data.title,
21026 description : d.data.description,
21027 venue : d.data.venue
21032 this.renderEvents();
21034 if(this.calevents.length && this.loadMask){
21035 this.maskEl.hide();
21039 onBeforeLoad: function()
21041 this.clearEvents();
21043 this.maskEl.show();
21057 * @class Roo.bootstrap.Popover
21058 * @extends Roo.bootstrap.Component
21059 * Bootstrap Popover class
21060 * @cfg {String} html contents of the popover (or false to use children..)
21061 * @cfg {String} title of popover (or false to hide)
21062 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21063 * @cfg {String} trigger click || hover (or false to trigger manually)
21064 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21065 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21066 * - if false and it has a 'parent' then it will be automatically added to that element
21067 * - if string - Roo.get will be called
21068 * @cfg {Number} delay - delay before showing
21071 * Create a new Popover
21072 * @param {Object} config The config object
21075 Roo.bootstrap.Popover = function(config){
21076 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21082 * After the popover show
21084 * @param {Roo.bootstrap.Popover} this
21089 * After the popover hide
21091 * @param {Roo.bootstrap.Popover} this
21097 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21102 placement : 'right',
21103 trigger : 'hover', // hover
21109 can_build_overlaid : false,
21111 maskEl : false, // the mask element
21114 alignEl : false, // when show is called with an element - this get's stored.
21116 getChildContainer : function()
21118 return this.contentEl;
21121 getPopoverHeader : function()
21123 this.title = true; // flag not to hide it..
21124 this.headerEl.addClass('p-0');
21125 return this.headerEl
21129 getAutoCreate : function(){
21132 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21133 style: 'display:block',
21139 cls : 'popover-inner ',
21143 cls: 'popover-title popover-header',
21144 html : this.title === false ? '' : this.title
21147 cls : 'popover-content popover-body ' + (this.cls || ''),
21148 html : this.html || ''
21159 * @param {string} the title
21161 setTitle: function(str)
21165 this.headerEl.dom.innerHTML = str;
21170 * @param {string} the body content
21172 setContent: function(str)
21175 if (this.contentEl) {
21176 this.contentEl.dom.innerHTML = str;
21180 // as it get's added to the bottom of the page.
21181 onRender : function(ct, position)
21183 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21188 var cfg = Roo.apply({}, this.getAutoCreate());
21192 cfg.cls += ' ' + this.cls;
21195 cfg.style = this.style;
21197 //Roo.log("adding to ");
21198 this.el = Roo.get(document.body).createChild(cfg, position);
21199 // Roo.log(this.el);
21202 this.contentEl = this.el.select('.popover-content',true).first();
21203 this.headerEl = this.el.select('.popover-title',true).first();
21206 if(typeof(this.items) != 'undefined'){
21207 var items = this.items;
21210 for(var i =0;i < items.length;i++) {
21211 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21215 this.items = nitems;
21217 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21218 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21225 resizeMask : function()
21227 this.maskEl.setSize(
21228 Roo.lib.Dom.getViewWidth(true),
21229 Roo.lib.Dom.getViewHeight(true)
21233 initEvents : function()
21237 Roo.bootstrap.Popover.register(this);
21240 this.arrowEl = this.el.select('.arrow',true).first();
21241 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21242 this.el.enableDisplayMode('block');
21246 if (this.over === false && !this.parent()) {
21249 if (this.triggers === false) {
21254 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21255 var triggers = this.trigger ? this.trigger.split(' ') : [];
21256 Roo.each(triggers, function(trigger) {
21258 if (trigger == 'click') {
21259 on_el.on('click', this.toggle, this);
21260 } else if (trigger != 'manual') {
21261 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21262 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21264 on_el.on(eventIn ,this.enter, this);
21265 on_el.on(eventOut, this.leave, this);
21275 toggle : function () {
21276 this.hoverState == 'in' ? this.leave() : this.enter();
21279 enter : function () {
21281 clearTimeout(this.timeout);
21283 this.hoverState = 'in';
21285 if (!this.delay || !this.delay.show) {
21290 this.timeout = setTimeout(function () {
21291 if (_t.hoverState == 'in') {
21294 }, this.delay.show)
21297 leave : function() {
21298 clearTimeout(this.timeout);
21300 this.hoverState = 'out';
21302 if (!this.delay || !this.delay.hide) {
21307 this.timeout = setTimeout(function () {
21308 if (_t.hoverState == 'out') {
21311 }, this.delay.hide)
21315 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21316 * @param {string} (left|right|top|bottom) position
21318 show : function (on_el, placement)
21320 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21321 on_el = on_el || false; // default to false
21324 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21325 on_el = this.parent().el;
21326 } else if (this.over) {
21327 on_el = Roo.get(this.over);
21332 this.alignEl = Roo.get( on_el );
21335 this.render(document.body);
21341 if (this.title === false) {
21342 this.headerEl.hide();
21347 this.el.dom.style.display = 'block';
21350 if (this.alignEl) {
21351 this.updatePosition(this.placement, true);
21354 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21355 var es = this.el.getSize();
21356 var x = Roo.lib.Dom.getViewWidth()/2;
21357 var y = Roo.lib.Dom.getViewHeight()/2;
21358 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21363 //var arrow = this.el.select('.arrow',true).first();
21364 //arrow.set(align[2],
21366 this.el.addClass('in');
21370 this.hoverState = 'in';
21373 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21374 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21375 this.maskEl.dom.style.display = 'block';
21376 this.maskEl.addClass('show');
21378 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21380 this.fireEvent('show', this);
21384 * fire this manually after loading a grid in the table for example
21385 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21386 * @param {Boolean} try and move it if we cant get right position.
21388 updatePosition : function(placement, try_move)
21390 // allow for calling with no parameters
21391 placement = placement ? placement : this.placement;
21392 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21394 this.el.removeClass([
21395 'fade','top','bottom', 'left', 'right','in',
21396 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21398 this.el.addClass(placement + ' bs-popover-' + placement);
21400 if (!this.alignEl ) {
21404 switch (placement) {
21406 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21407 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21408 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21409 //normal display... or moved up/down.
21410 this.el.setXY(offset);
21411 var xy = this.alignEl.getAnchorXY('tr', false);
21413 this.arrowEl.setXY(xy);
21416 // continue through...
21417 return this.updatePosition('left', false);
21421 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21422 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21423 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21424 //normal display... or moved up/down.
21425 this.el.setXY(offset);
21426 var xy = this.alignEl.getAnchorXY('tl', false);
21427 xy[0]-=10;xy[1]+=5; // << fix me
21428 this.arrowEl.setXY(xy);
21432 return this.updatePosition('right', false);
21435 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21436 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21437 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21438 //normal display... or moved up/down.
21439 this.el.setXY(offset);
21440 var xy = this.alignEl.getAnchorXY('t', false);
21441 xy[1]-=10; // << fix me
21442 this.arrowEl.setXY(xy);
21446 return this.updatePosition('bottom', false);
21449 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21450 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21451 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21452 //normal display... or moved up/down.
21453 this.el.setXY(offset);
21454 var xy = this.alignEl.getAnchorXY('b', false);
21455 xy[1]+=2; // << fix me
21456 this.arrowEl.setXY(xy);
21460 return this.updatePosition('top', false);
21471 this.el.setXY([0,0]);
21472 this.el.removeClass('in');
21474 this.hoverState = null;
21475 this.maskEl.hide(); // always..
21476 this.fireEvent('hide', this);
21482 Roo.apply(Roo.bootstrap.Popover, {
21485 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21486 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21487 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21488 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21493 clickHander : false,
21497 onMouseDown : function(e)
21499 if (this.popups.length && !e.getTarget(".roo-popover")) {
21500 /// what is nothing is showing..
21509 register : function(popup)
21511 if (!Roo.bootstrap.Popover.clickHandler) {
21512 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21514 // hide other popups.
21515 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21516 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21517 this.hideAll(); //<< why?
21518 //this.popups.push(popup);
21520 hideAll : function()
21522 this.popups.forEach(function(p) {
21526 onShow : function() {
21527 Roo.bootstrap.Popover.popups.push(this);
21529 onHide : function() {
21530 Roo.bootstrap.Popover.popups.remove(this);
21536 * Card header - holder for the card header elements.
21541 * @class Roo.bootstrap.PopoverNav
21542 * @extends Roo.bootstrap.NavGroup
21543 * Bootstrap Popover header navigation class
21545 * Create a new Popover Header Navigation
21546 * @param {Object} config The config object
21549 Roo.bootstrap.PopoverNav = function(config){
21550 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21553 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21556 container_method : 'getPopoverHeader'
21574 * @class Roo.bootstrap.Progress
21575 * @extends Roo.bootstrap.Component
21576 * Bootstrap Progress class
21577 * @cfg {Boolean} striped striped of the progress bar
21578 * @cfg {Boolean} active animated of the progress bar
21582 * Create a new Progress
21583 * @param {Object} config The config object
21586 Roo.bootstrap.Progress = function(config){
21587 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21590 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21595 getAutoCreate : function(){
21603 cfg.cls += ' progress-striped';
21607 cfg.cls += ' active';
21626 * @class Roo.bootstrap.ProgressBar
21627 * @extends Roo.bootstrap.Component
21628 * Bootstrap ProgressBar class
21629 * @cfg {Number} aria_valuenow aria-value now
21630 * @cfg {Number} aria_valuemin aria-value min
21631 * @cfg {Number} aria_valuemax aria-value max
21632 * @cfg {String} label label for the progress bar
21633 * @cfg {String} panel (success | info | warning | danger )
21634 * @cfg {String} role role of the progress bar
21635 * @cfg {String} sr_only text
21639 * Create a new ProgressBar
21640 * @param {Object} config The config object
21643 Roo.bootstrap.ProgressBar = function(config){
21644 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21647 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21651 aria_valuemax : 100,
21657 getAutoCreate : function()
21662 cls: 'progress-bar',
21663 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21675 cfg.role = this.role;
21678 if(this.aria_valuenow){
21679 cfg['aria-valuenow'] = this.aria_valuenow;
21682 if(this.aria_valuemin){
21683 cfg['aria-valuemin'] = this.aria_valuemin;
21686 if(this.aria_valuemax){
21687 cfg['aria-valuemax'] = this.aria_valuemax;
21690 if(this.label && !this.sr_only){
21691 cfg.html = this.label;
21695 cfg.cls += ' progress-bar-' + this.panel;
21701 update : function(aria_valuenow)
21703 this.aria_valuenow = aria_valuenow;
21705 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21720 * @class Roo.bootstrap.TabGroup
21721 * @extends Roo.bootstrap.Column
21722 * Bootstrap Column class
21723 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21724 * @cfg {Boolean} carousel true to make the group behave like a carousel
21725 * @cfg {Boolean} bullets show bullets for the panels
21726 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21727 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21728 * @cfg {Boolean} showarrow (true|false) show arrow default true
21731 * Create a new TabGroup
21732 * @param {Object} config The config object
21735 Roo.bootstrap.TabGroup = function(config){
21736 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21738 this.navId = Roo.id();
21741 Roo.bootstrap.TabGroup.register(this);
21745 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21748 transition : false,
21753 slideOnTouch : false,
21756 getAutoCreate : function()
21758 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21760 cfg.cls += ' tab-content';
21762 if (this.carousel) {
21763 cfg.cls += ' carousel slide';
21766 cls : 'carousel-inner',
21770 if(this.bullets && !Roo.isTouch){
21773 cls : 'carousel-bullets',
21777 if(this.bullets_cls){
21778 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21785 cfg.cn[0].cn.push(bullets);
21788 if(this.showarrow){
21789 cfg.cn[0].cn.push({
21791 class : 'carousel-arrow',
21795 class : 'carousel-prev',
21799 class : 'fa fa-chevron-left'
21805 class : 'carousel-next',
21809 class : 'fa fa-chevron-right'
21822 initEvents: function()
21824 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21825 // this.el.on("touchstart", this.onTouchStart, this);
21828 if(this.autoslide){
21831 this.slideFn = window.setInterval(function() {
21832 _this.showPanelNext();
21836 if(this.showarrow){
21837 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21838 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21844 // onTouchStart : function(e, el, o)
21846 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21850 // this.showPanelNext();
21854 getChildContainer : function()
21856 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21860 * register a Navigation item
21861 * @param {Roo.bootstrap.NavItem} the navitem to add
21863 register : function(item)
21865 this.tabs.push( item);
21866 item.navId = this.navId; // not really needed..
21871 getActivePanel : function()
21874 Roo.each(this.tabs, function(t) {
21884 getPanelByName : function(n)
21887 Roo.each(this.tabs, function(t) {
21888 if (t.tabId == n) {
21896 indexOfPanel : function(p)
21899 Roo.each(this.tabs, function(t,i) {
21900 if (t.tabId == p.tabId) {
21909 * show a specific panel
21910 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21911 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21913 showPanel : function (pan)
21915 if(this.transition || typeof(pan) == 'undefined'){
21916 Roo.log("waiting for the transitionend");
21920 if (typeof(pan) == 'number') {
21921 pan = this.tabs[pan];
21924 if (typeof(pan) == 'string') {
21925 pan = this.getPanelByName(pan);
21928 var cur = this.getActivePanel();
21931 Roo.log('pan or acitve pan is undefined');
21935 if (pan.tabId == this.getActivePanel().tabId) {
21939 if (false === cur.fireEvent('beforedeactivate')) {
21943 if(this.bullets > 0 && !Roo.isTouch){
21944 this.setActiveBullet(this.indexOfPanel(pan));
21947 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21949 //class="carousel-item carousel-item-next carousel-item-left"
21951 this.transition = true;
21952 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21953 var lr = dir == 'next' ? 'left' : 'right';
21954 pan.el.addClass(dir); // or prev
21955 pan.el.addClass('carousel-item-' + dir); // or prev
21956 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21957 cur.el.addClass(lr); // or right
21958 pan.el.addClass(lr);
21959 cur.el.addClass('carousel-item-' +lr); // or right
21960 pan.el.addClass('carousel-item-' +lr);
21964 cur.el.on('transitionend', function() {
21965 Roo.log("trans end?");
21967 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21968 pan.setActive(true);
21970 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21971 cur.setActive(false);
21973 _this.transition = false;
21975 }, this, { single: true } );
21980 cur.setActive(false);
21981 pan.setActive(true);
21986 showPanelNext : function()
21988 var i = this.indexOfPanel(this.getActivePanel());
21990 if (i >= this.tabs.length - 1 && !this.autoslide) {
21994 if (i >= this.tabs.length - 1 && this.autoslide) {
21998 this.showPanel(this.tabs[i+1]);
22001 showPanelPrev : function()
22003 var i = this.indexOfPanel(this.getActivePanel());
22005 if (i < 1 && !this.autoslide) {
22009 if (i < 1 && this.autoslide) {
22010 i = this.tabs.length;
22013 this.showPanel(this.tabs[i-1]);
22017 addBullet: function()
22019 if(!this.bullets || Roo.isTouch){
22022 var ctr = this.el.select('.carousel-bullets',true).first();
22023 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22024 var bullet = ctr.createChild({
22025 cls : 'bullet bullet-' + i
22026 },ctr.dom.lastChild);
22031 bullet.on('click', (function(e, el, o, ii, t){
22033 e.preventDefault();
22035 this.showPanel(ii);
22037 if(this.autoslide && this.slideFn){
22038 clearInterval(this.slideFn);
22039 this.slideFn = window.setInterval(function() {
22040 _this.showPanelNext();
22044 }).createDelegate(this, [i, bullet], true));
22049 setActiveBullet : function(i)
22055 Roo.each(this.el.select('.bullet', true).elements, function(el){
22056 el.removeClass('selected');
22059 var bullet = this.el.select('.bullet-' + i, true).first();
22065 bullet.addClass('selected');
22076 Roo.apply(Roo.bootstrap.TabGroup, {
22080 * register a Navigation Group
22081 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22083 register : function(navgrp)
22085 this.groups[navgrp.navId] = navgrp;
22089 * fetch a Navigation Group based on the navigation ID
22090 * if one does not exist , it will get created.
22091 * @param {string} the navgroup to add
22092 * @returns {Roo.bootstrap.NavGroup} the navgroup
22094 get: function(navId) {
22095 if (typeof(this.groups[navId]) == 'undefined') {
22096 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22098 return this.groups[navId] ;
22113 * @class Roo.bootstrap.TabPanel
22114 * @extends Roo.bootstrap.Component
22115 * Bootstrap TabPanel class
22116 * @cfg {Boolean} active panel active
22117 * @cfg {String} html panel content
22118 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22119 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22120 * @cfg {String} href click to link..
22121 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22125 * Create a new TabPanel
22126 * @param {Object} config The config object
22129 Roo.bootstrap.TabPanel = function(config){
22130 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22134 * Fires when the active status changes
22135 * @param {Roo.bootstrap.TabPanel} this
22136 * @param {Boolean} state the new state
22141 * @event beforedeactivate
22142 * Fires before a tab is de-activated - can be used to do validation on a form.
22143 * @param {Roo.bootstrap.TabPanel} this
22144 * @return {Boolean} false if there is an error
22147 'beforedeactivate': true
22150 this.tabId = this.tabId || Roo.id();
22154 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22161 touchSlide : false,
22162 getAutoCreate : function(){
22167 // item is needed for carousel - not sure if it has any effect otherwise
22168 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22169 html: this.html || ''
22173 cfg.cls += ' active';
22177 cfg.tabId = this.tabId;
22185 initEvents: function()
22187 var p = this.parent();
22189 this.navId = this.navId || p.navId;
22191 if (typeof(this.navId) != 'undefined') {
22192 // not really needed.. but just in case.. parent should be a NavGroup.
22193 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22197 var i = tg.tabs.length - 1;
22199 if(this.active && tg.bullets > 0 && i < tg.bullets){
22200 tg.setActiveBullet(i);
22204 this.el.on('click', this.onClick, this);
22206 if(Roo.isTouch && this.touchSlide){
22207 this.el.on("touchstart", this.onTouchStart, this);
22208 this.el.on("touchmove", this.onTouchMove, this);
22209 this.el.on("touchend", this.onTouchEnd, this);
22214 onRender : function(ct, position)
22216 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22219 setActive : function(state)
22221 Roo.log("panel - set active " + this.tabId + "=" + state);
22223 this.active = state;
22225 this.el.removeClass('active');
22227 } else if (!this.el.hasClass('active')) {
22228 this.el.addClass('active');
22231 this.fireEvent('changed', this, state);
22234 onClick : function(e)
22236 e.preventDefault();
22238 if(!this.href.length){
22242 window.location.href = this.href;
22251 onTouchStart : function(e)
22253 this.swiping = false;
22255 this.startX = e.browserEvent.touches[0].clientX;
22256 this.startY = e.browserEvent.touches[0].clientY;
22259 onTouchMove : function(e)
22261 this.swiping = true;
22263 this.endX = e.browserEvent.touches[0].clientX;
22264 this.endY = e.browserEvent.touches[0].clientY;
22267 onTouchEnd : function(e)
22274 var tabGroup = this.parent();
22276 if(this.endX > this.startX){ // swiping right
22277 tabGroup.showPanelPrev();
22281 if(this.startX > this.endX){ // swiping left
22282 tabGroup.showPanelNext();
22301 * @class Roo.bootstrap.DateField
22302 * @extends Roo.bootstrap.Input
22303 * Bootstrap DateField class
22304 * @cfg {Number} weekStart default 0
22305 * @cfg {String} viewMode default empty, (months|years)
22306 * @cfg {String} minViewMode default empty, (months|years)
22307 * @cfg {Number} startDate default -Infinity
22308 * @cfg {Number} endDate default Infinity
22309 * @cfg {Boolean} todayHighlight default false
22310 * @cfg {Boolean} todayBtn default false
22311 * @cfg {Boolean} calendarWeeks default false
22312 * @cfg {Object} daysOfWeekDisabled default empty
22313 * @cfg {Boolean} singleMode default false (true | false)
22315 * @cfg {Boolean} keyboardNavigation default true
22316 * @cfg {String} language default en
22319 * Create a new DateField
22320 * @param {Object} config The config object
22323 Roo.bootstrap.DateField = function(config){
22324 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22328 * Fires when this field show.
22329 * @param {Roo.bootstrap.DateField} this
22330 * @param {Mixed} date The date value
22335 * Fires when this field hide.
22336 * @param {Roo.bootstrap.DateField} this
22337 * @param {Mixed} date The date value
22342 * Fires when select a date.
22343 * @param {Roo.bootstrap.DateField} this
22344 * @param {Mixed} date The date value
22348 * @event beforeselect
22349 * Fires when before select a date.
22350 * @param {Roo.bootstrap.DateField} this
22351 * @param {Mixed} date The date value
22353 beforeselect : true
22357 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22360 * @cfg {String} format
22361 * The default date format string which can be overriden for localization support. The format must be
22362 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22366 * @cfg {String} altFormats
22367 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22368 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22370 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22378 todayHighlight : false,
22384 keyboardNavigation: true,
22386 calendarWeeks: false,
22388 startDate: -Infinity,
22392 daysOfWeekDisabled: [],
22396 singleMode : false,
22398 UTCDate: function()
22400 return new Date(Date.UTC.apply(Date, arguments));
22403 UTCToday: function()
22405 var today = new Date();
22406 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22409 getDate: function() {
22410 var d = this.getUTCDate();
22411 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22414 getUTCDate: function() {
22418 setDate: function(d) {
22419 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22422 setUTCDate: function(d) {
22424 this.setValue(this.formatDate(this.date));
22427 onRender: function(ct, position)
22430 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22432 this.language = this.language || 'en';
22433 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22434 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22436 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22437 this.format = this.format || 'm/d/y';
22438 this.isInline = false;
22439 this.isInput = true;
22440 this.component = this.el.select('.add-on', true).first() || false;
22441 this.component = (this.component && this.component.length === 0) ? false : this.component;
22442 this.hasInput = this.component && this.inputEl().length;
22444 if (typeof(this.minViewMode === 'string')) {
22445 switch (this.minViewMode) {
22447 this.minViewMode = 1;
22450 this.minViewMode = 2;
22453 this.minViewMode = 0;
22458 if (typeof(this.viewMode === 'string')) {
22459 switch (this.viewMode) {
22472 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22474 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22476 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22478 this.picker().on('mousedown', this.onMousedown, this);
22479 this.picker().on('click', this.onClick, this);
22481 this.picker().addClass('datepicker-dropdown');
22483 this.startViewMode = this.viewMode;
22485 if(this.singleMode){
22486 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22487 v.setVisibilityMode(Roo.Element.DISPLAY);
22491 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22492 v.setStyle('width', '189px');
22496 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22497 if(!this.calendarWeeks){
22502 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22503 v.attr('colspan', function(i, val){
22504 return parseInt(val) + 1;
22509 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22511 this.setStartDate(this.startDate);
22512 this.setEndDate(this.endDate);
22514 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22521 if(this.isInline) {
22526 picker : function()
22528 return this.pickerEl;
22529 // return this.el.select('.datepicker', true).first();
22532 fillDow: function()
22534 var dowCnt = this.weekStart;
22543 if(this.calendarWeeks){
22551 while (dowCnt < this.weekStart + 7) {
22555 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22559 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22562 fillMonths: function()
22565 var months = this.picker().select('>.datepicker-months td', true).first();
22567 months.dom.innerHTML = '';
22573 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22576 months.createChild(month);
22583 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;
22585 if (this.date < this.startDate) {
22586 this.viewDate = new Date(this.startDate);
22587 } else if (this.date > this.endDate) {
22588 this.viewDate = new Date(this.endDate);
22590 this.viewDate = new Date(this.date);
22598 var d = new Date(this.viewDate),
22599 year = d.getUTCFullYear(),
22600 month = d.getUTCMonth(),
22601 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22602 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22603 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22604 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22605 currentDate = this.date && this.date.valueOf(),
22606 today = this.UTCToday();
22608 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22610 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22612 // this.picker.select('>tfoot th.today').
22613 // .text(dates[this.language].today)
22614 // .toggle(this.todayBtn !== false);
22616 this.updateNavArrows();
22619 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22621 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22623 prevMonth.setUTCDate(day);
22625 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22627 var nextMonth = new Date(prevMonth);
22629 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22631 nextMonth = nextMonth.valueOf();
22633 var fillMonths = false;
22635 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22637 while(prevMonth.valueOf() <= nextMonth) {
22640 if (prevMonth.getUTCDay() === this.weekStart) {
22642 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22650 if(this.calendarWeeks){
22651 // ISO 8601: First week contains first thursday.
22652 // ISO also states week starts on Monday, but we can be more abstract here.
22654 // Start of current week: based on weekstart/current date
22655 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22656 // Thursday of this week
22657 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22658 // First Thursday of year, year from thursday
22659 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22660 // Calendar week: ms between thursdays, div ms per day, div 7 days
22661 calWeek = (th - yth) / 864e5 / 7 + 1;
22663 fillMonths.cn.push({
22671 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22673 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22676 if (this.todayHighlight &&
22677 prevMonth.getUTCFullYear() == today.getFullYear() &&
22678 prevMonth.getUTCMonth() == today.getMonth() &&
22679 prevMonth.getUTCDate() == today.getDate()) {
22680 clsName += ' today';
22683 if (currentDate && prevMonth.valueOf() === currentDate) {
22684 clsName += ' active';
22687 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22688 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22689 clsName += ' disabled';
22692 fillMonths.cn.push({
22694 cls: 'day ' + clsName,
22695 html: prevMonth.getDate()
22698 prevMonth.setDate(prevMonth.getDate()+1);
22701 var currentYear = this.date && this.date.getUTCFullYear();
22702 var currentMonth = this.date && this.date.getUTCMonth();
22704 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22706 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22707 v.removeClass('active');
22709 if(currentYear === year && k === currentMonth){
22710 v.addClass('active');
22713 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22714 v.addClass('disabled');
22720 year = parseInt(year/10, 10) * 10;
22722 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22724 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22727 for (var i = -1; i < 11; i++) {
22728 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22730 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22738 showMode: function(dir)
22741 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22744 Roo.each(this.picker().select('>div',true).elements, function(v){
22745 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22748 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22753 if(this.isInline) {
22757 this.picker().removeClass(['bottom', 'top']);
22759 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22761 * place to the top of element!
22765 this.picker().addClass('top');
22766 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22771 this.picker().addClass('bottom');
22773 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22776 parseDate : function(value)
22778 if(!value || value instanceof Date){
22781 var v = Date.parseDate(value, this.format);
22782 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22783 v = Date.parseDate(value, 'Y-m-d');
22785 if(!v && this.altFormats){
22786 if(!this.altFormatsArray){
22787 this.altFormatsArray = this.altFormats.split("|");
22789 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22790 v = Date.parseDate(value, this.altFormatsArray[i]);
22796 formatDate : function(date, fmt)
22798 return (!date || !(date instanceof Date)) ?
22799 date : date.dateFormat(fmt || this.format);
22802 onFocus : function()
22804 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22808 onBlur : function()
22810 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22812 var d = this.inputEl().getValue();
22819 showPopup : function()
22821 this.picker().show();
22825 this.fireEvent('showpopup', this, this.date);
22828 hidePopup : function()
22830 if(this.isInline) {
22833 this.picker().hide();
22834 this.viewMode = this.startViewMode;
22837 this.fireEvent('hidepopup', this, this.date);
22841 onMousedown: function(e)
22843 e.stopPropagation();
22844 e.preventDefault();
22849 Roo.bootstrap.DateField.superclass.keyup.call(this);
22853 setValue: function(v)
22855 if(this.fireEvent('beforeselect', this, v) !== false){
22856 var d = new Date(this.parseDate(v) ).clearTime();
22858 if(isNaN(d.getTime())){
22859 this.date = this.viewDate = '';
22860 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22864 v = this.formatDate(d);
22866 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22868 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22872 this.fireEvent('select', this, this.date);
22876 getValue: function()
22878 return this.formatDate(this.date);
22881 fireKey: function(e)
22883 if (!this.picker().isVisible()){
22884 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22890 var dateChanged = false,
22892 newDate, newViewDate;
22897 e.preventDefault();
22901 if (!this.keyboardNavigation) {
22904 dir = e.keyCode == 37 ? -1 : 1;
22907 newDate = this.moveYear(this.date, dir);
22908 newViewDate = this.moveYear(this.viewDate, dir);
22909 } else if (e.shiftKey){
22910 newDate = this.moveMonth(this.date, dir);
22911 newViewDate = this.moveMonth(this.viewDate, dir);
22913 newDate = new Date(this.date);
22914 newDate.setUTCDate(this.date.getUTCDate() + dir);
22915 newViewDate = new Date(this.viewDate);
22916 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22918 if (this.dateWithinRange(newDate)){
22919 this.date = newDate;
22920 this.viewDate = newViewDate;
22921 this.setValue(this.formatDate(this.date));
22923 e.preventDefault();
22924 dateChanged = true;
22929 if (!this.keyboardNavigation) {
22932 dir = e.keyCode == 38 ? -1 : 1;
22934 newDate = this.moveYear(this.date, dir);
22935 newViewDate = this.moveYear(this.viewDate, dir);
22936 } else if (e.shiftKey){
22937 newDate = this.moveMonth(this.date, dir);
22938 newViewDate = this.moveMonth(this.viewDate, dir);
22940 newDate = new Date(this.date);
22941 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22942 newViewDate = new Date(this.viewDate);
22943 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22945 if (this.dateWithinRange(newDate)){
22946 this.date = newDate;
22947 this.viewDate = newViewDate;
22948 this.setValue(this.formatDate(this.date));
22950 e.preventDefault();
22951 dateChanged = true;
22955 this.setValue(this.formatDate(this.date));
22957 e.preventDefault();
22960 this.setValue(this.formatDate(this.date));
22974 onClick: function(e)
22976 e.stopPropagation();
22977 e.preventDefault();
22979 var target = e.getTarget();
22981 if(target.nodeName.toLowerCase() === 'i'){
22982 target = Roo.get(target).dom.parentNode;
22985 var nodeName = target.nodeName;
22986 var className = target.className;
22987 var html = target.innerHTML;
22988 //Roo.log(nodeName);
22990 switch(nodeName.toLowerCase()) {
22992 switch(className) {
22998 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22999 switch(this.viewMode){
23001 this.viewDate = this.moveMonth(this.viewDate, dir);
23005 this.viewDate = this.moveYear(this.viewDate, dir);
23011 var date = new Date();
23012 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23014 this.setValue(this.formatDate(this.date));
23021 if (className.indexOf('disabled') < 0) {
23022 if (!this.viewDate) {
23023 this.viewDate = new Date();
23025 this.viewDate.setUTCDate(1);
23026 if (className.indexOf('month') > -1) {
23027 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23029 var year = parseInt(html, 10) || 0;
23030 this.viewDate.setUTCFullYear(year);
23034 if(this.singleMode){
23035 this.setValue(this.formatDate(this.viewDate));
23046 //Roo.log(className);
23047 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23048 var day = parseInt(html, 10) || 1;
23049 var year = (this.viewDate || new Date()).getUTCFullYear(),
23050 month = (this.viewDate || new Date()).getUTCMonth();
23052 if (className.indexOf('old') > -1) {
23059 } else if (className.indexOf('new') > -1) {
23067 //Roo.log([year,month,day]);
23068 this.date = this.UTCDate(year, month, day,0,0,0,0);
23069 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23071 //Roo.log(this.formatDate(this.date));
23072 this.setValue(this.formatDate(this.date));
23079 setStartDate: function(startDate)
23081 this.startDate = startDate || -Infinity;
23082 if (this.startDate !== -Infinity) {
23083 this.startDate = this.parseDate(this.startDate);
23086 this.updateNavArrows();
23089 setEndDate: function(endDate)
23091 this.endDate = endDate || Infinity;
23092 if (this.endDate !== Infinity) {
23093 this.endDate = this.parseDate(this.endDate);
23096 this.updateNavArrows();
23099 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23101 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23102 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23103 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23105 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23106 return parseInt(d, 10);
23109 this.updateNavArrows();
23112 updateNavArrows: function()
23114 if(this.singleMode){
23118 var d = new Date(this.viewDate),
23119 year = d.getUTCFullYear(),
23120 month = d.getUTCMonth();
23122 Roo.each(this.picker().select('.prev', true).elements, function(v){
23124 switch (this.viewMode) {
23127 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23133 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23140 Roo.each(this.picker().select('.next', true).elements, function(v){
23142 switch (this.viewMode) {
23145 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23151 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23159 moveMonth: function(date, dir)
23164 var new_date = new Date(date.valueOf()),
23165 day = new_date.getUTCDate(),
23166 month = new_date.getUTCMonth(),
23167 mag = Math.abs(dir),
23169 dir = dir > 0 ? 1 : -1;
23172 // If going back one month, make sure month is not current month
23173 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23175 return new_date.getUTCMonth() == month;
23177 // If going forward one month, make sure month is as expected
23178 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23180 return new_date.getUTCMonth() != new_month;
23182 new_month = month + dir;
23183 new_date.setUTCMonth(new_month);
23184 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23185 if (new_month < 0 || new_month > 11) {
23186 new_month = (new_month + 12) % 12;
23189 // For magnitudes >1, move one month at a time...
23190 for (var i=0; i<mag; i++) {
23191 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23192 new_date = this.moveMonth(new_date, dir);
23194 // ...then reset the day, keeping it in the new month
23195 new_month = new_date.getUTCMonth();
23196 new_date.setUTCDate(day);
23198 return new_month != new_date.getUTCMonth();
23201 // Common date-resetting loop -- if date is beyond end of month, make it
23204 new_date.setUTCDate(--day);
23205 new_date.setUTCMonth(new_month);
23210 moveYear: function(date, dir)
23212 return this.moveMonth(date, dir*12);
23215 dateWithinRange: function(date)
23217 return date >= this.startDate && date <= this.endDate;
23223 this.picker().remove();
23226 validateValue : function(value)
23228 if(this.getVisibilityEl().hasClass('hidden')){
23232 if(value.length < 1) {
23233 if(this.allowBlank){
23239 if(value.length < this.minLength){
23242 if(value.length > this.maxLength){
23246 var vt = Roo.form.VTypes;
23247 if(!vt[this.vtype](value, this)){
23251 if(typeof this.validator == "function"){
23252 var msg = this.validator(value);
23258 if(this.regex && !this.regex.test(value)){
23262 if(typeof(this.parseDate(value)) == 'undefined'){
23266 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23270 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23280 this.date = this.viewDate = '';
23282 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23287 Roo.apply(Roo.bootstrap.DateField, {
23298 html: '<i class="fa fa-arrow-left"/>'
23308 html: '<i class="fa fa-arrow-right"/>'
23350 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23351 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23352 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23353 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23354 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23367 navFnc: 'FullYear',
23372 navFnc: 'FullYear',
23377 Roo.apply(Roo.bootstrap.DateField, {
23381 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23385 cls: 'datepicker-days',
23389 cls: 'table-condensed',
23391 Roo.bootstrap.DateField.head,
23395 Roo.bootstrap.DateField.footer
23402 cls: 'datepicker-months',
23406 cls: 'table-condensed',
23408 Roo.bootstrap.DateField.head,
23409 Roo.bootstrap.DateField.content,
23410 Roo.bootstrap.DateField.footer
23417 cls: 'datepicker-years',
23421 cls: 'table-condensed',
23423 Roo.bootstrap.DateField.head,
23424 Roo.bootstrap.DateField.content,
23425 Roo.bootstrap.DateField.footer
23444 * @class Roo.bootstrap.TimeField
23445 * @extends Roo.bootstrap.Input
23446 * Bootstrap DateField class
23450 * Create a new TimeField
23451 * @param {Object} config The config object
23454 Roo.bootstrap.TimeField = function(config){
23455 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23459 * Fires when this field show.
23460 * @param {Roo.bootstrap.DateField} thisthis
23461 * @param {Mixed} date The date value
23466 * Fires when this field hide.
23467 * @param {Roo.bootstrap.DateField} this
23468 * @param {Mixed} date The date value
23473 * Fires when select a date.
23474 * @param {Roo.bootstrap.DateField} this
23475 * @param {Mixed} date The date value
23481 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23484 * @cfg {String} format
23485 * The default time format string which can be overriden for localization support. The format must be
23486 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23490 getAutoCreate : function()
23492 this.after = '<i class="fa far fa-clock"></i>';
23493 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23497 onRender: function(ct, position)
23500 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23502 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23504 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23506 this.pop = this.picker().select('>.datepicker-time',true).first();
23507 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23509 this.picker().on('mousedown', this.onMousedown, this);
23510 this.picker().on('click', this.onClick, this);
23512 this.picker().addClass('datepicker-dropdown');
23517 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23518 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23519 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23520 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23521 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23522 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23526 fireKey: function(e){
23527 if (!this.picker().isVisible()){
23528 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23534 e.preventDefault();
23542 this.onTogglePeriod();
23545 this.onIncrementMinutes();
23548 this.onDecrementMinutes();
23557 onClick: function(e) {
23558 e.stopPropagation();
23559 e.preventDefault();
23562 picker : function()
23564 return this.pickerEl;
23567 fillTime: function()
23569 var time = this.pop.select('tbody', true).first();
23571 time.dom.innerHTML = '';
23586 cls: 'hours-up fa fas fa-chevron-up'
23606 cls: 'minutes-up fa fas fa-chevron-up'
23627 cls: 'timepicker-hour',
23642 cls: 'timepicker-minute',
23657 cls: 'btn btn-primary period',
23679 cls: 'hours-down fa fas fa-chevron-down'
23699 cls: 'minutes-down fa fas fa-chevron-down'
23717 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23724 var hours = this.time.getHours();
23725 var minutes = this.time.getMinutes();
23738 hours = hours - 12;
23742 hours = '0' + hours;
23746 minutes = '0' + minutes;
23749 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23750 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23751 this.pop.select('button', true).first().dom.innerHTML = period;
23757 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23759 var cls = ['bottom'];
23761 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23768 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23772 //this.picker().setXY(20000,20000);
23773 this.picker().addClass(cls.join('-'));
23777 Roo.each(cls, function(c){
23782 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23783 //_this.picker().setTop(_this.inputEl().getHeight());
23787 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23789 //_this.picker().setTop(0 - _this.picker().getHeight());
23794 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23798 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23806 onFocus : function()
23808 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23812 onBlur : function()
23814 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23820 this.picker().show();
23825 this.fireEvent('show', this, this.date);
23830 this.picker().hide();
23833 this.fireEvent('hide', this, this.date);
23836 setTime : function()
23839 this.setValue(this.time.format(this.format));
23841 this.fireEvent('select', this, this.date);
23846 onMousedown: function(e){
23847 e.stopPropagation();
23848 e.preventDefault();
23851 onIncrementHours: function()
23853 Roo.log('onIncrementHours');
23854 this.time = this.time.add(Date.HOUR, 1);
23859 onDecrementHours: function()
23861 Roo.log('onDecrementHours');
23862 this.time = this.time.add(Date.HOUR, -1);
23866 onIncrementMinutes: function()
23868 Roo.log('onIncrementMinutes');
23869 this.time = this.time.add(Date.MINUTE, 1);
23873 onDecrementMinutes: function()
23875 Roo.log('onDecrementMinutes');
23876 this.time = this.time.add(Date.MINUTE, -1);
23880 onTogglePeriod: function()
23882 Roo.log('onTogglePeriod');
23883 this.time = this.time.add(Date.HOUR, 12);
23891 Roo.apply(Roo.bootstrap.TimeField, {
23895 cls: 'datepicker dropdown-menu',
23899 cls: 'datepicker-time',
23903 cls: 'table-condensed',
23932 cls: 'btn btn-info ok',
23960 * @class Roo.bootstrap.MonthField
23961 * @extends Roo.bootstrap.Input
23962 * Bootstrap MonthField class
23964 * @cfg {String} language default en
23967 * Create a new MonthField
23968 * @param {Object} config The config object
23971 Roo.bootstrap.MonthField = function(config){
23972 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23977 * Fires when this field show.
23978 * @param {Roo.bootstrap.MonthField} this
23979 * @param {Mixed} date The date value
23984 * Fires when this field hide.
23985 * @param {Roo.bootstrap.MonthField} this
23986 * @param {Mixed} date The date value
23991 * Fires when select a date.
23992 * @param {Roo.bootstrap.MonthField} this
23993 * @param {String} oldvalue The old value
23994 * @param {String} newvalue The new value
24000 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
24002 onRender: function(ct, position)
24005 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24007 this.language = this.language || 'en';
24008 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24009 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24011 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24012 this.isInline = false;
24013 this.isInput = true;
24014 this.component = this.el.select('.add-on', true).first() || false;
24015 this.component = (this.component && this.component.length === 0) ? false : this.component;
24016 this.hasInput = this.component && this.inputEL().length;
24018 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24020 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24022 this.picker().on('mousedown', this.onMousedown, this);
24023 this.picker().on('click', this.onClick, this);
24025 this.picker().addClass('datepicker-dropdown');
24027 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24028 v.setStyle('width', '189px');
24035 if(this.isInline) {
24041 setValue: function(v, suppressEvent)
24043 var o = this.getValue();
24045 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24049 if(suppressEvent !== true){
24050 this.fireEvent('select', this, o, v);
24055 getValue: function()
24060 onClick: function(e)
24062 e.stopPropagation();
24063 e.preventDefault();
24065 var target = e.getTarget();
24067 if(target.nodeName.toLowerCase() === 'i'){
24068 target = Roo.get(target).dom.parentNode;
24071 var nodeName = target.nodeName;
24072 var className = target.className;
24073 var html = target.innerHTML;
24075 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24079 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24081 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24087 picker : function()
24089 return this.pickerEl;
24092 fillMonths: function()
24095 var months = this.picker().select('>.datepicker-months td', true).first();
24097 months.dom.innerHTML = '';
24103 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24106 months.createChild(month);
24115 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24116 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24119 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24120 e.removeClass('active');
24122 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24123 e.addClass('active');
24130 if(this.isInline) {
24134 this.picker().removeClass(['bottom', 'top']);
24136 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24138 * place to the top of element!
24142 this.picker().addClass('top');
24143 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24148 this.picker().addClass('bottom');
24150 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24153 onFocus : function()
24155 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24159 onBlur : function()
24161 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24163 var d = this.inputEl().getValue();
24172 this.picker().show();
24173 this.picker().select('>.datepicker-months', true).first().show();
24177 this.fireEvent('show', this, this.date);
24182 if(this.isInline) {
24185 this.picker().hide();
24186 this.fireEvent('hide', this, this.date);
24190 onMousedown: function(e)
24192 e.stopPropagation();
24193 e.preventDefault();
24198 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24202 fireKey: function(e)
24204 if (!this.picker().isVisible()){
24205 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24216 e.preventDefault();
24220 dir = e.keyCode == 37 ? -1 : 1;
24222 this.vIndex = this.vIndex + dir;
24224 if(this.vIndex < 0){
24228 if(this.vIndex > 11){
24232 if(isNaN(this.vIndex)){
24236 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24242 dir = e.keyCode == 38 ? -1 : 1;
24244 this.vIndex = this.vIndex + dir * 4;
24246 if(this.vIndex < 0){
24250 if(this.vIndex > 11){
24254 if(isNaN(this.vIndex)){
24258 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24263 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24264 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24268 e.preventDefault();
24271 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24272 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24288 this.picker().remove();
24293 Roo.apply(Roo.bootstrap.MonthField, {
24312 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24313 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24318 Roo.apply(Roo.bootstrap.MonthField, {
24322 cls: 'datepicker dropdown-menu roo-dynamic',
24326 cls: 'datepicker-months',
24330 cls: 'table-condensed',
24332 Roo.bootstrap.DateField.content
24352 * @class Roo.bootstrap.CheckBox
24353 * @extends Roo.bootstrap.Input
24354 * Bootstrap CheckBox class
24356 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24357 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24358 * @cfg {String} boxLabel The text that appears beside the checkbox
24359 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24360 * @cfg {Boolean} checked initnal the element
24361 * @cfg {Boolean} inline inline the element (default false)
24362 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24363 * @cfg {String} tooltip label tooltip
24366 * Create a new CheckBox
24367 * @param {Object} config The config object
24370 Roo.bootstrap.CheckBox = function(config){
24371 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24376 * Fires when the element is checked or unchecked.
24377 * @param {Roo.bootstrap.CheckBox} this This input
24378 * @param {Boolean} checked The new checked value
24383 * Fires when the element is click.
24384 * @param {Roo.bootstrap.CheckBox} this This input
24391 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24393 inputType: 'checkbox',
24402 // checkbox success does not make any sense really..
24407 getAutoCreate : function()
24409 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24415 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24418 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24424 type : this.inputType,
24425 value : this.inputValue,
24426 cls : 'roo-' + this.inputType, //'form-box',
24427 placeholder : this.placeholder || ''
24431 if(this.inputType != 'radio'){
24435 cls : 'roo-hidden-value',
24436 value : this.checked ? this.inputValue : this.valueOff
24441 if (this.weight) { // Validity check?
24442 cfg.cls += " " + this.inputType + "-" + this.weight;
24445 if (this.disabled) {
24446 input.disabled=true;
24450 input.checked = this.checked;
24455 input.name = this.name;
24457 if(this.inputType != 'radio'){
24458 hidden.name = this.name;
24459 input.name = '_hidden_' + this.name;
24464 input.cls += ' input-' + this.size;
24469 ['xs','sm','md','lg'].map(function(size){
24470 if (settings[size]) {
24471 cfg.cls += ' col-' + size + '-' + settings[size];
24475 var inputblock = input;
24477 if (this.before || this.after) {
24480 cls : 'input-group',
24485 inputblock.cn.push({
24487 cls : 'input-group-addon',
24492 inputblock.cn.push(input);
24494 if(this.inputType != 'radio'){
24495 inputblock.cn.push(hidden);
24499 inputblock.cn.push({
24501 cls : 'input-group-addon',
24507 var boxLabelCfg = false;
24513 //'for': id, // box label is handled by onclick - so no for...
24515 html: this.boxLabel
24518 boxLabelCfg.tooltip = this.tooltip;
24524 if (align ==='left' && this.fieldLabel.length) {
24525 // Roo.log("left and has label");
24530 cls : 'control-label',
24531 html : this.fieldLabel
24542 cfg.cn[1].cn.push(boxLabelCfg);
24545 if(this.labelWidth > 12){
24546 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24549 if(this.labelWidth < 13 && this.labelmd == 0){
24550 this.labelmd = this.labelWidth;
24553 if(this.labellg > 0){
24554 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24555 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24558 if(this.labelmd > 0){
24559 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24560 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24563 if(this.labelsm > 0){
24564 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24565 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24568 if(this.labelxs > 0){
24569 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24570 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24573 } else if ( this.fieldLabel.length) {
24574 // Roo.log(" label");
24578 tag: this.boxLabel ? 'span' : 'label',
24580 cls: 'control-label box-input-label',
24581 //cls : 'input-group-addon',
24582 html : this.fieldLabel
24589 cfg.cn.push(boxLabelCfg);
24594 // Roo.log(" no label && no align");
24595 cfg.cn = [ inputblock ] ;
24597 cfg.cn.push(boxLabelCfg);
24605 if(this.inputType != 'radio'){
24606 cfg.cn.push(hidden);
24614 * return the real input element.
24616 inputEl: function ()
24618 return this.el.select('input.roo-' + this.inputType,true).first();
24620 hiddenEl: function ()
24622 return this.el.select('input.roo-hidden-value',true).first();
24625 labelEl: function()
24627 return this.el.select('label.control-label',true).first();
24629 /* depricated... */
24633 return this.labelEl();
24636 boxLabelEl: function()
24638 return this.el.select('label.box-label',true).first();
24641 initEvents : function()
24643 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24645 this.inputEl().on('click', this.onClick, this);
24647 if (this.boxLabel) {
24648 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24651 this.startValue = this.getValue();
24654 Roo.bootstrap.CheckBox.register(this);
24658 onClick : function(e)
24660 if(this.fireEvent('click', this, e) !== false){
24661 this.setChecked(!this.checked);
24666 setChecked : function(state,suppressEvent)
24668 this.startValue = this.getValue();
24670 if(this.inputType == 'radio'){
24672 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24673 e.dom.checked = false;
24676 this.inputEl().dom.checked = true;
24678 this.inputEl().dom.value = this.inputValue;
24680 if(suppressEvent !== true){
24681 this.fireEvent('check', this, true);
24689 this.checked = state;
24691 this.inputEl().dom.checked = state;
24694 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24696 if(suppressEvent !== true){
24697 this.fireEvent('check', this, state);
24703 getValue : function()
24705 if(this.inputType == 'radio'){
24706 return this.getGroupValue();
24709 return this.hiddenEl().dom.value;
24713 getGroupValue : function()
24715 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24719 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24722 setValue : function(v,suppressEvent)
24724 if(this.inputType == 'radio'){
24725 this.setGroupValue(v, suppressEvent);
24729 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24734 setGroupValue : function(v, suppressEvent)
24736 this.startValue = this.getValue();
24738 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24739 e.dom.checked = false;
24741 if(e.dom.value == v){
24742 e.dom.checked = true;
24746 if(suppressEvent !== true){
24747 this.fireEvent('check', this, true);
24755 validate : function()
24757 if(this.getVisibilityEl().hasClass('hidden')){
24763 (this.inputType == 'radio' && this.validateRadio()) ||
24764 (this.inputType == 'checkbox' && this.validateCheckbox())
24770 this.markInvalid();
24774 validateRadio : function()
24776 if(this.getVisibilityEl().hasClass('hidden')){
24780 if(this.allowBlank){
24786 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24787 if(!e.dom.checked){
24799 validateCheckbox : function()
24802 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24803 //return (this.getValue() == this.inputValue) ? true : false;
24806 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24814 for(var i in group){
24815 if(group[i].el.isVisible(true)){
24823 for(var i in group){
24828 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24835 * Mark this field as valid
24837 markValid : function()
24841 this.fireEvent('valid', this);
24843 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24846 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24853 if(this.inputType == 'radio'){
24854 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24855 var fg = e.findParent('.form-group', false, true);
24856 if (Roo.bootstrap.version == 3) {
24857 fg.removeClass([_this.invalidClass, _this.validClass]);
24858 fg.addClass(_this.validClass);
24860 fg.removeClass(['is-valid', 'is-invalid']);
24861 fg.addClass('is-valid');
24869 var fg = this.el.findParent('.form-group', false, true);
24870 if (Roo.bootstrap.version == 3) {
24871 fg.removeClass([this.invalidClass, this.validClass]);
24872 fg.addClass(this.validClass);
24874 fg.removeClass(['is-valid', 'is-invalid']);
24875 fg.addClass('is-valid');
24880 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24886 for(var i in group){
24887 var fg = group[i].el.findParent('.form-group', false, true);
24888 if (Roo.bootstrap.version == 3) {
24889 fg.removeClass([this.invalidClass, this.validClass]);
24890 fg.addClass(this.validClass);
24892 fg.removeClass(['is-valid', 'is-invalid']);
24893 fg.addClass('is-valid');
24899 * Mark this field as invalid
24900 * @param {String} msg The validation message
24902 markInvalid : function(msg)
24904 if(this.allowBlank){
24910 this.fireEvent('invalid', this, msg);
24912 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24915 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24919 label.markInvalid();
24922 if(this.inputType == 'radio'){
24924 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24925 var fg = e.findParent('.form-group', false, true);
24926 if (Roo.bootstrap.version == 3) {
24927 fg.removeClass([_this.invalidClass, _this.validClass]);
24928 fg.addClass(_this.invalidClass);
24930 fg.removeClass(['is-invalid', 'is-valid']);
24931 fg.addClass('is-invalid');
24939 var fg = this.el.findParent('.form-group', false, true);
24940 if (Roo.bootstrap.version == 3) {
24941 fg.removeClass([_this.invalidClass, _this.validClass]);
24942 fg.addClass(_this.invalidClass);
24944 fg.removeClass(['is-invalid', 'is-valid']);
24945 fg.addClass('is-invalid');
24950 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24956 for(var i in group){
24957 var fg = group[i].el.findParent('.form-group', false, true);
24958 if (Roo.bootstrap.version == 3) {
24959 fg.removeClass([_this.invalidClass, _this.validClass]);
24960 fg.addClass(_this.invalidClass);
24962 fg.removeClass(['is-invalid', 'is-valid']);
24963 fg.addClass('is-invalid');
24969 clearInvalid : function()
24971 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24973 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24975 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24977 if (label && label.iconEl) {
24978 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24979 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24983 disable : function()
24985 if(this.inputType != 'radio'){
24986 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24993 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24994 _this.getActionEl().addClass(this.disabledClass);
24995 e.dom.disabled = true;
24999 this.disabled = true;
25000 this.fireEvent("disable", this);
25004 enable : function()
25006 if(this.inputType != 'radio'){
25007 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25014 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25015 _this.getActionEl().removeClass(this.disabledClass);
25016 e.dom.disabled = false;
25020 this.disabled = false;
25021 this.fireEvent("enable", this);
25025 setBoxLabel : function(v)
25030 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25036 Roo.apply(Roo.bootstrap.CheckBox, {
25041 * register a CheckBox Group
25042 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25044 register : function(checkbox)
25046 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25047 this.groups[checkbox.groupId] = {};
25050 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25054 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25058 * fetch a CheckBox Group based on the group ID
25059 * @param {string} the group ID
25060 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25062 get: function(groupId) {
25063 if (typeof(this.groups[groupId]) == 'undefined') {
25067 return this.groups[groupId] ;
25080 * @class Roo.bootstrap.Radio
25081 * @extends Roo.bootstrap.Component
25082 * Bootstrap Radio class
25083 * @cfg {String} boxLabel - the label associated
25084 * @cfg {String} value - the value of radio
25087 * Create a new Radio
25088 * @param {Object} config The config object
25090 Roo.bootstrap.Radio = function(config){
25091 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25095 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25101 getAutoCreate : function()
25105 cls : 'form-group radio',
25110 html : this.boxLabel
25118 initEvents : function()
25120 this.parent().register(this);
25122 this.el.on('click', this.onClick, this);
25126 onClick : function(e)
25128 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25129 this.setChecked(true);
25133 setChecked : function(state, suppressEvent)
25135 this.parent().setValue(this.value, suppressEvent);
25139 setBoxLabel : function(v)
25144 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25159 * @class Roo.bootstrap.SecurePass
25160 * @extends Roo.bootstrap.Input
25161 * Bootstrap SecurePass class
25165 * Create a new SecurePass
25166 * @param {Object} config The config object
25169 Roo.bootstrap.SecurePass = function (config) {
25170 // these go here, so the translation tool can replace them..
25172 PwdEmpty: "Please type a password, and then retype it to confirm.",
25173 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25174 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25175 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25176 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25177 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25178 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25179 TooWeak: "Your password is Too Weak."
25181 this.meterLabel = "Password strength:";
25182 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25183 this.meterClass = [
25184 "roo-password-meter-tooweak",
25185 "roo-password-meter-weak",
25186 "roo-password-meter-medium",
25187 "roo-password-meter-strong",
25188 "roo-password-meter-grey"
25193 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25196 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25198 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25200 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25201 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25202 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25203 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25204 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25205 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25206 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25216 * @cfg {String/Object} Label for the strength meter (defaults to
25217 * 'Password strength:')
25222 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25223 * ['Weak', 'Medium', 'Strong'])
25226 pwdStrengths: false,
25239 initEvents: function ()
25241 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25243 if (this.el.is('input[type=password]') && Roo.isSafari) {
25244 this.el.on('keydown', this.SafariOnKeyDown, this);
25247 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25250 onRender: function (ct, position)
25252 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25253 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25254 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25256 this.trigger.createChild({
25261 cls: 'roo-password-meter-grey col-xs-12',
25264 //width: this.meterWidth + 'px'
25268 cls: 'roo-password-meter-text'
25274 if (this.hideTrigger) {
25275 this.trigger.setDisplayed(false);
25277 this.setSize(this.width || '', this.height || '');
25280 onDestroy: function ()
25282 if (this.trigger) {
25283 this.trigger.removeAllListeners();
25284 this.trigger.remove();
25287 this.wrap.remove();
25289 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25292 checkStrength: function ()
25294 var pwd = this.inputEl().getValue();
25295 if (pwd == this._lastPwd) {
25300 if (this.ClientSideStrongPassword(pwd)) {
25302 } else if (this.ClientSideMediumPassword(pwd)) {
25304 } else if (this.ClientSideWeakPassword(pwd)) {
25310 Roo.log('strength1: ' + strength);
25312 //var pm = this.trigger.child('div/div/div').dom;
25313 var pm = this.trigger.child('div/div');
25314 pm.removeClass(this.meterClass);
25315 pm.addClass(this.meterClass[strength]);
25318 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25320 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25322 this._lastPwd = pwd;
25326 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25328 this._lastPwd = '';
25330 var pm = this.trigger.child('div/div');
25331 pm.removeClass(this.meterClass);
25332 pm.addClass('roo-password-meter-grey');
25335 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25338 this.inputEl().dom.type='password';
25341 validateValue: function (value)
25343 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25346 if (value.length == 0) {
25347 if (this.allowBlank) {
25348 this.clearInvalid();
25352 this.markInvalid(this.errors.PwdEmpty);
25353 this.errorMsg = this.errors.PwdEmpty;
25361 if (!value.match(/[\x21-\x7e]+/)) {
25362 this.markInvalid(this.errors.PwdBadChar);
25363 this.errorMsg = this.errors.PwdBadChar;
25366 if (value.length < 6) {
25367 this.markInvalid(this.errors.PwdShort);
25368 this.errorMsg = this.errors.PwdShort;
25371 if (value.length > 16) {
25372 this.markInvalid(this.errors.PwdLong);
25373 this.errorMsg = this.errors.PwdLong;
25377 if (this.ClientSideStrongPassword(value)) {
25379 } else if (this.ClientSideMediumPassword(value)) {
25381 } else if (this.ClientSideWeakPassword(value)) {
25388 if (strength < 2) {
25389 //this.markInvalid(this.errors.TooWeak);
25390 this.errorMsg = this.errors.TooWeak;
25395 console.log('strength2: ' + strength);
25397 //var pm = this.trigger.child('div/div/div').dom;
25399 var pm = this.trigger.child('div/div');
25400 pm.removeClass(this.meterClass);
25401 pm.addClass(this.meterClass[strength]);
25403 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25405 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25407 this.errorMsg = '';
25411 CharacterSetChecks: function (type)
25414 this.fResult = false;
25417 isctype: function (character, type)
25420 case this.kCapitalLetter:
25421 if (character >= 'A' && character <= 'Z') {
25426 case this.kSmallLetter:
25427 if (character >= 'a' && character <= 'z') {
25433 if (character >= '0' && character <= '9') {
25438 case this.kPunctuation:
25439 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25450 IsLongEnough: function (pwd, size)
25452 return !(pwd == null || isNaN(size) || pwd.length < size);
25455 SpansEnoughCharacterSets: function (word, nb)
25457 if (!this.IsLongEnough(word, nb))
25462 var characterSetChecks = new Array(
25463 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25464 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25467 for (var index = 0; index < word.length; ++index) {
25468 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25469 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25470 characterSetChecks[nCharSet].fResult = true;
25477 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25478 if (characterSetChecks[nCharSet].fResult) {
25483 if (nCharSets < nb) {
25489 ClientSideStrongPassword: function (pwd)
25491 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25494 ClientSideMediumPassword: function (pwd)
25496 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25499 ClientSideWeakPassword: function (pwd)
25501 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25504 })//<script type="text/javascript">
25507 * Based Ext JS Library 1.1.1
25508 * Copyright(c) 2006-2007, Ext JS, LLC.
25514 * @class Roo.HtmlEditorCore
25515 * @extends Roo.Component
25516 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25518 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25521 Roo.HtmlEditorCore = function(config){
25524 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25529 * @event initialize
25530 * Fires when the editor is fully initialized (including the iframe)
25531 * @param {Roo.HtmlEditorCore} this
25536 * Fires when the editor is first receives the focus. Any insertion must wait
25537 * until after this event.
25538 * @param {Roo.HtmlEditorCore} this
25542 * @event beforesync
25543 * Fires before the textarea is updated with content from the editor iframe. Return false
25544 * to cancel the sync.
25545 * @param {Roo.HtmlEditorCore} this
25546 * @param {String} html
25550 * @event beforepush
25551 * Fires before the iframe editor is updated with content from the textarea. Return false
25552 * to cancel the push.
25553 * @param {Roo.HtmlEditorCore} this
25554 * @param {String} html
25559 * Fires when the textarea is updated with content from the editor iframe.
25560 * @param {Roo.HtmlEditorCore} this
25561 * @param {String} html
25566 * Fires when the iframe editor is updated with content from the textarea.
25567 * @param {Roo.HtmlEditorCore} this
25568 * @param {String} html
25573 * @event editorevent
25574 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25575 * @param {Roo.HtmlEditorCore} this
25581 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25583 // defaults : white / black...
25584 this.applyBlacklists();
25591 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25595 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25601 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25606 * @cfg {Number} height (in pixels)
25610 * @cfg {Number} width (in pixels)
25615 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25618 stylesheets: false,
25621 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25623 allowComments: false,
25627 // private properties
25628 validationEvent : false,
25630 initialized : false,
25632 sourceEditMode : false,
25633 onFocus : Roo.emptyFn,
25635 hideMode:'offsets',
25639 // blacklist + whitelisted elements..
25646 * Protected method that will not generally be called directly. It
25647 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25648 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25650 getDocMarkup : function(){
25654 // inherit styels from page...??
25655 if (this.stylesheets === false) {
25657 Roo.get(document.head).select('style').each(function(node) {
25658 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25661 Roo.get(document.head).select('link').each(function(node) {
25662 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25665 } else if (!this.stylesheets.length) {
25667 st = '<style type="text/css">' +
25668 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25671 for (var i in this.stylesheets) {
25672 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25677 st += '<style type="text/css">' +
25678 'IMG { cursor: pointer } ' +
25681 var cls = 'roo-htmleditor-body';
25683 if(this.bodyCls.length){
25684 cls += ' ' + this.bodyCls;
25687 return '<html><head>' + st +
25688 //<style type="text/css">' +
25689 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25691 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25695 onRender : function(ct, position)
25698 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25699 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25702 this.el.dom.style.border = '0 none';
25703 this.el.dom.setAttribute('tabIndex', -1);
25704 this.el.addClass('x-hidden hide');
25708 if(Roo.isIE){ // fix IE 1px bogus margin
25709 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25713 this.frameId = Roo.id();
25717 var iframe = this.owner.wrap.createChild({
25719 cls: 'form-control', // bootstrap..
25721 name: this.frameId,
25722 frameBorder : 'no',
25723 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25728 this.iframe = iframe.dom;
25730 this.assignDocWin();
25732 this.doc.designMode = 'on';
25735 this.doc.write(this.getDocMarkup());
25739 var task = { // must defer to wait for browser to be ready
25741 //console.log("run task?" + this.doc.readyState);
25742 this.assignDocWin();
25743 if(this.doc.body || this.doc.readyState == 'complete'){
25745 this.doc.designMode="on";
25749 Roo.TaskMgr.stop(task);
25750 this.initEditor.defer(10, this);
25757 Roo.TaskMgr.start(task);
25762 onResize : function(w, h)
25764 Roo.log('resize: ' +w + ',' + h );
25765 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25769 if(typeof w == 'number'){
25771 this.iframe.style.width = w + 'px';
25773 if(typeof h == 'number'){
25775 this.iframe.style.height = h + 'px';
25777 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25784 * Toggles the editor between standard and source edit mode.
25785 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25787 toggleSourceEdit : function(sourceEditMode){
25789 this.sourceEditMode = sourceEditMode === true;
25791 if(this.sourceEditMode){
25793 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25796 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25797 //this.iframe.className = '';
25800 //this.setSize(this.owner.wrap.getSize());
25801 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25808 * Protected method that will not generally be called directly. If you need/want
25809 * custom HTML cleanup, this is the method you should override.
25810 * @param {String} html The HTML to be cleaned
25811 * return {String} The cleaned HTML
25813 cleanHtml : function(html){
25814 html = String(html);
25815 if(html.length > 5){
25816 if(Roo.isSafari){ // strip safari nonsense
25817 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25820 if(html == ' '){
25827 * HTML Editor -> Textarea
25828 * Protected method that will not generally be called directly. Syncs the contents
25829 * of the editor iframe with the textarea.
25831 syncValue : function(){
25832 if(this.initialized){
25833 var bd = (this.doc.body || this.doc.documentElement);
25834 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25835 var html = bd.innerHTML;
25837 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25838 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25840 html = '<div style="'+m[0]+'">' + html + '</div>';
25843 html = this.cleanHtml(html);
25844 // fix up the special chars.. normaly like back quotes in word...
25845 // however we do not want to do this with chinese..
25846 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25848 var cc = match.charCodeAt();
25850 // Get the character value, handling surrogate pairs
25851 if (match.length == 2) {
25852 // It's a surrogate pair, calculate the Unicode code point
25853 var high = match.charCodeAt(0) - 0xD800;
25854 var low = match.charCodeAt(1) - 0xDC00;
25855 cc = (high * 0x400) + low + 0x10000;
25857 (cc >= 0x4E00 && cc < 0xA000 ) ||
25858 (cc >= 0x3400 && cc < 0x4E00 ) ||
25859 (cc >= 0xf900 && cc < 0xfb00 )
25864 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25865 return "&#" + cc + ";";
25872 if(this.owner.fireEvent('beforesync', this, html) !== false){
25873 this.el.dom.value = html;
25874 this.owner.fireEvent('sync', this, html);
25880 * Protected method that will not generally be called directly. Pushes the value of the textarea
25881 * into the iframe editor.
25883 pushValue : function(){
25884 if(this.initialized){
25885 var v = this.el.dom.value.trim();
25887 // if(v.length < 1){
25891 if(this.owner.fireEvent('beforepush', this, v) !== false){
25892 var d = (this.doc.body || this.doc.documentElement);
25894 this.cleanUpPaste();
25895 this.el.dom.value = d.innerHTML;
25896 this.owner.fireEvent('push', this, v);
25902 deferFocus : function(){
25903 this.focus.defer(10, this);
25907 focus : function(){
25908 if(this.win && !this.sourceEditMode){
25915 assignDocWin: function()
25917 var iframe = this.iframe;
25920 this.doc = iframe.contentWindow.document;
25921 this.win = iframe.contentWindow;
25923 // if (!Roo.get(this.frameId)) {
25926 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25927 // this.win = Roo.get(this.frameId).dom.contentWindow;
25929 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25933 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25934 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25939 initEditor : function(){
25940 //console.log("INIT EDITOR");
25941 this.assignDocWin();
25945 this.doc.designMode="on";
25947 this.doc.write(this.getDocMarkup());
25950 var dbody = (this.doc.body || this.doc.documentElement);
25951 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25952 // this copies styles from the containing element into thsi one..
25953 // not sure why we need all of this..
25954 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25956 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25957 //ss['background-attachment'] = 'fixed'; // w3c
25958 dbody.bgProperties = 'fixed'; // ie
25959 //Roo.DomHelper.applyStyles(dbody, ss);
25960 Roo.EventManager.on(this.doc, {
25961 //'mousedown': this.onEditorEvent,
25962 'mouseup': this.onEditorEvent,
25963 'dblclick': this.onEditorEvent,
25964 'click': this.onEditorEvent,
25965 'keyup': this.onEditorEvent,
25970 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25972 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25973 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25975 this.initialized = true;
25977 this.owner.fireEvent('initialize', this);
25982 onDestroy : function(){
25988 //for (var i =0; i < this.toolbars.length;i++) {
25989 // // fixme - ask toolbars for heights?
25990 // this.toolbars[i].onDestroy();
25993 //this.wrap.dom.innerHTML = '';
25994 //this.wrap.remove();
25999 onFirstFocus : function(){
26001 this.assignDocWin();
26004 this.activated = true;
26007 if(Roo.isGecko){ // prevent silly gecko errors
26009 var s = this.win.getSelection();
26010 if(!s.focusNode || s.focusNode.nodeType != 3){
26011 var r = s.getRangeAt(0);
26012 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26017 this.execCmd('useCSS', true);
26018 this.execCmd('styleWithCSS', false);
26021 this.owner.fireEvent('activate', this);
26025 adjustFont: function(btn){
26026 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26027 //if(Roo.isSafari){ // safari
26030 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26031 if(Roo.isSafari){ // safari
26032 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26033 v = (v < 10) ? 10 : v;
26034 v = (v > 48) ? 48 : v;
26035 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26040 v = Math.max(1, v+adjust);
26042 this.execCmd('FontSize', v );
26045 onEditorEvent : function(e)
26047 this.owner.fireEvent('editorevent', this, e);
26048 // this.updateToolbar();
26049 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26052 insertTag : function(tg)
26054 // could be a bit smarter... -> wrap the current selected tRoo..
26055 if (tg.toLowerCase() == 'span' ||
26056 tg.toLowerCase() == 'code' ||
26057 tg.toLowerCase() == 'sup' ||
26058 tg.toLowerCase() == 'sub'
26061 range = this.createRange(this.getSelection());
26062 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26063 wrappingNode.appendChild(range.extractContents());
26064 range.insertNode(wrappingNode);
26071 this.execCmd("formatblock", tg);
26075 insertText : function(txt)
26079 var range = this.createRange();
26080 range.deleteContents();
26081 //alert(Sender.getAttribute('label'));
26083 range.insertNode(this.doc.createTextNode(txt));
26089 * Executes a Midas editor command on the editor document and performs necessary focus and
26090 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26091 * @param {String} cmd The Midas command
26092 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26094 relayCmd : function(cmd, value){
26096 this.execCmd(cmd, value);
26097 this.owner.fireEvent('editorevent', this);
26098 //this.updateToolbar();
26099 this.owner.deferFocus();
26103 * Executes a Midas editor command directly on the editor document.
26104 * For visual commands, you should use {@link #relayCmd} instead.
26105 * <b>This should only be called after the editor is initialized.</b>
26106 * @param {String} cmd The Midas command
26107 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26109 execCmd : function(cmd, value){
26110 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26117 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26119 * @param {String} text | dom node..
26121 insertAtCursor : function(text)
26124 if(!this.activated){
26130 var r = this.doc.selection.createRange();
26141 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26145 // from jquery ui (MIT licenced)
26147 var win = this.win;
26149 if (win.getSelection && win.getSelection().getRangeAt) {
26150 range = win.getSelection().getRangeAt(0);
26151 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26152 range.insertNode(node);
26153 } else if (win.document.selection && win.document.selection.createRange) {
26154 // no firefox support
26155 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26156 win.document.selection.createRange().pasteHTML(txt);
26158 // no firefox support
26159 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26160 this.execCmd('InsertHTML', txt);
26169 mozKeyPress : function(e){
26171 var c = e.getCharCode(), cmd;
26174 c = String.fromCharCode(c).toLowerCase();
26188 this.cleanUpPaste.defer(100, this);
26196 e.preventDefault();
26204 fixKeys : function(){ // load time branching for fastest keydown performance
26206 return function(e){
26207 var k = e.getKey(), r;
26210 r = this.doc.selection.createRange();
26213 r.pasteHTML('    ');
26220 r = this.doc.selection.createRange();
26222 var target = r.parentElement();
26223 if(!target || target.tagName.toLowerCase() != 'li'){
26225 r.pasteHTML('<br />');
26231 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26232 this.cleanUpPaste.defer(100, this);
26238 }else if(Roo.isOpera){
26239 return function(e){
26240 var k = e.getKey();
26244 this.execCmd('InsertHTML','    ');
26247 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26248 this.cleanUpPaste.defer(100, this);
26253 }else if(Roo.isSafari){
26254 return function(e){
26255 var k = e.getKey();
26259 this.execCmd('InsertText','\t');
26263 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26264 this.cleanUpPaste.defer(100, this);
26272 getAllAncestors: function()
26274 var p = this.getSelectedNode();
26277 a.push(p); // push blank onto stack..
26278 p = this.getParentElement();
26282 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26286 a.push(this.doc.body);
26290 lastSelNode : false,
26293 getSelection : function()
26295 this.assignDocWin();
26296 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26299 getSelectedNode: function()
26301 // this may only work on Gecko!!!
26303 // should we cache this!!!!
26308 var range = this.createRange(this.getSelection()).cloneRange();
26311 var parent = range.parentElement();
26313 var testRange = range.duplicate();
26314 testRange.moveToElementText(parent);
26315 if (testRange.inRange(range)) {
26318 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26321 parent = parent.parentElement;
26326 // is ancestor a text element.
26327 var ac = range.commonAncestorContainer;
26328 if (ac.nodeType == 3) {
26329 ac = ac.parentNode;
26332 var ar = ac.childNodes;
26335 var other_nodes = [];
26336 var has_other_nodes = false;
26337 for (var i=0;i<ar.length;i++) {
26338 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26341 // fullly contained node.
26343 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26348 // probably selected..
26349 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26350 other_nodes.push(ar[i]);
26354 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26359 has_other_nodes = true;
26361 if (!nodes.length && other_nodes.length) {
26362 nodes= other_nodes;
26364 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26370 createRange: function(sel)
26372 // this has strange effects when using with
26373 // top toolbar - not sure if it's a great idea.
26374 //this.editor.contentWindow.focus();
26375 if (typeof sel != "undefined") {
26377 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26379 return this.doc.createRange();
26382 return this.doc.createRange();
26385 getParentElement: function()
26388 this.assignDocWin();
26389 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26391 var range = this.createRange(sel);
26394 var p = range.commonAncestorContainer;
26395 while (p.nodeType == 3) { // text node
26406 * Range intersection.. the hard stuff...
26410 * [ -- selected range --- ]
26414 * if end is before start or hits it. fail.
26415 * if start is after end or hits it fail.
26417 * if either hits (but other is outside. - then it's not
26423 // @see http://www.thismuchiknow.co.uk/?p=64.
26424 rangeIntersectsNode : function(range, node)
26426 var nodeRange = node.ownerDocument.createRange();
26428 nodeRange.selectNode(node);
26430 nodeRange.selectNodeContents(node);
26433 var rangeStartRange = range.cloneRange();
26434 rangeStartRange.collapse(true);
26436 var rangeEndRange = range.cloneRange();
26437 rangeEndRange.collapse(false);
26439 var nodeStartRange = nodeRange.cloneRange();
26440 nodeStartRange.collapse(true);
26442 var nodeEndRange = nodeRange.cloneRange();
26443 nodeEndRange.collapse(false);
26445 return rangeStartRange.compareBoundaryPoints(
26446 Range.START_TO_START, nodeEndRange) == -1 &&
26447 rangeEndRange.compareBoundaryPoints(
26448 Range.START_TO_START, nodeStartRange) == 1;
26452 rangeCompareNode : function(range, node)
26454 var nodeRange = node.ownerDocument.createRange();
26456 nodeRange.selectNode(node);
26458 nodeRange.selectNodeContents(node);
26462 range.collapse(true);
26464 nodeRange.collapse(true);
26466 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26467 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26469 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26471 var nodeIsBefore = ss == 1;
26472 var nodeIsAfter = ee == -1;
26474 if (nodeIsBefore && nodeIsAfter) {
26477 if (!nodeIsBefore && nodeIsAfter) {
26478 return 1; //right trailed.
26481 if (nodeIsBefore && !nodeIsAfter) {
26482 return 2; // left trailed.
26488 // private? - in a new class?
26489 cleanUpPaste : function()
26491 // cleans up the whole document..
26492 Roo.log('cleanuppaste');
26494 this.cleanUpChildren(this.doc.body);
26495 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26496 if (clean != this.doc.body.innerHTML) {
26497 this.doc.body.innerHTML = clean;
26502 cleanWordChars : function(input) {// change the chars to hex code
26503 var he = Roo.HtmlEditorCore;
26505 var output = input;
26506 Roo.each(he.swapCodes, function(sw) {
26507 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26509 output = output.replace(swapper, sw[1]);
26516 cleanUpChildren : function (n)
26518 if (!n.childNodes.length) {
26521 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26522 this.cleanUpChild(n.childNodes[i]);
26529 cleanUpChild : function (node)
26532 //console.log(node);
26533 if (node.nodeName == "#text") {
26534 // clean up silly Windows -- stuff?
26537 if (node.nodeName == "#comment") {
26538 if (!this.allowComments) {
26539 node.parentNode.removeChild(node);
26541 // clean up silly Windows -- stuff?
26544 var lcname = node.tagName.toLowerCase();
26545 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26546 // whitelist of tags..
26548 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26550 node.parentNode.removeChild(node);
26555 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26557 // spans with no attributes - just remove them..
26558 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26559 remove_keep_children = true;
26562 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26563 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26565 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26566 // remove_keep_children = true;
26569 if (remove_keep_children) {
26570 this.cleanUpChildren(node);
26571 // inserts everything just before this node...
26572 while (node.childNodes.length) {
26573 var cn = node.childNodes[0];
26574 node.removeChild(cn);
26575 node.parentNode.insertBefore(cn, node);
26577 node.parentNode.removeChild(node);
26581 if (!node.attributes || !node.attributes.length) {
26586 this.cleanUpChildren(node);
26590 function cleanAttr(n,v)
26593 if (v.match(/^\./) || v.match(/^\//)) {
26596 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26599 if (v.match(/^#/)) {
26602 if (v.match(/^\{/)) { // allow template editing.
26605 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26606 node.removeAttribute(n);
26610 var cwhite = this.cwhite;
26611 var cblack = this.cblack;
26613 function cleanStyle(n,v)
26615 if (v.match(/expression/)) { //XSS?? should we even bother..
26616 node.removeAttribute(n);
26620 var parts = v.split(/;/);
26623 Roo.each(parts, function(p) {
26624 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26628 var l = p.split(':').shift().replace(/\s+/g,'');
26629 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26631 if ( cwhite.length && cblack.indexOf(l) > -1) {
26632 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26633 //node.removeAttribute(n);
26637 // only allow 'c whitelisted system attributes'
26638 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26639 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26640 //node.removeAttribute(n);
26650 if (clean.length) {
26651 node.setAttribute(n, clean.join(';'));
26653 node.removeAttribute(n);
26659 for (var i = node.attributes.length-1; i > -1 ; i--) {
26660 var a = node.attributes[i];
26663 if (a.name.toLowerCase().substr(0,2)=='on') {
26664 node.removeAttribute(a.name);
26667 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26668 node.removeAttribute(a.name);
26671 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26672 cleanAttr(a.name,a.value); // fixme..
26675 if (a.name == 'style') {
26676 cleanStyle(a.name,a.value);
26679 /// clean up MS crap..
26680 // tecnically this should be a list of valid class'es..
26683 if (a.name == 'class') {
26684 if (a.value.match(/^Mso/)) {
26685 node.removeAttribute('class');
26688 if (a.value.match(/^body$/)) {
26689 node.removeAttribute('class');
26700 this.cleanUpChildren(node);
26706 * Clean up MS wordisms...
26708 cleanWord : function(node)
26711 this.cleanWord(this.doc.body);
26716 node.nodeName == 'SPAN' &&
26717 !node.hasAttributes() &&
26718 node.childNodes.length == 1 &&
26719 node.firstChild.nodeName == "#text"
26721 var textNode = node.firstChild;
26722 node.removeChild(textNode);
26723 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26724 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26726 node.parentNode.insertBefore(textNode, node);
26727 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26728 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26730 node.parentNode.removeChild(node);
26733 if (node.nodeName == "#text") {
26734 // clean up silly Windows -- stuff?
26737 if (node.nodeName == "#comment") {
26738 node.parentNode.removeChild(node);
26739 // clean up silly Windows -- stuff?
26743 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26744 node.parentNode.removeChild(node);
26747 //Roo.log(node.tagName);
26748 // remove - but keep children..
26749 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26750 //Roo.log('-- removed');
26751 while (node.childNodes.length) {
26752 var cn = node.childNodes[0];
26753 node.removeChild(cn);
26754 node.parentNode.insertBefore(cn, node);
26755 // move node to parent - and clean it..
26756 this.cleanWord(cn);
26758 node.parentNode.removeChild(node);
26759 /// no need to iterate chidlren = it's got none..
26760 //this.iterateChildren(node, this.cleanWord);
26764 if (node.className.length) {
26766 var cn = node.className.split(/\W+/);
26768 Roo.each(cn, function(cls) {
26769 if (cls.match(/Mso[a-zA-Z]+/)) {
26774 node.className = cna.length ? cna.join(' ') : '';
26776 node.removeAttribute("class");
26780 if (node.hasAttribute("lang")) {
26781 node.removeAttribute("lang");
26784 if (node.hasAttribute("style")) {
26786 var styles = node.getAttribute("style").split(";");
26788 Roo.each(styles, function(s) {
26789 if (!s.match(/:/)) {
26792 var kv = s.split(":");
26793 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26796 // what ever is left... we allow.
26799 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26800 if (!nstyle.length) {
26801 node.removeAttribute('style');
26804 this.iterateChildren(node, this.cleanWord);
26810 * iterateChildren of a Node, calling fn each time, using this as the scole..
26811 * @param {DomNode} node node to iterate children of.
26812 * @param {Function} fn method of this class to call on each item.
26814 iterateChildren : function(node, fn)
26816 if (!node.childNodes.length) {
26819 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26820 fn.call(this, node.childNodes[i])
26826 * cleanTableWidths.
26828 * Quite often pasting from word etc.. results in tables with column and widths.
26829 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26832 cleanTableWidths : function(node)
26837 this.cleanTableWidths(this.doc.body);
26842 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26845 Roo.log(node.tagName);
26846 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26847 this.iterateChildren(node, this.cleanTableWidths);
26850 if (node.hasAttribute('width')) {
26851 node.removeAttribute('width');
26855 if (node.hasAttribute("style")) {
26858 var styles = node.getAttribute("style").split(";");
26860 Roo.each(styles, function(s) {
26861 if (!s.match(/:/)) {
26864 var kv = s.split(":");
26865 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26868 // what ever is left... we allow.
26871 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26872 if (!nstyle.length) {
26873 node.removeAttribute('style');
26877 this.iterateChildren(node, this.cleanTableWidths);
26885 domToHTML : function(currentElement, depth, nopadtext) {
26887 depth = depth || 0;
26888 nopadtext = nopadtext || false;
26890 if (!currentElement) {
26891 return this.domToHTML(this.doc.body);
26894 //Roo.log(currentElement);
26896 var allText = false;
26897 var nodeName = currentElement.nodeName;
26898 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26900 if (nodeName == '#text') {
26902 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26907 if (nodeName != 'BODY') {
26910 // Prints the node tagName, such as <A>, <IMG>, etc
26913 for(i = 0; i < currentElement.attributes.length;i++) {
26915 var aname = currentElement.attributes.item(i).name;
26916 if (!currentElement.attributes.item(i).value.length) {
26919 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26922 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26931 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26934 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26939 // Traverse the tree
26941 var currentElementChild = currentElement.childNodes.item(i);
26942 var allText = true;
26943 var innerHTML = '';
26945 while (currentElementChild) {
26946 // Formatting code (indent the tree so it looks nice on the screen)
26947 var nopad = nopadtext;
26948 if (lastnode == 'SPAN') {
26952 if (currentElementChild.nodeName == '#text') {
26953 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26954 toadd = nopadtext ? toadd : toadd.trim();
26955 if (!nopad && toadd.length > 80) {
26956 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26958 innerHTML += toadd;
26961 currentElementChild = currentElement.childNodes.item(i);
26967 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26969 // Recursively traverse the tree structure of the child node
26970 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26971 lastnode = currentElementChild.nodeName;
26973 currentElementChild=currentElement.childNodes.item(i);
26979 // The remaining code is mostly for formatting the tree
26980 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26985 ret+= "</"+tagName+">";
26991 applyBlacklists : function()
26993 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26994 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26998 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26999 if (b.indexOf(tag) > -1) {
27002 this.white.push(tag);
27006 Roo.each(w, function(tag) {
27007 if (b.indexOf(tag) > -1) {
27010 if (this.white.indexOf(tag) > -1) {
27013 this.white.push(tag);
27018 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27019 if (w.indexOf(tag) > -1) {
27022 this.black.push(tag);
27026 Roo.each(b, function(tag) {
27027 if (w.indexOf(tag) > -1) {
27030 if (this.black.indexOf(tag) > -1) {
27033 this.black.push(tag);
27038 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27039 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27043 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27044 if (b.indexOf(tag) > -1) {
27047 this.cwhite.push(tag);
27051 Roo.each(w, function(tag) {
27052 if (b.indexOf(tag) > -1) {
27055 if (this.cwhite.indexOf(tag) > -1) {
27058 this.cwhite.push(tag);
27063 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27064 if (w.indexOf(tag) > -1) {
27067 this.cblack.push(tag);
27071 Roo.each(b, function(tag) {
27072 if (w.indexOf(tag) > -1) {
27075 if (this.cblack.indexOf(tag) > -1) {
27078 this.cblack.push(tag);
27083 setStylesheets : function(stylesheets)
27085 if(typeof(stylesheets) == 'string'){
27086 Roo.get(this.iframe.contentDocument.head).createChild({
27088 rel : 'stylesheet',
27097 Roo.each(stylesheets, function(s) {
27102 Roo.get(_this.iframe.contentDocument.head).createChild({
27104 rel : 'stylesheet',
27113 removeStylesheets : function()
27117 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27122 setStyle : function(style)
27124 Roo.get(this.iframe.contentDocument.head).createChild({
27133 // hide stuff that is not compatible
27147 * @event specialkey
27151 * @cfg {String} fieldClass @hide
27154 * @cfg {String} focusClass @hide
27157 * @cfg {String} autoCreate @hide
27160 * @cfg {String} inputType @hide
27163 * @cfg {String} invalidClass @hide
27166 * @cfg {String} invalidText @hide
27169 * @cfg {String} msgFx @hide
27172 * @cfg {String} validateOnBlur @hide
27176 Roo.HtmlEditorCore.white = [
27177 'area', 'br', 'img', 'input', 'hr', 'wbr',
27179 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27180 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27181 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27182 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27183 'table', 'ul', 'xmp',
27185 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27188 'dir', 'menu', 'ol', 'ul', 'dl',
27194 Roo.HtmlEditorCore.black = [
27195 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27197 'base', 'basefont', 'bgsound', 'blink', 'body',
27198 'frame', 'frameset', 'head', 'html', 'ilayer',
27199 'iframe', 'layer', 'link', 'meta', 'object',
27200 'script', 'style' ,'title', 'xml' // clean later..
27202 Roo.HtmlEditorCore.clean = [
27203 'script', 'style', 'title', 'xml'
27205 Roo.HtmlEditorCore.remove = [
27210 Roo.HtmlEditorCore.ablack = [
27214 Roo.HtmlEditorCore.aclean = [
27215 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27219 Roo.HtmlEditorCore.pwhite= [
27220 'http', 'https', 'mailto'
27223 // white listed style attributes.
27224 Roo.HtmlEditorCore.cwhite= [
27225 // 'text-align', /// default is to allow most things..
27231 // black listed style attributes.
27232 Roo.HtmlEditorCore.cblack= [
27233 // 'font-size' -- this can be set by the project
27237 Roo.HtmlEditorCore.swapCodes =[
27238 [ 8211, "–" ],
27239 [ 8212, "—" ],
27256 * @class Roo.bootstrap.HtmlEditor
27257 * @extends Roo.bootstrap.TextArea
27258 * Bootstrap HtmlEditor class
27261 * Create a new HtmlEditor
27262 * @param {Object} config The config object
27265 Roo.bootstrap.HtmlEditor = function(config){
27266 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27267 if (!this.toolbars) {
27268 this.toolbars = [];
27271 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27274 * @event initialize
27275 * Fires when the editor is fully initialized (including the iframe)
27276 * @param {HtmlEditor} this
27281 * Fires when the editor is first receives the focus. Any insertion must wait
27282 * until after this event.
27283 * @param {HtmlEditor} this
27287 * @event beforesync
27288 * Fires before the textarea is updated with content from the editor iframe. Return false
27289 * to cancel the sync.
27290 * @param {HtmlEditor} this
27291 * @param {String} html
27295 * @event beforepush
27296 * Fires before the iframe editor is updated with content from the textarea. Return false
27297 * to cancel the push.
27298 * @param {HtmlEditor} this
27299 * @param {String} html
27304 * Fires when the textarea is updated with content from the editor iframe.
27305 * @param {HtmlEditor} this
27306 * @param {String} html
27311 * Fires when the iframe editor is updated with content from the textarea.
27312 * @param {HtmlEditor} this
27313 * @param {String} html
27317 * @event editmodechange
27318 * Fires when the editor switches edit modes
27319 * @param {HtmlEditor} this
27320 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27322 editmodechange: true,
27324 * @event editorevent
27325 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27326 * @param {HtmlEditor} this
27330 * @event firstfocus
27331 * Fires when on first focus - needed by toolbars..
27332 * @param {HtmlEditor} this
27337 * Auto save the htmlEditor value as a file into Events
27338 * @param {HtmlEditor} this
27342 * @event savedpreview
27343 * preview the saved version of htmlEditor
27344 * @param {HtmlEditor} this
27351 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27355 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27360 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27365 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27370 * @cfg {Number} height (in pixels)
27374 * @cfg {Number} width (in pixels)
27379 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27382 stylesheets: false,
27387 // private properties
27388 validationEvent : false,
27390 initialized : false,
27393 onFocus : Roo.emptyFn,
27395 hideMode:'offsets',
27397 tbContainer : false,
27401 toolbarContainer :function() {
27402 return this.wrap.select('.x-html-editor-tb',true).first();
27406 * Protected method that will not generally be called directly. It
27407 * is called when the editor creates its toolbar. Override this method if you need to
27408 * add custom toolbar buttons.
27409 * @param {HtmlEditor} editor
27411 createToolbar : function(){
27412 Roo.log('renewing');
27413 Roo.log("create toolbars");
27415 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27416 this.toolbars[0].render(this.toolbarContainer());
27420 // if (!editor.toolbars || !editor.toolbars.length) {
27421 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27424 // for (var i =0 ; i < editor.toolbars.length;i++) {
27425 // editor.toolbars[i] = Roo.factory(
27426 // typeof(editor.toolbars[i]) == 'string' ?
27427 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27428 // Roo.bootstrap.HtmlEditor);
27429 // editor.toolbars[i].init(editor);
27435 onRender : function(ct, position)
27437 // Roo.log("Call onRender: " + this.xtype);
27439 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27441 this.wrap = this.inputEl().wrap({
27442 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27445 this.editorcore.onRender(ct, position);
27447 if (this.resizable) {
27448 this.resizeEl = new Roo.Resizable(this.wrap, {
27452 minHeight : this.height,
27453 height: this.height,
27454 handles : this.resizable,
27457 resize : function(r, w, h) {
27458 _t.onResize(w,h); // -something
27464 this.createToolbar(this);
27467 if(!this.width && this.resizable){
27468 this.setSize(this.wrap.getSize());
27470 if (this.resizeEl) {
27471 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27472 // should trigger onReize..
27478 onResize : function(w, h)
27480 Roo.log('resize: ' +w + ',' + h );
27481 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27485 if(this.inputEl() ){
27486 if(typeof w == 'number'){
27487 var aw = w - this.wrap.getFrameWidth('lr');
27488 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27491 if(typeof h == 'number'){
27492 var tbh = -11; // fixme it needs to tool bar size!
27493 for (var i =0; i < this.toolbars.length;i++) {
27494 // fixme - ask toolbars for heights?
27495 tbh += this.toolbars[i].el.getHeight();
27496 //if (this.toolbars[i].footer) {
27497 // tbh += this.toolbars[i].footer.el.getHeight();
27505 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27506 ah -= 5; // knock a few pixes off for look..
27507 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27511 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27512 this.editorcore.onResize(ew,eh);
27517 * Toggles the editor between standard and source edit mode.
27518 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27520 toggleSourceEdit : function(sourceEditMode)
27522 this.editorcore.toggleSourceEdit(sourceEditMode);
27524 if(this.editorcore.sourceEditMode){
27525 Roo.log('editor - showing textarea');
27528 // Roo.log(this.syncValue());
27530 this.inputEl().removeClass(['hide', 'x-hidden']);
27531 this.inputEl().dom.removeAttribute('tabIndex');
27532 this.inputEl().focus();
27534 Roo.log('editor - hiding textarea');
27536 // Roo.log(this.pushValue());
27539 this.inputEl().addClass(['hide', 'x-hidden']);
27540 this.inputEl().dom.setAttribute('tabIndex', -1);
27541 //this.deferFocus();
27544 if(this.resizable){
27545 this.setSize(this.wrap.getSize());
27548 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27551 // private (for BoxComponent)
27552 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27554 // private (for BoxComponent)
27555 getResizeEl : function(){
27559 // private (for BoxComponent)
27560 getPositionEl : function(){
27565 initEvents : function(){
27566 this.originalValue = this.getValue();
27570 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27573 // markInvalid : Roo.emptyFn,
27575 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27578 // clearInvalid : Roo.emptyFn,
27580 setValue : function(v){
27581 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27582 this.editorcore.pushValue();
27587 deferFocus : function(){
27588 this.focus.defer(10, this);
27592 focus : function(){
27593 this.editorcore.focus();
27599 onDestroy : function(){
27605 for (var i =0; i < this.toolbars.length;i++) {
27606 // fixme - ask toolbars for heights?
27607 this.toolbars[i].onDestroy();
27610 this.wrap.dom.innerHTML = '';
27611 this.wrap.remove();
27616 onFirstFocus : function(){
27617 //Roo.log("onFirstFocus");
27618 this.editorcore.onFirstFocus();
27619 for (var i =0; i < this.toolbars.length;i++) {
27620 this.toolbars[i].onFirstFocus();
27626 syncValue : function()
27628 this.editorcore.syncValue();
27631 pushValue : function()
27633 this.editorcore.pushValue();
27637 // hide stuff that is not compatible
27651 * @event specialkey
27655 * @cfg {String} fieldClass @hide
27658 * @cfg {String} focusClass @hide
27661 * @cfg {String} autoCreate @hide
27664 * @cfg {String} inputType @hide
27668 * @cfg {String} invalidText @hide
27671 * @cfg {String} msgFx @hide
27674 * @cfg {String} validateOnBlur @hide
27683 Roo.namespace('Roo.bootstrap.htmleditor');
27685 * @class Roo.bootstrap.HtmlEditorToolbar1
27691 new Roo.bootstrap.HtmlEditor({
27694 new Roo.bootstrap.HtmlEditorToolbar1({
27695 disable : { fonts: 1 , format: 1, ..., ... , ...],
27701 * @cfg {Object} disable List of elements to disable..
27702 * @cfg {Array} btns List of additional buttons.
27706 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27709 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27712 Roo.apply(this, config);
27714 // default disabled, based on 'good practice'..
27715 this.disable = this.disable || {};
27716 Roo.applyIf(this.disable, {
27719 specialElements : true
27721 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27723 this.editor = config.editor;
27724 this.editorcore = config.editor.editorcore;
27726 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27728 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27729 // dont call parent... till later.
27731 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27736 editorcore : false,
27741 "h1","h2","h3","h4","h5","h6",
27743 "abbr", "acronym", "address", "cite", "samp", "var",
27747 onRender : function(ct, position)
27749 // Roo.log("Call onRender: " + this.xtype);
27751 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27753 this.el.dom.style.marginBottom = '0';
27755 var editorcore = this.editorcore;
27756 var editor= this.editor;
27759 var btn = function(id,cmd , toggle, handler, html){
27761 var event = toggle ? 'toggle' : 'click';
27766 xns: Roo.bootstrap,
27770 enableToggle:toggle !== false,
27772 pressed : toggle ? false : null,
27775 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27776 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27782 // var cb_box = function...
27787 xns: Roo.bootstrap,
27792 xns: Roo.bootstrap,
27796 Roo.each(this.formats, function(f) {
27797 style.menu.items.push({
27799 xns: Roo.bootstrap,
27800 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27805 editorcore.insertTag(this.tagname);
27812 children.push(style);
27814 btn('bold',false,true);
27815 btn('italic',false,true);
27816 btn('align-left', 'justifyleft',true);
27817 btn('align-center', 'justifycenter',true);
27818 btn('align-right' , 'justifyright',true);
27819 btn('link', false, false, function(btn) {
27820 //Roo.log("create link?");
27821 var url = prompt(this.createLinkText, this.defaultLinkValue);
27822 if(url && url != 'http:/'+'/'){
27823 this.editorcore.relayCmd('createlink', url);
27826 btn('list','insertunorderedlist',true);
27827 btn('pencil', false,true, function(btn){
27829 this.toggleSourceEdit(btn.pressed);
27832 if (this.editor.btns.length > 0) {
27833 for (var i = 0; i<this.editor.btns.length; i++) {
27834 children.push(this.editor.btns[i]);
27842 xns: Roo.bootstrap,
27847 xns: Roo.bootstrap,
27852 cog.menu.items.push({
27854 xns: Roo.bootstrap,
27855 html : Clean styles,
27860 editorcore.insertTag(this.tagname);
27869 this.xtype = 'NavSimplebar';
27871 for(var i=0;i< children.length;i++) {
27873 this.buttons.add(this.addxtypeChild(children[i]));
27877 editor.on('editorevent', this.updateToolbar, this);
27879 onBtnClick : function(id)
27881 this.editorcore.relayCmd(id);
27882 this.editorcore.focus();
27886 * Protected method that will not generally be called directly. It triggers
27887 * a toolbar update by reading the markup state of the current selection in the editor.
27889 updateToolbar: function(){
27891 if(!this.editorcore.activated){
27892 this.editor.onFirstFocus(); // is this neeed?
27896 var btns = this.buttons;
27897 var doc = this.editorcore.doc;
27898 btns.get('bold').setActive(doc.queryCommandState('bold'));
27899 btns.get('italic').setActive(doc.queryCommandState('italic'));
27900 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27902 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27903 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27904 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27906 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27907 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27910 var ans = this.editorcore.getAllAncestors();
27911 if (this.formatCombo) {
27914 var store = this.formatCombo.store;
27915 this.formatCombo.setValue("");
27916 for (var i =0; i < ans.length;i++) {
27917 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27919 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27927 // hides menus... - so this cant be on a menu...
27928 Roo.bootstrap.MenuMgr.hideAll();
27930 Roo.bootstrap.MenuMgr.hideAll();
27931 //this.editorsyncValue();
27933 onFirstFocus: function() {
27934 this.buttons.each(function(item){
27938 toggleSourceEdit : function(sourceEditMode){
27941 if(sourceEditMode){
27942 Roo.log("disabling buttons");
27943 this.buttons.each( function(item){
27944 if(item.cmd != 'pencil'){
27950 Roo.log("enabling buttons");
27951 if(this.editorcore.initialized){
27952 this.buttons.each( function(item){
27958 Roo.log("calling toggole on editor");
27959 // tell the editor that it's been pressed..
27960 this.editor.toggleSourceEdit(sourceEditMode);
27974 * @class Roo.bootstrap.Markdown
27975 * @extends Roo.bootstrap.TextArea
27976 * Bootstrap Showdown editable area
27977 * @cfg {string} content
27980 * Create a new Showdown
27983 Roo.bootstrap.Markdown = function(config){
27984 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27988 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27992 initEvents : function()
27995 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27996 this.markdownEl = this.el.createChild({
27997 cls : 'roo-markdown-area'
27999 this.inputEl().addClass('d-none');
28000 if (this.getValue() == '') {
28001 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28004 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28006 this.markdownEl.on('click', this.toggleTextEdit, this);
28007 this.on('blur', this.toggleTextEdit, this);
28008 this.on('specialkey', this.resizeTextArea, this);
28011 toggleTextEdit : function()
28013 var sh = this.markdownEl.getHeight();
28014 this.inputEl().addClass('d-none');
28015 this.markdownEl.addClass('d-none');
28016 if (!this.editing) {
28018 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28019 this.inputEl().removeClass('d-none');
28020 this.inputEl().focus();
28021 this.editing = true;
28024 // show showdown...
28025 this.updateMarkdown();
28026 this.markdownEl.removeClass('d-none');
28027 this.editing = false;
28030 updateMarkdown : function()
28032 if (this.getValue() == '') {
28033 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28037 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28040 resizeTextArea: function () {
28043 Roo.log([sh, this.getValue().split("\n").length * 30]);
28044 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28046 setValue : function(val)
28048 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28049 if (!this.editing) {
28050 this.updateMarkdown();
28056 if (!this.editing) {
28057 this.toggleTextEdit();
28065 * Ext JS Library 1.1.1
28066 * Copyright(c) 2006-2007, Ext JS, LLC.
28068 * Originally Released Under LGPL - original licence link has changed is not relivant.
28071 * <script type="text/javascript">
28075 * @class Roo.bootstrap.PagingToolbar
28076 * @extends Roo.bootstrap.NavSimplebar
28077 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28079 * Create a new PagingToolbar
28080 * @param {Object} config The config object
28081 * @param {Roo.data.Store} store
28083 Roo.bootstrap.PagingToolbar = function(config)
28085 // old args format still supported... - xtype is prefered..
28086 // created from xtype...
28088 this.ds = config.dataSource;
28090 if (config.store && !this.ds) {
28091 this.store= Roo.factory(config.store, Roo.data);
28092 this.ds = this.store;
28093 this.ds.xmodule = this.xmodule || false;
28096 this.toolbarItems = [];
28097 if (config.items) {
28098 this.toolbarItems = config.items;
28101 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28106 this.bind(this.ds);
28109 if (Roo.bootstrap.version == 4) {
28110 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28112 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28117 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28119 * @cfg {Roo.data.Store} dataSource
28120 * The underlying data store providing the paged data
28123 * @cfg {String/HTMLElement/Element} container
28124 * container The id or element that will contain the toolbar
28127 * @cfg {Boolean} displayInfo
28128 * True to display the displayMsg (defaults to false)
28131 * @cfg {Number} pageSize
28132 * The number of records to display per page (defaults to 20)
28136 * @cfg {String} displayMsg
28137 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28139 displayMsg : 'Displaying {0} - {1} of {2}',
28141 * @cfg {String} emptyMsg
28142 * The message to display when no records are found (defaults to "No data to display")
28144 emptyMsg : 'No data to display',
28146 * Customizable piece of the default paging text (defaults to "Page")
28149 beforePageText : "Page",
28151 * Customizable piece of the default paging text (defaults to "of %0")
28154 afterPageText : "of {0}",
28156 * Customizable piece of the default paging text (defaults to "First Page")
28159 firstText : "First Page",
28161 * Customizable piece of the default paging text (defaults to "Previous Page")
28164 prevText : "Previous Page",
28166 * Customizable piece of the default paging text (defaults to "Next Page")
28169 nextText : "Next Page",
28171 * Customizable piece of the default paging text (defaults to "Last Page")
28174 lastText : "Last Page",
28176 * Customizable piece of the default paging text (defaults to "Refresh")
28179 refreshText : "Refresh",
28183 onRender : function(ct, position)
28185 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28186 this.navgroup.parentId = this.id;
28187 this.navgroup.onRender(this.el, null);
28188 // add the buttons to the navgroup
28190 if(this.displayInfo){
28191 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28192 this.displayEl = this.el.select('.x-paging-info', true).first();
28193 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28194 // this.displayEl = navel.el.select('span',true).first();
28200 Roo.each(_this.buttons, function(e){ // this might need to use render????
28201 Roo.factory(e).render(_this.el);
28205 Roo.each(_this.toolbarItems, function(e) {
28206 _this.navgroup.addItem(e);
28210 this.first = this.navgroup.addItem({
28211 tooltip: this.firstText,
28212 cls: "prev btn-outline-secondary",
28213 html : ' <i class="fa fa-step-backward"></i>',
28215 preventDefault: true,
28216 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28219 this.prev = this.navgroup.addItem({
28220 tooltip: this.prevText,
28221 cls: "prev btn-outline-secondary",
28222 html : ' <i class="fa fa-backward"></i>',
28224 preventDefault: true,
28225 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28227 //this.addSeparator();
28230 var field = this.navgroup.addItem( {
28232 cls : 'x-paging-position btn-outline-secondary',
28234 html : this.beforePageText +
28235 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28236 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28239 this.field = field.el.select('input', true).first();
28240 this.field.on("keydown", this.onPagingKeydown, this);
28241 this.field.on("focus", function(){this.dom.select();});
28244 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28245 //this.field.setHeight(18);
28246 //this.addSeparator();
28247 this.next = this.navgroup.addItem({
28248 tooltip: this.nextText,
28249 cls: "next btn-outline-secondary",
28250 html : ' <i class="fa fa-forward"></i>',
28252 preventDefault: true,
28253 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28255 this.last = this.navgroup.addItem({
28256 tooltip: this.lastText,
28257 html : ' <i class="fa fa-step-forward"></i>',
28258 cls: "next btn-outline-secondary",
28260 preventDefault: true,
28261 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28263 //this.addSeparator();
28264 this.loading = this.navgroup.addItem({
28265 tooltip: this.refreshText,
28266 cls: "btn-outline-secondary",
28267 html : ' <i class="fa fa-refresh"></i>',
28268 preventDefault: true,
28269 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28275 updateInfo : function(){
28276 if(this.displayEl){
28277 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28278 var msg = count == 0 ?
28282 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28284 this.displayEl.update(msg);
28289 onLoad : function(ds, r, o)
28291 this.cursor = o.params && o.params.start ? o.params.start : 0;
28293 var d = this.getPageData(),
28298 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28299 this.field.dom.value = ap;
28300 this.first.setDisabled(ap == 1);
28301 this.prev.setDisabled(ap == 1);
28302 this.next.setDisabled(ap == ps);
28303 this.last.setDisabled(ap == ps);
28304 this.loading.enable();
28309 getPageData : function(){
28310 var total = this.ds.getTotalCount();
28313 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28314 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28319 onLoadError : function(){
28320 this.loading.enable();
28324 onPagingKeydown : function(e){
28325 var k = e.getKey();
28326 var d = this.getPageData();
28328 var v = this.field.dom.value, pageNum;
28329 if(!v || isNaN(pageNum = parseInt(v, 10))){
28330 this.field.dom.value = d.activePage;
28333 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28334 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28337 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))
28339 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28340 this.field.dom.value = pageNum;
28341 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28344 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28346 var v = this.field.dom.value, pageNum;
28347 var increment = (e.shiftKey) ? 10 : 1;
28348 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28351 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28352 this.field.dom.value = d.activePage;
28355 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28357 this.field.dom.value = parseInt(v, 10) + increment;
28358 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28359 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28366 beforeLoad : function(){
28368 this.loading.disable();
28373 onClick : function(which){
28382 ds.load({params:{start: 0, limit: this.pageSize}});
28385 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28388 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28391 var total = ds.getTotalCount();
28392 var extra = total % this.pageSize;
28393 var lastStart = extra ? (total - extra) : total-this.pageSize;
28394 ds.load({params:{start: lastStart, limit: this.pageSize}});
28397 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28403 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28404 * @param {Roo.data.Store} store The data store to unbind
28406 unbind : function(ds){
28407 ds.un("beforeload", this.beforeLoad, this);
28408 ds.un("load", this.onLoad, this);
28409 ds.un("loadexception", this.onLoadError, this);
28410 ds.un("remove", this.updateInfo, this);
28411 ds.un("add", this.updateInfo, this);
28412 this.ds = undefined;
28416 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28417 * @param {Roo.data.Store} store The data store to bind
28419 bind : function(ds){
28420 ds.on("beforeload", this.beforeLoad, this);
28421 ds.on("load", this.onLoad, this);
28422 ds.on("loadexception", this.onLoadError, this);
28423 ds.on("remove", this.updateInfo, this);
28424 ds.on("add", this.updateInfo, this);
28435 * @class Roo.bootstrap.MessageBar
28436 * @extends Roo.bootstrap.Component
28437 * Bootstrap MessageBar class
28438 * @cfg {String} html contents of the MessageBar
28439 * @cfg {String} weight (info | success | warning | danger) default info
28440 * @cfg {String} beforeClass insert the bar before the given class
28441 * @cfg {Boolean} closable (true | false) default false
28442 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28445 * Create a new Element
28446 * @param {Object} config The config object
28449 Roo.bootstrap.MessageBar = function(config){
28450 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28453 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28459 beforeClass: 'bootstrap-sticky-wrap',
28461 getAutoCreate : function(){
28465 cls: 'alert alert-dismissable alert-' + this.weight,
28470 html: this.html || ''
28476 cfg.cls += ' alert-messages-fixed';
28490 onRender : function(ct, position)
28492 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28495 var cfg = Roo.apply({}, this.getAutoCreate());
28499 cfg.cls += ' ' + this.cls;
28502 cfg.style = this.style;
28504 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28506 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28509 this.el.select('>button.close').on('click', this.hide, this);
28515 if (!this.rendered) {
28521 this.fireEvent('show', this);
28527 if (!this.rendered) {
28533 this.fireEvent('hide', this);
28536 update : function()
28538 // var e = this.el.dom.firstChild;
28540 // if(this.closable){
28541 // e = e.nextSibling;
28544 // e.data = this.html || '';
28546 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28562 * @class Roo.bootstrap.Graph
28563 * @extends Roo.bootstrap.Component
28564 * Bootstrap Graph class
28568 @cfg {String} graphtype bar | vbar | pie
28569 @cfg {number} g_x coodinator | centre x (pie)
28570 @cfg {number} g_y coodinator | centre y (pie)
28571 @cfg {number} g_r radius (pie)
28572 @cfg {number} g_height height of the chart (respected by all elements in the set)
28573 @cfg {number} g_width width of the chart (respected by all elements in the set)
28574 @cfg {Object} title The title of the chart
28577 -opts (object) options for the chart
28579 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28580 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28582 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.
28583 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28585 o stretch (boolean)
28587 -opts (object) options for the pie
28590 o startAngle (number)
28591 o endAngle (number)
28595 * Create a new Input
28596 * @param {Object} config The config object
28599 Roo.bootstrap.Graph = function(config){
28600 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28606 * The img click event for the img.
28607 * @param {Roo.EventObject} e
28613 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28624 //g_colors: this.colors,
28631 getAutoCreate : function(){
28642 onRender : function(ct,position){
28645 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28647 if (typeof(Raphael) == 'undefined') {
28648 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28652 this.raphael = Raphael(this.el.dom);
28654 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28655 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28656 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28657 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28659 r.text(160, 10, "Single Series Chart").attr(txtattr);
28660 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28661 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28662 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28664 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28665 r.barchart(330, 10, 300, 220, data1);
28666 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28667 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28670 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28671 // r.barchart(30, 30, 560, 250, xdata, {
28672 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28673 // axis : "0 0 1 1",
28674 // axisxlabels : xdata
28675 // //yvalues : cols,
28678 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28680 // this.load(null,xdata,{
28681 // axis : "0 0 1 1",
28682 // axisxlabels : xdata
28687 load : function(graphtype,xdata,opts)
28689 this.raphael.clear();
28691 graphtype = this.graphtype;
28696 var r = this.raphael,
28697 fin = function () {
28698 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28700 fout = function () {
28701 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28703 pfin = function() {
28704 this.sector.stop();
28705 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28708 this.label[0].stop();
28709 this.label[0].attr({ r: 7.5 });
28710 this.label[1].attr({ "font-weight": 800 });
28713 pfout = function() {
28714 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28717 this.label[0].animate({ r: 5 }, 500, "bounce");
28718 this.label[1].attr({ "font-weight": 400 });
28724 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28727 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28730 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28731 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28733 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28740 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28745 setTitle: function(o)
28750 initEvents: function() {
28753 this.el.on('click', this.onClick, this);
28757 onClick : function(e)
28759 Roo.log('img onclick');
28760 this.fireEvent('click', this, e);
28772 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28775 * @class Roo.bootstrap.dash.NumberBox
28776 * @extends Roo.bootstrap.Component
28777 * Bootstrap NumberBox class
28778 * @cfg {String} headline Box headline
28779 * @cfg {String} content Box content
28780 * @cfg {String} icon Box icon
28781 * @cfg {String} footer Footer text
28782 * @cfg {String} fhref Footer href
28785 * Create a new NumberBox
28786 * @param {Object} config The config object
28790 Roo.bootstrap.dash.NumberBox = function(config){
28791 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28795 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28804 getAutoCreate : function(){
28808 cls : 'small-box ',
28816 cls : 'roo-headline',
28817 html : this.headline
28821 cls : 'roo-content',
28822 html : this.content
28836 cls : 'ion ' + this.icon
28845 cls : 'small-box-footer',
28846 href : this.fhref || '#',
28850 cfg.cn.push(footer);
28857 onRender : function(ct,position){
28858 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28865 setHeadline: function (value)
28867 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28870 setFooter: function (value, href)
28872 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28875 this.el.select('a.small-box-footer',true).first().attr('href', href);
28880 setContent: function (value)
28882 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28885 initEvents: function()
28899 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28902 * @class Roo.bootstrap.dash.TabBox
28903 * @extends Roo.bootstrap.Component
28904 * Bootstrap TabBox class
28905 * @cfg {String} title Title of the TabBox
28906 * @cfg {String} icon Icon of the TabBox
28907 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28908 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28911 * Create a new TabBox
28912 * @param {Object} config The config object
28916 Roo.bootstrap.dash.TabBox = function(config){
28917 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28922 * When a pane is added
28923 * @param {Roo.bootstrap.dash.TabPane} pane
28927 * @event activatepane
28928 * When a pane is activated
28929 * @param {Roo.bootstrap.dash.TabPane} pane
28931 "activatepane" : true
28939 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28944 tabScrollable : false,
28946 getChildContainer : function()
28948 return this.el.select('.tab-content', true).first();
28951 getAutoCreate : function(){
28955 cls: 'pull-left header',
28963 cls: 'fa ' + this.icon
28969 cls: 'nav nav-tabs pull-right',
28975 if(this.tabScrollable){
28982 cls: 'nav nav-tabs pull-right',
28993 cls: 'nav-tabs-custom',
28998 cls: 'tab-content no-padding',
29006 initEvents : function()
29008 //Roo.log('add add pane handler');
29009 this.on('addpane', this.onAddPane, this);
29012 * Updates the box title
29013 * @param {String} html to set the title to.
29015 setTitle : function(value)
29017 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29019 onAddPane : function(pane)
29021 this.panes.push(pane);
29022 //Roo.log('addpane');
29024 // tabs are rendere left to right..
29025 if(!this.showtabs){
29029 var ctr = this.el.select('.nav-tabs', true).first();
29032 var existing = ctr.select('.nav-tab',true);
29033 var qty = existing.getCount();;
29036 var tab = ctr.createChild({
29038 cls : 'nav-tab' + (qty ? '' : ' active'),
29046 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29049 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29051 pane.el.addClass('active');
29056 onTabClick : function(ev,un,ob,pane)
29058 //Roo.log('tab - prev default');
29059 ev.preventDefault();
29062 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29063 pane.tab.addClass('active');
29064 //Roo.log(pane.title);
29065 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29066 // technically we should have a deactivate event.. but maybe add later.
29067 // and it should not de-activate the selected tab...
29068 this.fireEvent('activatepane', pane);
29069 pane.el.addClass('active');
29070 pane.fireEvent('activate');
29075 getActivePane : function()
29078 Roo.each(this.panes, function(p) {
29079 if(p.el.hasClass('active')){
29100 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29102 * @class Roo.bootstrap.TabPane
29103 * @extends Roo.bootstrap.Component
29104 * Bootstrap TabPane class
29105 * @cfg {Boolean} active (false | true) Default false
29106 * @cfg {String} title title of panel
29110 * Create a new TabPane
29111 * @param {Object} config The config object
29114 Roo.bootstrap.dash.TabPane = function(config){
29115 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29121 * When a pane is activated
29122 * @param {Roo.bootstrap.dash.TabPane} pane
29129 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29134 // the tabBox that this is attached to.
29137 getAutoCreate : function()
29145 cfg.cls += ' active';
29150 initEvents : function()
29152 //Roo.log('trigger add pane handler');
29153 this.parent().fireEvent('addpane', this)
29157 * Updates the tab title
29158 * @param {String} html to set the title to.
29160 setTitle: function(str)
29166 this.tab.select('a', true).first().dom.innerHTML = str;
29183 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29186 * @class Roo.bootstrap.menu.Menu
29187 * @extends Roo.bootstrap.Component
29188 * Bootstrap Menu class - container for Menu
29189 * @cfg {String} html Text of the menu
29190 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29191 * @cfg {String} icon Font awesome icon
29192 * @cfg {String} pos Menu align to (top | bottom) default bottom
29196 * Create a new Menu
29197 * @param {Object} config The config object
29201 Roo.bootstrap.menu.Menu = function(config){
29202 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29206 * @event beforeshow
29207 * Fires before this menu is displayed
29208 * @param {Roo.bootstrap.menu.Menu} this
29212 * @event beforehide
29213 * Fires before this menu is hidden
29214 * @param {Roo.bootstrap.menu.Menu} this
29219 * Fires after this menu is displayed
29220 * @param {Roo.bootstrap.menu.Menu} this
29225 * Fires after this menu is hidden
29226 * @param {Roo.bootstrap.menu.Menu} this
29231 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29232 * @param {Roo.bootstrap.menu.Menu} this
29233 * @param {Roo.EventObject} e
29240 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29244 weight : 'default',
29249 getChildContainer : function() {
29250 if(this.isSubMenu){
29254 return this.el.select('ul.dropdown-menu', true).first();
29257 getAutoCreate : function()
29262 cls : 'roo-menu-text',
29270 cls : 'fa ' + this.icon
29281 cls : 'dropdown-button btn btn-' + this.weight,
29286 cls : 'dropdown-toggle btn btn-' + this.weight,
29296 cls : 'dropdown-menu'
29302 if(this.pos == 'top'){
29303 cfg.cls += ' dropup';
29306 if(this.isSubMenu){
29309 cls : 'dropdown-menu'
29316 onRender : function(ct, position)
29318 this.isSubMenu = ct.hasClass('dropdown-submenu');
29320 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29323 initEvents : function()
29325 if(this.isSubMenu){
29329 this.hidden = true;
29331 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29332 this.triggerEl.on('click', this.onTriggerPress, this);
29334 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29335 this.buttonEl.on('click', this.onClick, this);
29341 if(this.isSubMenu){
29345 return this.el.select('ul.dropdown-menu', true).first();
29348 onClick : function(e)
29350 this.fireEvent("click", this, e);
29353 onTriggerPress : function(e)
29355 if (this.isVisible()) {
29362 isVisible : function(){
29363 return !this.hidden;
29368 this.fireEvent("beforeshow", this);
29370 this.hidden = false;
29371 this.el.addClass('open');
29373 Roo.get(document).on("mouseup", this.onMouseUp, this);
29375 this.fireEvent("show", this);
29382 this.fireEvent("beforehide", this);
29384 this.hidden = true;
29385 this.el.removeClass('open');
29387 Roo.get(document).un("mouseup", this.onMouseUp);
29389 this.fireEvent("hide", this);
29392 onMouseUp : function()
29406 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29409 * @class Roo.bootstrap.menu.Item
29410 * @extends Roo.bootstrap.Component
29411 * Bootstrap MenuItem class
29412 * @cfg {Boolean} submenu (true | false) default false
29413 * @cfg {String} html text of the item
29414 * @cfg {String} href the link
29415 * @cfg {Boolean} disable (true | false) default false
29416 * @cfg {Boolean} preventDefault (true | false) default true
29417 * @cfg {String} icon Font awesome icon
29418 * @cfg {String} pos Submenu align to (left | right) default right
29422 * Create a new Item
29423 * @param {Object} config The config object
29427 Roo.bootstrap.menu.Item = function(config){
29428 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29432 * Fires when the mouse is hovering over this menu
29433 * @param {Roo.bootstrap.menu.Item} this
29434 * @param {Roo.EventObject} e
29439 * Fires when the mouse exits this menu
29440 * @param {Roo.bootstrap.menu.Item} this
29441 * @param {Roo.EventObject} e
29447 * The raw click event for the entire grid.
29448 * @param {Roo.EventObject} e
29454 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29459 preventDefault: true,
29464 getAutoCreate : function()
29469 cls : 'roo-menu-item-text',
29477 cls : 'fa ' + this.icon
29486 href : this.href || '#',
29493 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29497 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29499 if(this.pos == 'left'){
29500 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29507 initEvents : function()
29509 this.el.on('mouseover', this.onMouseOver, this);
29510 this.el.on('mouseout', this.onMouseOut, this);
29512 this.el.select('a', true).first().on('click', this.onClick, this);
29516 onClick : function(e)
29518 if(this.preventDefault){
29519 e.preventDefault();
29522 this.fireEvent("click", this, e);
29525 onMouseOver : function(e)
29527 if(this.submenu && this.pos == 'left'){
29528 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29531 this.fireEvent("mouseover", this, e);
29534 onMouseOut : function(e)
29536 this.fireEvent("mouseout", this, e);
29548 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29551 * @class Roo.bootstrap.menu.Separator
29552 * @extends Roo.bootstrap.Component
29553 * Bootstrap Separator class
29556 * Create a new Separator
29557 * @param {Object} config The config object
29561 Roo.bootstrap.menu.Separator = function(config){
29562 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29565 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29567 getAutoCreate : function(){
29570 cls: 'dropdown-divider divider'
29588 * @class Roo.bootstrap.Tooltip
29589 * Bootstrap Tooltip class
29590 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29591 * to determine which dom element triggers the tooltip.
29593 * It needs to add support for additional attributes like tooltip-position
29596 * Create a new Toolti
29597 * @param {Object} config The config object
29600 Roo.bootstrap.Tooltip = function(config){
29601 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29603 this.alignment = Roo.bootstrap.Tooltip.alignment;
29605 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29606 this.alignment = config.alignment;
29611 Roo.apply(Roo.bootstrap.Tooltip, {
29613 * @function init initialize tooltip monitoring.
29617 currentTip : false,
29618 currentRegion : false,
29624 Roo.get(document).on('mouseover', this.enter ,this);
29625 Roo.get(document).on('mouseout', this.leave, this);
29628 this.currentTip = new Roo.bootstrap.Tooltip();
29631 enter : function(ev)
29633 var dom = ev.getTarget();
29635 //Roo.log(['enter',dom]);
29636 var el = Roo.fly(dom);
29637 if (this.currentEl) {
29639 //Roo.log(this.currentEl);
29640 //Roo.log(this.currentEl.contains(dom));
29641 if (this.currentEl == el) {
29644 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29650 if (this.currentTip.el) {
29651 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29655 if(!el || el.dom == document){
29661 if (!el.attr('tooltip')) {
29662 pel = el.findParent("[tooltip]");
29664 bindEl = Roo.get(pel);
29670 // you can not look for children, as if el is the body.. then everythign is the child..
29671 if (!pel && !el.attr('tooltip')) { //
29672 if (!el.select("[tooltip]").elements.length) {
29675 // is the mouse over this child...?
29676 bindEl = el.select("[tooltip]").first();
29677 var xy = ev.getXY();
29678 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29679 //Roo.log("not in region.");
29682 //Roo.log("child element over..");
29685 this.currentEl = el;
29686 this.currentTip.bind(bindEl);
29687 this.currentRegion = Roo.lib.Region.getRegion(dom);
29688 this.currentTip.enter();
29691 leave : function(ev)
29693 var dom = ev.getTarget();
29694 //Roo.log(['leave',dom]);
29695 if (!this.currentEl) {
29700 if (dom != this.currentEl.dom) {
29703 var xy = ev.getXY();
29704 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29707 // only activate leave if mouse cursor is outside... bounding box..
29712 if (this.currentTip) {
29713 this.currentTip.leave();
29715 //Roo.log('clear currentEl');
29716 this.currentEl = false;
29721 'left' : ['r-l', [-2,0], 'right'],
29722 'right' : ['l-r', [2,0], 'left'],
29723 'bottom' : ['t-b', [0,2], 'top'],
29724 'top' : [ 'b-t', [0,-2], 'bottom']
29730 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29735 delay : null, // can be { show : 300 , hide: 500}
29739 hoverState : null, //???
29741 placement : 'bottom',
29745 getAutoCreate : function(){
29752 cls : 'tooltip-arrow arrow'
29755 cls : 'tooltip-inner'
29762 bind : function(el)
29767 initEvents : function()
29769 this.arrowEl = this.el.select('.arrow', true).first();
29770 this.innerEl = this.el.select('.tooltip-inner', true).first();
29773 enter : function () {
29775 if (this.timeout != null) {
29776 clearTimeout(this.timeout);
29779 this.hoverState = 'in';
29780 //Roo.log("enter - show");
29781 if (!this.delay || !this.delay.show) {
29786 this.timeout = setTimeout(function () {
29787 if (_t.hoverState == 'in') {
29790 }, this.delay.show);
29794 clearTimeout(this.timeout);
29796 this.hoverState = 'out';
29797 if (!this.delay || !this.delay.hide) {
29803 this.timeout = setTimeout(function () {
29804 //Roo.log("leave - timeout");
29806 if (_t.hoverState == 'out') {
29808 Roo.bootstrap.Tooltip.currentEl = false;
29813 show : function (msg)
29816 this.render(document.body);
29819 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29821 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29823 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29825 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29826 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29828 var placement = typeof this.placement == 'function' ?
29829 this.placement.call(this, this.el, on_el) :
29832 var autoToken = /\s?auto?\s?/i;
29833 var autoPlace = autoToken.test(placement);
29835 placement = placement.replace(autoToken, '') || 'top';
29839 //this.el.setXY([0,0]);
29841 //this.el.dom.style.display='block';
29843 //this.el.appendTo(on_el);
29845 var p = this.getPosition();
29846 var box = this.el.getBox();
29852 var align = this.alignment[placement];
29854 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29856 if(placement == 'top' || placement == 'bottom'){
29858 placement = 'right';
29861 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29862 placement = 'left';
29865 var scroll = Roo.select('body', true).first().getScroll();
29867 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29871 align = this.alignment[placement];
29873 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29877 var elems = document.getElementsByTagName('div');
29878 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29879 for (var i = 0; i < elems.length; i++) {
29880 var zindex = Number.parseInt(
29881 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29884 if (zindex > highest) {
29891 this.el.dom.style.zIndex = highest;
29893 this.el.alignTo(this.bindEl, align[0],align[1]);
29894 //var arrow = this.el.select('.arrow',true).first();
29895 //arrow.set(align[2],
29897 this.el.addClass(placement);
29898 this.el.addClass("bs-tooltip-"+ placement);
29900 this.el.addClass('in fade show');
29902 this.hoverState = null;
29904 if (this.el.hasClass('fade')) {
29919 //this.el.setXY([0,0]);
29920 this.el.removeClass(['show', 'in']);
29936 * @class Roo.bootstrap.LocationPicker
29937 * @extends Roo.bootstrap.Component
29938 * Bootstrap LocationPicker class
29939 * @cfg {Number} latitude Position when init default 0
29940 * @cfg {Number} longitude Position when init default 0
29941 * @cfg {Number} zoom default 15
29942 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29943 * @cfg {Boolean} mapTypeControl default false
29944 * @cfg {Boolean} disableDoubleClickZoom default false
29945 * @cfg {Boolean} scrollwheel default true
29946 * @cfg {Boolean} streetViewControl default false
29947 * @cfg {Number} radius default 0
29948 * @cfg {String} locationName
29949 * @cfg {Boolean} draggable default true
29950 * @cfg {Boolean} enableAutocomplete default false
29951 * @cfg {Boolean} enableReverseGeocode default true
29952 * @cfg {String} markerTitle
29955 * Create a new LocationPicker
29956 * @param {Object} config The config object
29960 Roo.bootstrap.LocationPicker = function(config){
29962 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29967 * Fires when the picker initialized.
29968 * @param {Roo.bootstrap.LocationPicker} this
29969 * @param {Google Location} location
29973 * @event positionchanged
29974 * Fires when the picker position changed.
29975 * @param {Roo.bootstrap.LocationPicker} this
29976 * @param {Google Location} location
29978 positionchanged : true,
29981 * Fires when the map resize.
29982 * @param {Roo.bootstrap.LocationPicker} this
29987 * Fires when the map show.
29988 * @param {Roo.bootstrap.LocationPicker} this
29993 * Fires when the map hide.
29994 * @param {Roo.bootstrap.LocationPicker} this
29999 * Fires when click the map.
30000 * @param {Roo.bootstrap.LocationPicker} this
30001 * @param {Map event} e
30005 * @event mapRightClick
30006 * Fires when right click the map.
30007 * @param {Roo.bootstrap.LocationPicker} this
30008 * @param {Map event} e
30010 mapRightClick : true,
30012 * @event markerClick
30013 * Fires when click the marker.
30014 * @param {Roo.bootstrap.LocationPicker} this
30015 * @param {Map event} e
30017 markerClick : true,
30019 * @event markerRightClick
30020 * Fires when right click the marker.
30021 * @param {Roo.bootstrap.LocationPicker} this
30022 * @param {Map event} e
30024 markerRightClick : true,
30026 * @event OverlayViewDraw
30027 * Fires when OverlayView Draw
30028 * @param {Roo.bootstrap.LocationPicker} this
30030 OverlayViewDraw : true,
30032 * @event OverlayViewOnAdd
30033 * Fires when OverlayView Draw
30034 * @param {Roo.bootstrap.LocationPicker} this
30036 OverlayViewOnAdd : true,
30038 * @event OverlayViewOnRemove
30039 * Fires when OverlayView Draw
30040 * @param {Roo.bootstrap.LocationPicker} this
30042 OverlayViewOnRemove : true,
30044 * @event OverlayViewShow
30045 * Fires when OverlayView Draw
30046 * @param {Roo.bootstrap.LocationPicker} this
30047 * @param {Pixel} cpx
30049 OverlayViewShow : true,
30051 * @event OverlayViewHide
30052 * Fires when OverlayView Draw
30053 * @param {Roo.bootstrap.LocationPicker} this
30055 OverlayViewHide : true,
30057 * @event loadexception
30058 * Fires when load google lib failed.
30059 * @param {Roo.bootstrap.LocationPicker} this
30061 loadexception : true
30066 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30068 gMapContext: false,
30074 mapTypeControl: false,
30075 disableDoubleClickZoom: false,
30077 streetViewControl: false,
30081 enableAutocomplete: false,
30082 enableReverseGeocode: true,
30085 getAutoCreate: function()
30090 cls: 'roo-location-picker'
30096 initEvents: function(ct, position)
30098 if(!this.el.getWidth() || this.isApplied()){
30102 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30107 initial: function()
30109 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30110 this.fireEvent('loadexception', this);
30114 if(!this.mapTypeId){
30115 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30118 this.gMapContext = this.GMapContext();
30120 this.initOverlayView();
30122 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30126 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30127 _this.setPosition(_this.gMapContext.marker.position);
30130 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30131 _this.fireEvent('mapClick', this, event);
30135 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30136 _this.fireEvent('mapRightClick', this, event);
30140 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30141 _this.fireEvent('markerClick', this, event);
30145 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30146 _this.fireEvent('markerRightClick', this, event);
30150 this.setPosition(this.gMapContext.location);
30152 this.fireEvent('initial', this, this.gMapContext.location);
30155 initOverlayView: function()
30159 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30163 _this.fireEvent('OverlayViewDraw', _this);
30168 _this.fireEvent('OverlayViewOnAdd', _this);
30171 onRemove: function()
30173 _this.fireEvent('OverlayViewOnRemove', _this);
30176 show: function(cpx)
30178 _this.fireEvent('OverlayViewShow', _this, cpx);
30183 _this.fireEvent('OverlayViewHide', _this);
30189 fromLatLngToContainerPixel: function(event)
30191 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30194 isApplied: function()
30196 return this.getGmapContext() == false ? false : true;
30199 getGmapContext: function()
30201 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30204 GMapContext: function()
30206 var position = new google.maps.LatLng(this.latitude, this.longitude);
30208 var _map = new google.maps.Map(this.el.dom, {
30211 mapTypeId: this.mapTypeId,
30212 mapTypeControl: this.mapTypeControl,
30213 disableDoubleClickZoom: this.disableDoubleClickZoom,
30214 scrollwheel: this.scrollwheel,
30215 streetViewControl: this.streetViewControl,
30216 locationName: this.locationName,
30217 draggable: this.draggable,
30218 enableAutocomplete: this.enableAutocomplete,
30219 enableReverseGeocode: this.enableReverseGeocode
30222 var _marker = new google.maps.Marker({
30223 position: position,
30225 title: this.markerTitle,
30226 draggable: this.draggable
30233 location: position,
30234 radius: this.radius,
30235 locationName: this.locationName,
30236 addressComponents: {
30237 formatted_address: null,
30238 addressLine1: null,
30239 addressLine2: null,
30241 streetNumber: null,
30245 stateOrProvince: null
30248 domContainer: this.el.dom,
30249 geodecoder: new google.maps.Geocoder()
30253 drawCircle: function(center, radius, options)
30255 if (this.gMapContext.circle != null) {
30256 this.gMapContext.circle.setMap(null);
30260 options = Roo.apply({}, options, {
30261 strokeColor: "#0000FF",
30262 strokeOpacity: .35,
30264 fillColor: "#0000FF",
30268 options.map = this.gMapContext.map;
30269 options.radius = radius;
30270 options.center = center;
30271 this.gMapContext.circle = new google.maps.Circle(options);
30272 return this.gMapContext.circle;
30278 setPosition: function(location)
30280 this.gMapContext.location = location;
30281 this.gMapContext.marker.setPosition(location);
30282 this.gMapContext.map.panTo(location);
30283 this.drawCircle(location, this.gMapContext.radius, {});
30287 if (this.gMapContext.settings.enableReverseGeocode) {
30288 this.gMapContext.geodecoder.geocode({
30289 latLng: this.gMapContext.location
30290 }, function(results, status) {
30292 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30293 _this.gMapContext.locationName = results[0].formatted_address;
30294 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30296 _this.fireEvent('positionchanged', this, location);
30303 this.fireEvent('positionchanged', this, location);
30308 google.maps.event.trigger(this.gMapContext.map, "resize");
30310 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30312 this.fireEvent('resize', this);
30315 setPositionByLatLng: function(latitude, longitude)
30317 this.setPosition(new google.maps.LatLng(latitude, longitude));
30320 getCurrentPosition: function()
30323 latitude: this.gMapContext.location.lat(),
30324 longitude: this.gMapContext.location.lng()
30328 getAddressName: function()
30330 return this.gMapContext.locationName;
30333 getAddressComponents: function()
30335 return this.gMapContext.addressComponents;
30338 address_component_from_google_geocode: function(address_components)
30342 for (var i = 0; i < address_components.length; i++) {
30343 var component = address_components[i];
30344 if (component.types.indexOf("postal_code") >= 0) {
30345 result.postalCode = component.short_name;
30346 } else if (component.types.indexOf("street_number") >= 0) {
30347 result.streetNumber = component.short_name;
30348 } else if (component.types.indexOf("route") >= 0) {
30349 result.streetName = component.short_name;
30350 } else if (component.types.indexOf("neighborhood") >= 0) {
30351 result.city = component.short_name;
30352 } else if (component.types.indexOf("locality") >= 0) {
30353 result.city = component.short_name;
30354 } else if (component.types.indexOf("sublocality") >= 0) {
30355 result.district = component.short_name;
30356 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30357 result.stateOrProvince = component.short_name;
30358 } else if (component.types.indexOf("country") >= 0) {
30359 result.country = component.short_name;
30363 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30364 result.addressLine2 = "";
30368 setZoomLevel: function(zoom)
30370 this.gMapContext.map.setZoom(zoom);
30383 this.fireEvent('show', this);
30394 this.fireEvent('hide', this);
30399 Roo.apply(Roo.bootstrap.LocationPicker, {
30401 OverlayView : function(map, options)
30403 options = options || {};
30410 * @class Roo.bootstrap.Alert
30411 * @extends Roo.bootstrap.Component
30412 * Bootstrap Alert class - shows an alert area box
30414 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30415 Enter a valid email address
30418 * @cfg {String} title The title of alert
30419 * @cfg {String} html The content of alert
30420 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30421 * @cfg {String} fa font-awesomeicon
30422 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30423 * @cfg {Boolean} close true to show a x closer
30427 * Create a new alert
30428 * @param {Object} config The config object
30432 Roo.bootstrap.Alert = function(config){
30433 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30437 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30443 faicon: false, // BC
30447 getAutoCreate : function()
30459 style : this.close ? '' : 'display:none'
30463 cls : 'roo-alert-icon'
30468 cls : 'roo-alert-title',
30473 cls : 'roo-alert-text',
30480 cfg.cn[0].cls += ' fa ' + this.faicon;
30483 cfg.cn[0].cls += ' fa ' + this.fa;
30487 cfg.cls += ' alert-' + this.weight;
30493 initEvents: function()
30495 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30496 this.titleEl = this.el.select('.roo-alert-title',true).first();
30497 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30498 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30499 if (this.seconds > 0) {
30500 this.hide.defer(this.seconds, this);
30504 * Set the Title Message HTML
30505 * @param {String} html
30507 setTitle : function(str)
30509 this.titleEl.dom.innerHTML = str;
30513 * Set the Body Message HTML
30514 * @param {String} html
30516 setHtml : function(str)
30518 this.htmlEl.dom.innerHTML = str;
30521 * Set the Weight of the alert
30522 * @param {String} (success|info|warning|danger) weight
30525 setWeight : function(weight)
30528 this.el.removeClass('alert-' + this.weight);
30531 this.weight = weight;
30533 this.el.addClass('alert-' + this.weight);
30536 * Set the Icon of the alert
30537 * @param {String} see fontawsome names (name without the 'fa-' bit)
30539 setIcon : function(icon)
30542 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30545 this.faicon = icon;
30547 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30572 * @class Roo.bootstrap.UploadCropbox
30573 * @extends Roo.bootstrap.Component
30574 * Bootstrap UploadCropbox class
30575 * @cfg {String} emptyText show when image has been loaded
30576 * @cfg {String} rotateNotify show when image too small to rotate
30577 * @cfg {Number} errorTimeout default 3000
30578 * @cfg {Number} minWidth default 300
30579 * @cfg {Number} minHeight default 300
30580 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30581 * @cfg {Boolean} isDocument (true|false) default false
30582 * @cfg {String} url action url
30583 * @cfg {String} paramName default 'imageUpload'
30584 * @cfg {String} method default POST
30585 * @cfg {Boolean} loadMask (true|false) default true
30586 * @cfg {Boolean} loadingText default 'Loading...'
30589 * Create a new UploadCropbox
30590 * @param {Object} config The config object
30593 Roo.bootstrap.UploadCropbox = function(config){
30594 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30598 * @event beforeselectfile
30599 * Fire before select file
30600 * @param {Roo.bootstrap.UploadCropbox} this
30602 "beforeselectfile" : true,
30605 * Fire after initEvent
30606 * @param {Roo.bootstrap.UploadCropbox} this
30611 * Fire after initEvent
30612 * @param {Roo.bootstrap.UploadCropbox} this
30613 * @param {String} data
30618 * Fire when preparing the file data
30619 * @param {Roo.bootstrap.UploadCropbox} this
30620 * @param {Object} file
30625 * Fire when get exception
30626 * @param {Roo.bootstrap.UploadCropbox} this
30627 * @param {XMLHttpRequest} xhr
30629 "exception" : true,
30631 * @event beforeloadcanvas
30632 * Fire before load the canvas
30633 * @param {Roo.bootstrap.UploadCropbox} this
30634 * @param {String} src
30636 "beforeloadcanvas" : true,
30639 * Fire when trash image
30640 * @param {Roo.bootstrap.UploadCropbox} this
30645 * Fire when download the image
30646 * @param {Roo.bootstrap.UploadCropbox} this
30650 * @event footerbuttonclick
30651 * Fire when footerbuttonclick
30652 * @param {Roo.bootstrap.UploadCropbox} this
30653 * @param {String} type
30655 "footerbuttonclick" : true,
30659 * @param {Roo.bootstrap.UploadCropbox} this
30664 * Fire when rotate the image
30665 * @param {Roo.bootstrap.UploadCropbox} this
30666 * @param {String} pos
30671 * Fire when inspect the file
30672 * @param {Roo.bootstrap.UploadCropbox} this
30673 * @param {Object} file
30678 * Fire when xhr upload the file
30679 * @param {Roo.bootstrap.UploadCropbox} this
30680 * @param {Object} data
30685 * Fire when arrange the file data
30686 * @param {Roo.bootstrap.UploadCropbox} this
30687 * @param {Object} formData
30692 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30695 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30697 emptyText : 'Click to upload image',
30698 rotateNotify : 'Image is too small to rotate',
30699 errorTimeout : 3000,
30713 cropType : 'image/jpeg',
30715 canvasLoaded : false,
30716 isDocument : false,
30718 paramName : 'imageUpload',
30720 loadingText : 'Loading...',
30723 getAutoCreate : function()
30727 cls : 'roo-upload-cropbox',
30731 cls : 'roo-upload-cropbox-selector',
30736 cls : 'roo-upload-cropbox-body',
30737 style : 'cursor:pointer',
30741 cls : 'roo-upload-cropbox-preview'
30745 cls : 'roo-upload-cropbox-thumb'
30749 cls : 'roo-upload-cropbox-empty-notify',
30750 html : this.emptyText
30754 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30755 html : this.rotateNotify
30761 cls : 'roo-upload-cropbox-footer',
30764 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30774 onRender : function(ct, position)
30776 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30778 if (this.buttons.length) {
30780 Roo.each(this.buttons, function(bb) {
30782 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30784 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30790 this.maskEl = this.el;
30794 initEvents : function()
30796 this.urlAPI = (window.createObjectURL && window) ||
30797 (window.URL && URL.revokeObjectURL && URL) ||
30798 (window.webkitURL && webkitURL);
30800 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30801 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30803 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30804 this.selectorEl.hide();
30806 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30807 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30809 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30810 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30811 this.thumbEl.hide();
30813 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30814 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30816 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30817 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30818 this.errorEl.hide();
30820 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30821 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30822 this.footerEl.hide();
30824 this.setThumbBoxSize();
30830 this.fireEvent('initial', this);
30837 window.addEventListener("resize", function() { _this.resize(); } );
30839 this.bodyEl.on('click', this.beforeSelectFile, this);
30842 this.bodyEl.on('touchstart', this.onTouchStart, this);
30843 this.bodyEl.on('touchmove', this.onTouchMove, this);
30844 this.bodyEl.on('touchend', this.onTouchEnd, this);
30848 this.bodyEl.on('mousedown', this.onMouseDown, this);
30849 this.bodyEl.on('mousemove', this.onMouseMove, this);
30850 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30851 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30852 Roo.get(document).on('mouseup', this.onMouseUp, this);
30855 this.selectorEl.on('change', this.onFileSelected, this);
30861 this.baseScale = 1;
30863 this.baseRotate = 1;
30864 this.dragable = false;
30865 this.pinching = false;
30868 this.cropData = false;
30869 this.notifyEl.dom.innerHTML = this.emptyText;
30871 this.selectorEl.dom.value = '';
30875 resize : function()
30877 if(this.fireEvent('resize', this) != false){
30878 this.setThumbBoxPosition();
30879 this.setCanvasPosition();
30883 onFooterButtonClick : function(e, el, o, type)
30886 case 'rotate-left' :
30887 this.onRotateLeft(e);
30889 case 'rotate-right' :
30890 this.onRotateRight(e);
30893 this.beforeSelectFile(e);
30908 this.fireEvent('footerbuttonclick', this, type);
30911 beforeSelectFile : function(e)
30913 e.preventDefault();
30915 if(this.fireEvent('beforeselectfile', this) != false){
30916 this.selectorEl.dom.click();
30920 onFileSelected : function(e)
30922 e.preventDefault();
30924 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30928 var file = this.selectorEl.dom.files[0];
30930 if(this.fireEvent('inspect', this, file) != false){
30931 this.prepare(file);
30936 trash : function(e)
30938 this.fireEvent('trash', this);
30941 download : function(e)
30943 this.fireEvent('download', this);
30946 loadCanvas : function(src)
30948 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30952 this.imageEl = document.createElement('img');
30956 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30958 this.imageEl.src = src;
30962 onLoadCanvas : function()
30964 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30965 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30967 this.bodyEl.un('click', this.beforeSelectFile, this);
30969 this.notifyEl.hide();
30970 this.thumbEl.show();
30971 this.footerEl.show();
30973 this.baseRotateLevel();
30975 if(this.isDocument){
30976 this.setThumbBoxSize();
30979 this.setThumbBoxPosition();
30981 this.baseScaleLevel();
30987 this.canvasLoaded = true;
30990 this.maskEl.unmask();
30995 setCanvasPosition : function()
30997 if(!this.canvasEl){
31001 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31002 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31004 this.previewEl.setLeft(pw);
31005 this.previewEl.setTop(ph);
31009 onMouseDown : function(e)
31013 this.dragable = true;
31014 this.pinching = false;
31016 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31017 this.dragable = false;
31021 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31022 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31026 onMouseMove : function(e)
31030 if(!this.canvasLoaded){
31034 if (!this.dragable){
31038 var minX = Math.ceil(this.thumbEl.getLeft(true));
31039 var minY = Math.ceil(this.thumbEl.getTop(true));
31041 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31042 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31044 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31045 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31047 x = x - this.mouseX;
31048 y = y - this.mouseY;
31050 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31051 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31053 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31054 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31056 this.previewEl.setLeft(bgX);
31057 this.previewEl.setTop(bgY);
31059 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31060 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31063 onMouseUp : function(e)
31067 this.dragable = false;
31070 onMouseWheel : function(e)
31074 this.startScale = this.scale;
31076 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31078 if(!this.zoomable()){
31079 this.scale = this.startScale;
31088 zoomable : function()
31090 var minScale = this.thumbEl.getWidth() / this.minWidth;
31092 if(this.minWidth < this.minHeight){
31093 minScale = this.thumbEl.getHeight() / this.minHeight;
31096 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31097 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31101 (this.rotate == 0 || this.rotate == 180) &&
31103 width > this.imageEl.OriginWidth ||
31104 height > this.imageEl.OriginHeight ||
31105 (width < this.minWidth && height < this.minHeight)
31113 (this.rotate == 90 || this.rotate == 270) &&
31115 width > this.imageEl.OriginWidth ||
31116 height > this.imageEl.OriginHeight ||
31117 (width < this.minHeight && height < this.minWidth)
31124 !this.isDocument &&
31125 (this.rotate == 0 || this.rotate == 180) &&
31127 width < this.minWidth ||
31128 width > this.imageEl.OriginWidth ||
31129 height < this.minHeight ||
31130 height > this.imageEl.OriginHeight
31137 !this.isDocument &&
31138 (this.rotate == 90 || this.rotate == 270) &&
31140 width < this.minHeight ||
31141 width > this.imageEl.OriginWidth ||
31142 height < this.minWidth ||
31143 height > this.imageEl.OriginHeight
31153 onRotateLeft : function(e)
31155 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31157 var minScale = this.thumbEl.getWidth() / this.minWidth;
31159 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31160 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31162 this.startScale = this.scale;
31164 while (this.getScaleLevel() < minScale){
31166 this.scale = this.scale + 1;
31168 if(!this.zoomable()){
31173 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31174 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31179 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31186 this.scale = this.startScale;
31188 this.onRotateFail();
31193 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31195 if(this.isDocument){
31196 this.setThumbBoxSize();
31197 this.setThumbBoxPosition();
31198 this.setCanvasPosition();
31203 this.fireEvent('rotate', this, 'left');
31207 onRotateRight : function(e)
31209 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31211 var minScale = this.thumbEl.getWidth() / this.minWidth;
31213 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31214 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31216 this.startScale = this.scale;
31218 while (this.getScaleLevel() < minScale){
31220 this.scale = this.scale + 1;
31222 if(!this.zoomable()){
31227 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31228 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31233 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31240 this.scale = this.startScale;
31242 this.onRotateFail();
31247 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31249 if(this.isDocument){
31250 this.setThumbBoxSize();
31251 this.setThumbBoxPosition();
31252 this.setCanvasPosition();
31257 this.fireEvent('rotate', this, 'right');
31260 onRotateFail : function()
31262 this.errorEl.show(true);
31266 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31271 this.previewEl.dom.innerHTML = '';
31273 var canvasEl = document.createElement("canvas");
31275 var contextEl = canvasEl.getContext("2d");
31277 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31278 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31279 var center = this.imageEl.OriginWidth / 2;
31281 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31282 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31283 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31284 center = this.imageEl.OriginHeight / 2;
31287 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31289 contextEl.translate(center, center);
31290 contextEl.rotate(this.rotate * Math.PI / 180);
31292 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31294 this.canvasEl = document.createElement("canvas");
31296 this.contextEl = this.canvasEl.getContext("2d");
31298 switch (this.rotate) {
31301 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31302 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31304 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31309 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31310 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31312 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31313 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);
31317 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31322 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31323 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31325 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31326 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);
31330 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);
31335 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31336 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31338 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31339 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31343 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);
31350 this.previewEl.appendChild(this.canvasEl);
31352 this.setCanvasPosition();
31357 if(!this.canvasLoaded){
31361 var imageCanvas = document.createElement("canvas");
31363 var imageContext = imageCanvas.getContext("2d");
31365 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31366 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31368 var center = imageCanvas.width / 2;
31370 imageContext.translate(center, center);
31372 imageContext.rotate(this.rotate * Math.PI / 180);
31374 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31376 var canvas = document.createElement("canvas");
31378 var context = canvas.getContext("2d");
31380 canvas.width = this.minWidth;
31381 canvas.height = this.minHeight;
31383 switch (this.rotate) {
31386 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31387 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31389 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31390 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31392 var targetWidth = this.minWidth - 2 * x;
31393 var targetHeight = this.minHeight - 2 * y;
31397 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31398 scale = targetWidth / width;
31401 if(x > 0 && y == 0){
31402 scale = targetHeight / height;
31405 if(x > 0 && y > 0){
31406 scale = targetWidth / width;
31408 if(width < height){
31409 scale = targetHeight / height;
31413 context.scale(scale, scale);
31415 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31416 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31418 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31419 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31421 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31426 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31427 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31429 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31430 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31432 var targetWidth = this.minWidth - 2 * x;
31433 var targetHeight = this.minHeight - 2 * y;
31437 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31438 scale = targetWidth / width;
31441 if(x > 0 && y == 0){
31442 scale = targetHeight / height;
31445 if(x > 0 && y > 0){
31446 scale = targetWidth / width;
31448 if(width < height){
31449 scale = targetHeight / height;
31453 context.scale(scale, scale);
31455 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31456 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31458 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31459 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31461 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31463 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31468 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31469 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31471 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31472 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31474 var targetWidth = this.minWidth - 2 * x;
31475 var targetHeight = this.minHeight - 2 * y;
31479 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31480 scale = targetWidth / width;
31483 if(x > 0 && y == 0){
31484 scale = targetHeight / height;
31487 if(x > 0 && y > 0){
31488 scale = targetWidth / width;
31490 if(width < height){
31491 scale = targetHeight / height;
31495 context.scale(scale, scale);
31497 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31498 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31500 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31501 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31503 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31504 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31506 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31511 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31512 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31514 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31515 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31517 var targetWidth = this.minWidth - 2 * x;
31518 var targetHeight = this.minHeight - 2 * y;
31522 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31523 scale = targetWidth / width;
31526 if(x > 0 && y == 0){
31527 scale = targetHeight / height;
31530 if(x > 0 && y > 0){
31531 scale = targetWidth / width;
31533 if(width < height){
31534 scale = targetHeight / height;
31538 context.scale(scale, scale);
31540 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31541 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31543 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31544 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31546 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31548 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31555 this.cropData = canvas.toDataURL(this.cropType);
31557 if(this.fireEvent('crop', this, this.cropData) !== false){
31558 this.process(this.file, this.cropData);
31565 setThumbBoxSize : function()
31569 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31570 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31571 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31573 this.minWidth = width;
31574 this.minHeight = height;
31576 if(this.rotate == 90 || this.rotate == 270){
31577 this.minWidth = height;
31578 this.minHeight = width;
31583 width = Math.ceil(this.minWidth * height / this.minHeight);
31585 if(this.minWidth > this.minHeight){
31587 height = Math.ceil(this.minHeight * width / this.minWidth);
31590 this.thumbEl.setStyle({
31591 width : width + 'px',
31592 height : height + 'px'
31599 setThumbBoxPosition : function()
31601 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31602 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31604 this.thumbEl.setLeft(x);
31605 this.thumbEl.setTop(y);
31609 baseRotateLevel : function()
31611 this.baseRotate = 1;
31614 typeof(this.exif) != 'undefined' &&
31615 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31616 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31618 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31621 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31625 baseScaleLevel : function()
31629 if(this.isDocument){
31631 if(this.baseRotate == 6 || this.baseRotate == 8){
31633 height = this.thumbEl.getHeight();
31634 this.baseScale = height / this.imageEl.OriginWidth;
31636 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31637 width = this.thumbEl.getWidth();
31638 this.baseScale = width / this.imageEl.OriginHeight;
31644 height = this.thumbEl.getHeight();
31645 this.baseScale = height / this.imageEl.OriginHeight;
31647 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31648 width = this.thumbEl.getWidth();
31649 this.baseScale = width / this.imageEl.OriginWidth;
31655 if(this.baseRotate == 6 || this.baseRotate == 8){
31657 width = this.thumbEl.getHeight();
31658 this.baseScale = width / this.imageEl.OriginHeight;
31660 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31661 height = this.thumbEl.getWidth();
31662 this.baseScale = height / this.imageEl.OriginHeight;
31665 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31666 height = this.thumbEl.getWidth();
31667 this.baseScale = height / this.imageEl.OriginHeight;
31669 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31670 width = this.thumbEl.getHeight();
31671 this.baseScale = width / this.imageEl.OriginWidth;
31678 width = this.thumbEl.getWidth();
31679 this.baseScale = width / this.imageEl.OriginWidth;
31681 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31682 height = this.thumbEl.getHeight();
31683 this.baseScale = height / this.imageEl.OriginHeight;
31686 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31688 height = this.thumbEl.getHeight();
31689 this.baseScale = height / this.imageEl.OriginHeight;
31691 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31692 width = this.thumbEl.getWidth();
31693 this.baseScale = width / this.imageEl.OriginWidth;
31701 getScaleLevel : function()
31703 return this.baseScale * Math.pow(1.1, this.scale);
31706 onTouchStart : function(e)
31708 if(!this.canvasLoaded){
31709 this.beforeSelectFile(e);
31713 var touches = e.browserEvent.touches;
31719 if(touches.length == 1){
31720 this.onMouseDown(e);
31724 if(touches.length != 2){
31730 for(var i = 0, finger; finger = touches[i]; i++){
31731 coords.push(finger.pageX, finger.pageY);
31734 var x = Math.pow(coords[0] - coords[2], 2);
31735 var y = Math.pow(coords[1] - coords[3], 2);
31737 this.startDistance = Math.sqrt(x + y);
31739 this.startScale = this.scale;
31741 this.pinching = true;
31742 this.dragable = false;
31746 onTouchMove : function(e)
31748 if(!this.pinching && !this.dragable){
31752 var touches = e.browserEvent.touches;
31759 this.onMouseMove(e);
31765 for(var i = 0, finger; finger = touches[i]; i++){
31766 coords.push(finger.pageX, finger.pageY);
31769 var x = Math.pow(coords[0] - coords[2], 2);
31770 var y = Math.pow(coords[1] - coords[3], 2);
31772 this.endDistance = Math.sqrt(x + y);
31774 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31776 if(!this.zoomable()){
31777 this.scale = this.startScale;
31785 onTouchEnd : function(e)
31787 this.pinching = false;
31788 this.dragable = false;
31792 process : function(file, crop)
31795 this.maskEl.mask(this.loadingText);
31798 this.xhr = new XMLHttpRequest();
31800 file.xhr = this.xhr;
31802 this.xhr.open(this.method, this.url, true);
31805 "Accept": "application/json",
31806 "Cache-Control": "no-cache",
31807 "X-Requested-With": "XMLHttpRequest"
31810 for (var headerName in headers) {
31811 var headerValue = headers[headerName];
31813 this.xhr.setRequestHeader(headerName, headerValue);
31819 this.xhr.onload = function()
31821 _this.xhrOnLoad(_this.xhr);
31824 this.xhr.onerror = function()
31826 _this.xhrOnError(_this.xhr);
31829 var formData = new FormData();
31831 formData.append('returnHTML', 'NO');
31834 formData.append('crop', crop);
31837 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31838 formData.append(this.paramName, file, file.name);
31841 if(typeof(file.filename) != 'undefined'){
31842 formData.append('filename', file.filename);
31845 if(typeof(file.mimetype) != 'undefined'){
31846 formData.append('mimetype', file.mimetype);
31849 if(this.fireEvent('arrange', this, formData) != false){
31850 this.xhr.send(formData);
31854 xhrOnLoad : function(xhr)
31857 this.maskEl.unmask();
31860 if (xhr.readyState !== 4) {
31861 this.fireEvent('exception', this, xhr);
31865 var response = Roo.decode(xhr.responseText);
31867 if(!response.success){
31868 this.fireEvent('exception', this, xhr);
31872 var response = Roo.decode(xhr.responseText);
31874 this.fireEvent('upload', this, response);
31878 xhrOnError : function()
31881 this.maskEl.unmask();
31884 Roo.log('xhr on error');
31886 var response = Roo.decode(xhr.responseText);
31892 prepare : function(file)
31895 this.maskEl.mask(this.loadingText);
31901 if(typeof(file) === 'string'){
31902 this.loadCanvas(file);
31906 if(!file || !this.urlAPI){
31911 this.cropType = file.type;
31915 if(this.fireEvent('prepare', this, this.file) != false){
31917 var reader = new FileReader();
31919 reader.onload = function (e) {
31920 if (e.target.error) {
31921 Roo.log(e.target.error);
31925 var buffer = e.target.result,
31926 dataView = new DataView(buffer),
31928 maxOffset = dataView.byteLength - 4,
31932 if (dataView.getUint16(0) === 0xffd8) {
31933 while (offset < maxOffset) {
31934 markerBytes = dataView.getUint16(offset);
31936 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31937 markerLength = dataView.getUint16(offset + 2) + 2;
31938 if (offset + markerLength > dataView.byteLength) {
31939 Roo.log('Invalid meta data: Invalid segment size.');
31943 if(markerBytes == 0xffe1){
31944 _this.parseExifData(
31951 offset += markerLength;
31961 var url = _this.urlAPI.createObjectURL(_this.file);
31963 _this.loadCanvas(url);
31968 reader.readAsArrayBuffer(this.file);
31974 parseExifData : function(dataView, offset, length)
31976 var tiffOffset = offset + 10,
31980 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31981 // No Exif data, might be XMP data instead
31985 // Check for the ASCII code for "Exif" (0x45786966):
31986 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31987 // No Exif data, might be XMP data instead
31990 if (tiffOffset + 8 > dataView.byteLength) {
31991 Roo.log('Invalid Exif data: Invalid segment size.');
31994 // Check for the two null bytes:
31995 if (dataView.getUint16(offset + 8) !== 0x0000) {
31996 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31999 // Check the byte alignment:
32000 switch (dataView.getUint16(tiffOffset)) {
32002 littleEndian = true;
32005 littleEndian = false;
32008 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32011 // Check for the TIFF tag marker (0x002A):
32012 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32013 Roo.log('Invalid Exif data: Missing TIFF marker.');
32016 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32017 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32019 this.parseExifTags(
32022 tiffOffset + dirOffset,
32027 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32032 if (dirOffset + 6 > dataView.byteLength) {
32033 Roo.log('Invalid Exif data: Invalid directory offset.');
32036 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32037 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32038 if (dirEndOffset + 4 > dataView.byteLength) {
32039 Roo.log('Invalid Exif data: Invalid directory size.');
32042 for (i = 0; i < tagsNumber; i += 1) {
32046 dirOffset + 2 + 12 * i, // tag offset
32050 // Return the offset to the next directory:
32051 return dataView.getUint32(dirEndOffset, littleEndian);
32054 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32056 var tag = dataView.getUint16(offset, littleEndian);
32058 this.exif[tag] = this.getExifValue(
32062 dataView.getUint16(offset + 2, littleEndian), // tag type
32063 dataView.getUint32(offset + 4, littleEndian), // tag length
32068 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32070 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32079 Roo.log('Invalid Exif data: Invalid tag type.');
32083 tagSize = tagType.size * length;
32084 // Determine if the value is contained in the dataOffset bytes,
32085 // or if the value at the dataOffset is a pointer to the actual data:
32086 dataOffset = tagSize > 4 ?
32087 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32088 if (dataOffset + tagSize > dataView.byteLength) {
32089 Roo.log('Invalid Exif data: Invalid data offset.');
32092 if (length === 1) {
32093 return tagType.getValue(dataView, dataOffset, littleEndian);
32096 for (i = 0; i < length; i += 1) {
32097 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32100 if (tagType.ascii) {
32102 // Concatenate the chars:
32103 for (i = 0; i < values.length; i += 1) {
32105 // Ignore the terminating NULL byte(s):
32106 if (c === '\u0000') {
32118 Roo.apply(Roo.bootstrap.UploadCropbox, {
32120 'Orientation': 0x0112
32124 1: 0, //'top-left',
32126 3: 180, //'bottom-right',
32127 // 4: 'bottom-left',
32129 6: 90, //'right-top',
32130 // 7: 'right-bottom',
32131 8: 270 //'left-bottom'
32135 // byte, 8-bit unsigned int:
32137 getValue: function (dataView, dataOffset) {
32138 return dataView.getUint8(dataOffset);
32142 // ascii, 8-bit byte:
32144 getValue: function (dataView, dataOffset) {
32145 return String.fromCharCode(dataView.getUint8(dataOffset));
32150 // short, 16 bit int:
32152 getValue: function (dataView, dataOffset, littleEndian) {
32153 return dataView.getUint16(dataOffset, littleEndian);
32157 // long, 32 bit int:
32159 getValue: function (dataView, dataOffset, littleEndian) {
32160 return dataView.getUint32(dataOffset, littleEndian);
32164 // rational = two long values, first is numerator, second is denominator:
32166 getValue: function (dataView, dataOffset, littleEndian) {
32167 return dataView.getUint32(dataOffset, littleEndian) /
32168 dataView.getUint32(dataOffset + 4, littleEndian);
32172 // slong, 32 bit signed int:
32174 getValue: function (dataView, dataOffset, littleEndian) {
32175 return dataView.getInt32(dataOffset, littleEndian);
32179 // srational, two slongs, first is numerator, second is denominator:
32181 getValue: function (dataView, dataOffset, littleEndian) {
32182 return dataView.getInt32(dataOffset, littleEndian) /
32183 dataView.getInt32(dataOffset + 4, littleEndian);
32193 cls : 'btn-group roo-upload-cropbox-rotate-left',
32194 action : 'rotate-left',
32198 cls : 'btn btn-default',
32199 html : '<i class="fa fa-undo"></i>'
32205 cls : 'btn-group roo-upload-cropbox-picture',
32206 action : 'picture',
32210 cls : 'btn btn-default',
32211 html : '<i class="fa fa-picture-o"></i>'
32217 cls : 'btn-group roo-upload-cropbox-rotate-right',
32218 action : 'rotate-right',
32222 cls : 'btn btn-default',
32223 html : '<i class="fa fa-repeat"></i>'
32231 cls : 'btn-group roo-upload-cropbox-rotate-left',
32232 action : 'rotate-left',
32236 cls : 'btn btn-default',
32237 html : '<i class="fa fa-undo"></i>'
32243 cls : 'btn-group roo-upload-cropbox-download',
32244 action : 'download',
32248 cls : 'btn btn-default',
32249 html : '<i class="fa fa-download"></i>'
32255 cls : 'btn-group roo-upload-cropbox-crop',
32260 cls : 'btn btn-default',
32261 html : '<i class="fa fa-crop"></i>'
32267 cls : 'btn-group roo-upload-cropbox-trash',
32272 cls : 'btn btn-default',
32273 html : '<i class="fa fa-trash"></i>'
32279 cls : 'btn-group roo-upload-cropbox-rotate-right',
32280 action : 'rotate-right',
32284 cls : 'btn btn-default',
32285 html : '<i class="fa fa-repeat"></i>'
32293 cls : 'btn-group roo-upload-cropbox-rotate-left',
32294 action : 'rotate-left',
32298 cls : 'btn btn-default',
32299 html : '<i class="fa fa-undo"></i>'
32305 cls : 'btn-group roo-upload-cropbox-rotate-right',
32306 action : 'rotate-right',
32310 cls : 'btn btn-default',
32311 html : '<i class="fa fa-repeat"></i>'
32324 * @class Roo.bootstrap.DocumentManager
32325 * @extends Roo.bootstrap.Component
32326 * Bootstrap DocumentManager class
32327 * @cfg {String} paramName default 'imageUpload'
32328 * @cfg {String} toolTipName default 'filename'
32329 * @cfg {String} method default POST
32330 * @cfg {String} url action url
32331 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32332 * @cfg {Boolean} multiple multiple upload default true
32333 * @cfg {Number} thumbSize default 300
32334 * @cfg {String} fieldLabel
32335 * @cfg {Number} labelWidth default 4
32336 * @cfg {String} labelAlign (left|top) default left
32337 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32338 * @cfg {Number} labellg set the width of label (1-12)
32339 * @cfg {Number} labelmd set the width of label (1-12)
32340 * @cfg {Number} labelsm set the width of label (1-12)
32341 * @cfg {Number} labelxs set the width of label (1-12)
32344 * Create a new DocumentManager
32345 * @param {Object} config The config object
32348 Roo.bootstrap.DocumentManager = function(config){
32349 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32352 this.delegates = [];
32357 * Fire when initial the DocumentManager
32358 * @param {Roo.bootstrap.DocumentManager} this
32363 * inspect selected file
32364 * @param {Roo.bootstrap.DocumentManager} this
32365 * @param {File} file
32370 * Fire when xhr load exception
32371 * @param {Roo.bootstrap.DocumentManager} this
32372 * @param {XMLHttpRequest} xhr
32374 "exception" : true,
32376 * @event afterupload
32377 * Fire when xhr load exception
32378 * @param {Roo.bootstrap.DocumentManager} this
32379 * @param {XMLHttpRequest} xhr
32381 "afterupload" : true,
32384 * prepare the form data
32385 * @param {Roo.bootstrap.DocumentManager} this
32386 * @param {Object} formData
32391 * Fire when remove the file
32392 * @param {Roo.bootstrap.DocumentManager} this
32393 * @param {Object} file
32398 * Fire after refresh the file
32399 * @param {Roo.bootstrap.DocumentManager} this
32404 * Fire after click the image
32405 * @param {Roo.bootstrap.DocumentManager} this
32406 * @param {Object} file
32411 * Fire when upload a image and editable set to true
32412 * @param {Roo.bootstrap.DocumentManager} this
32413 * @param {Object} file
32417 * @event beforeselectfile
32418 * Fire before select file
32419 * @param {Roo.bootstrap.DocumentManager} this
32421 "beforeselectfile" : true,
32424 * Fire before process file
32425 * @param {Roo.bootstrap.DocumentManager} this
32426 * @param {Object} file
32430 * @event previewrendered
32431 * Fire when preview rendered
32432 * @param {Roo.bootstrap.DocumentManager} this
32433 * @param {Object} file
32435 "previewrendered" : true,
32438 "previewResize" : true
32443 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32452 paramName : 'imageUpload',
32453 toolTipName : 'filename',
32456 labelAlign : 'left',
32466 getAutoCreate : function()
32468 var managerWidget = {
32470 cls : 'roo-document-manager',
32474 cls : 'roo-document-manager-selector',
32479 cls : 'roo-document-manager-uploader',
32483 cls : 'roo-document-manager-upload-btn',
32484 html : '<i class="fa fa-plus"></i>'
32495 cls : 'column col-md-12',
32500 if(this.fieldLabel.length){
32505 cls : 'column col-md-12',
32506 html : this.fieldLabel
32510 cls : 'column col-md-12',
32515 if(this.labelAlign == 'left'){
32520 html : this.fieldLabel
32529 if(this.labelWidth > 12){
32530 content[0].style = "width: " + this.labelWidth + 'px';
32533 if(this.labelWidth < 13 && this.labelmd == 0){
32534 this.labelmd = this.labelWidth;
32537 if(this.labellg > 0){
32538 content[0].cls += ' col-lg-' + this.labellg;
32539 content[1].cls += ' col-lg-' + (12 - this.labellg);
32542 if(this.labelmd > 0){
32543 content[0].cls += ' col-md-' + this.labelmd;
32544 content[1].cls += ' col-md-' + (12 - this.labelmd);
32547 if(this.labelsm > 0){
32548 content[0].cls += ' col-sm-' + this.labelsm;
32549 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32552 if(this.labelxs > 0){
32553 content[0].cls += ' col-xs-' + this.labelxs;
32554 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32562 cls : 'row clearfix',
32570 initEvents : function()
32572 this.managerEl = this.el.select('.roo-document-manager', true).first();
32573 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32575 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32576 this.selectorEl.hide();
32579 this.selectorEl.attr('multiple', 'multiple');
32582 this.selectorEl.on('change', this.onFileSelected, this);
32584 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32585 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32587 this.uploader.on('click', this.onUploaderClick, this);
32589 this.renderProgressDialog();
32593 window.addEventListener("resize", function() { _this.refresh(); } );
32595 this.fireEvent('initial', this);
32598 renderProgressDialog : function()
32602 this.progressDialog = new Roo.bootstrap.Modal({
32603 cls : 'roo-document-manager-progress-dialog',
32604 allow_close : false,
32615 btnclick : function() {
32616 _this.uploadCancel();
32622 this.progressDialog.render(Roo.get(document.body));
32624 this.progress = new Roo.bootstrap.Progress({
32625 cls : 'roo-document-manager-progress',
32630 this.progress.render(this.progressDialog.getChildContainer());
32632 this.progressBar = new Roo.bootstrap.ProgressBar({
32633 cls : 'roo-document-manager-progress-bar',
32636 aria_valuemax : 12,
32640 this.progressBar.render(this.progress.getChildContainer());
32643 onUploaderClick : function(e)
32645 e.preventDefault();
32647 if(this.fireEvent('beforeselectfile', this) != false){
32648 this.selectorEl.dom.click();
32653 onFileSelected : function(e)
32655 e.preventDefault();
32657 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32661 Roo.each(this.selectorEl.dom.files, function(file){
32662 if(this.fireEvent('inspect', this, file) != false){
32663 this.files.push(file);
32673 this.selectorEl.dom.value = '';
32675 if(!this.files || !this.files.length){
32679 if(this.boxes > 0 && this.files.length > this.boxes){
32680 this.files = this.files.slice(0, this.boxes);
32683 this.uploader.show();
32685 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32686 this.uploader.hide();
32695 Roo.each(this.files, function(file){
32697 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32698 var f = this.renderPreview(file);
32703 if(file.type.indexOf('image') != -1){
32704 this.delegates.push(
32706 _this.process(file);
32707 }).createDelegate(this)
32715 _this.process(file);
32716 }).createDelegate(this)
32721 this.files = files;
32723 this.delegates = this.delegates.concat(docs);
32725 if(!this.delegates.length){
32730 this.progressBar.aria_valuemax = this.delegates.length;
32737 arrange : function()
32739 if(!this.delegates.length){
32740 this.progressDialog.hide();
32745 var delegate = this.delegates.shift();
32747 this.progressDialog.show();
32749 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32751 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32756 refresh : function()
32758 this.uploader.show();
32760 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32761 this.uploader.hide();
32764 Roo.isTouch ? this.closable(false) : this.closable(true);
32766 this.fireEvent('refresh', this);
32769 onRemove : function(e, el, o)
32771 e.preventDefault();
32773 this.fireEvent('remove', this, o);
32777 remove : function(o)
32781 Roo.each(this.files, function(file){
32782 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32791 this.files = files;
32798 Roo.each(this.files, function(file){
32803 file.target.remove();
32812 onClick : function(e, el, o)
32814 e.preventDefault();
32816 this.fireEvent('click', this, o);
32820 closable : function(closable)
32822 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32824 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32836 xhrOnLoad : function(xhr)
32838 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32842 if (xhr.readyState !== 4) {
32844 this.fireEvent('exception', this, xhr);
32848 var response = Roo.decode(xhr.responseText);
32850 if(!response.success){
32852 this.fireEvent('exception', this, xhr);
32856 var file = this.renderPreview(response.data);
32858 this.files.push(file);
32862 this.fireEvent('afterupload', this, xhr);
32866 xhrOnError : function(xhr)
32868 Roo.log('xhr on error');
32870 var response = Roo.decode(xhr.responseText);
32877 process : function(file)
32879 if(this.fireEvent('process', this, file) !== false){
32880 if(this.editable && file.type.indexOf('image') != -1){
32881 this.fireEvent('edit', this, file);
32885 this.uploadStart(file, false);
32892 uploadStart : function(file, crop)
32894 this.xhr = new XMLHttpRequest();
32896 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32901 file.xhr = this.xhr;
32903 this.managerEl.createChild({
32905 cls : 'roo-document-manager-loading',
32909 tooltip : file.name,
32910 cls : 'roo-document-manager-thumb',
32911 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32917 this.xhr.open(this.method, this.url, true);
32920 "Accept": "application/json",
32921 "Cache-Control": "no-cache",
32922 "X-Requested-With": "XMLHttpRequest"
32925 for (var headerName in headers) {
32926 var headerValue = headers[headerName];
32928 this.xhr.setRequestHeader(headerName, headerValue);
32934 this.xhr.onload = function()
32936 _this.xhrOnLoad(_this.xhr);
32939 this.xhr.onerror = function()
32941 _this.xhrOnError(_this.xhr);
32944 var formData = new FormData();
32946 formData.append('returnHTML', 'NO');
32949 formData.append('crop', crop);
32952 formData.append(this.paramName, file, file.name);
32959 if(this.fireEvent('prepare', this, formData, options) != false){
32961 if(options.manually){
32965 this.xhr.send(formData);
32969 this.uploadCancel();
32972 uploadCancel : function()
32978 this.delegates = [];
32980 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32987 renderPreview : function(file)
32989 if(typeof(file.target) != 'undefined' && file.target){
32993 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32995 var previewEl = this.managerEl.createChild({
32997 cls : 'roo-document-manager-preview',
33001 tooltip : file[this.toolTipName],
33002 cls : 'roo-document-manager-thumb',
33003 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33008 html : '<i class="fa fa-times-circle"></i>'
33013 var close = previewEl.select('button.close', true).first();
33015 close.on('click', this.onRemove, this, file);
33017 file.target = previewEl;
33019 var image = previewEl.select('img', true).first();
33023 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33025 image.on('click', this.onClick, this, file);
33027 this.fireEvent('previewrendered', this, file);
33033 onPreviewLoad : function(file, image)
33035 if(typeof(file.target) == 'undefined' || !file.target){
33039 var width = image.dom.naturalWidth || image.dom.width;
33040 var height = image.dom.naturalHeight || image.dom.height;
33042 if(!this.previewResize) {
33046 if(width > height){
33047 file.target.addClass('wide');
33051 file.target.addClass('tall');
33056 uploadFromSource : function(file, crop)
33058 this.xhr = new XMLHttpRequest();
33060 this.managerEl.createChild({
33062 cls : 'roo-document-manager-loading',
33066 tooltip : file.name,
33067 cls : 'roo-document-manager-thumb',
33068 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33074 this.xhr.open(this.method, this.url, true);
33077 "Accept": "application/json",
33078 "Cache-Control": "no-cache",
33079 "X-Requested-With": "XMLHttpRequest"
33082 for (var headerName in headers) {
33083 var headerValue = headers[headerName];
33085 this.xhr.setRequestHeader(headerName, headerValue);
33091 this.xhr.onload = function()
33093 _this.xhrOnLoad(_this.xhr);
33096 this.xhr.onerror = function()
33098 _this.xhrOnError(_this.xhr);
33101 var formData = new FormData();
33103 formData.append('returnHTML', 'NO');
33105 formData.append('crop', crop);
33107 if(typeof(file.filename) != 'undefined'){
33108 formData.append('filename', file.filename);
33111 if(typeof(file.mimetype) != 'undefined'){
33112 formData.append('mimetype', file.mimetype);
33117 if(this.fireEvent('prepare', this, formData) != false){
33118 this.xhr.send(formData);
33128 * @class Roo.bootstrap.DocumentViewer
33129 * @extends Roo.bootstrap.Component
33130 * Bootstrap DocumentViewer class
33131 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33132 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33135 * Create a new DocumentViewer
33136 * @param {Object} config The config object
33139 Roo.bootstrap.DocumentViewer = function(config){
33140 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33145 * Fire after initEvent
33146 * @param {Roo.bootstrap.DocumentViewer} this
33152 * @param {Roo.bootstrap.DocumentViewer} this
33157 * Fire after download button
33158 * @param {Roo.bootstrap.DocumentViewer} this
33163 * Fire after trash button
33164 * @param {Roo.bootstrap.DocumentViewer} this
33171 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33173 showDownload : true,
33177 getAutoCreate : function()
33181 cls : 'roo-document-viewer',
33185 cls : 'roo-document-viewer-body',
33189 cls : 'roo-document-viewer-thumb',
33193 cls : 'roo-document-viewer-image'
33201 cls : 'roo-document-viewer-footer',
33204 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33208 cls : 'btn-group roo-document-viewer-download',
33212 cls : 'btn btn-default',
33213 html : '<i class="fa fa-download"></i>'
33219 cls : 'btn-group roo-document-viewer-trash',
33223 cls : 'btn btn-default',
33224 html : '<i class="fa fa-trash"></i>'
33237 initEvents : function()
33239 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33240 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33242 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33243 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33245 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33246 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33248 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33249 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33251 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33252 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33254 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33255 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33257 this.bodyEl.on('click', this.onClick, this);
33258 this.downloadBtn.on('click', this.onDownload, this);
33259 this.trashBtn.on('click', this.onTrash, this);
33261 this.downloadBtn.hide();
33262 this.trashBtn.hide();
33264 if(this.showDownload){
33265 this.downloadBtn.show();
33268 if(this.showTrash){
33269 this.trashBtn.show();
33272 if(!this.showDownload && !this.showTrash) {
33273 this.footerEl.hide();
33278 initial : function()
33280 this.fireEvent('initial', this);
33284 onClick : function(e)
33286 e.preventDefault();
33288 this.fireEvent('click', this);
33291 onDownload : function(e)
33293 e.preventDefault();
33295 this.fireEvent('download', this);
33298 onTrash : function(e)
33300 e.preventDefault();
33302 this.fireEvent('trash', this);
33314 * @class Roo.bootstrap.NavProgressBar
33315 * @extends Roo.bootstrap.Component
33316 * Bootstrap NavProgressBar class
33319 * Create a new nav progress bar
33320 * @param {Object} config The config object
33323 Roo.bootstrap.NavProgressBar = function(config){
33324 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33326 this.bullets = this.bullets || [];
33328 // Roo.bootstrap.NavProgressBar.register(this);
33332 * Fires when the active item changes
33333 * @param {Roo.bootstrap.NavProgressBar} this
33334 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33335 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33342 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33347 getAutoCreate : function()
33349 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33353 cls : 'roo-navigation-bar-group',
33357 cls : 'roo-navigation-top-bar'
33361 cls : 'roo-navigation-bullets-bar',
33365 cls : 'roo-navigation-bar'
33372 cls : 'roo-navigation-bottom-bar'
33382 initEvents: function()
33387 onRender : function(ct, position)
33389 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33391 if(this.bullets.length){
33392 Roo.each(this.bullets, function(b){
33401 addItem : function(cfg)
33403 var item = new Roo.bootstrap.NavProgressItem(cfg);
33405 item.parentId = this.id;
33406 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33409 var top = new Roo.bootstrap.Element({
33411 cls : 'roo-navigation-bar-text'
33414 var bottom = new Roo.bootstrap.Element({
33416 cls : 'roo-navigation-bar-text'
33419 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33420 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33422 var topText = new Roo.bootstrap.Element({
33424 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33427 var bottomText = new Roo.bootstrap.Element({
33429 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33432 topText.onRender(top.el, null);
33433 bottomText.onRender(bottom.el, null);
33436 item.bottomEl = bottom;
33439 this.barItems.push(item);
33444 getActive : function()
33446 var active = false;
33448 Roo.each(this.barItems, function(v){
33450 if (!v.isActive()) {
33462 setActiveItem : function(item)
33466 Roo.each(this.barItems, function(v){
33467 if (v.rid == item.rid) {
33471 if (v.isActive()) {
33472 v.setActive(false);
33477 item.setActive(true);
33479 this.fireEvent('changed', this, item, prev);
33482 getBarItem: function(rid)
33486 Roo.each(this.barItems, function(e) {
33487 if (e.rid != rid) {
33498 indexOfItem : function(item)
33502 Roo.each(this.barItems, function(v, i){
33504 if (v.rid != item.rid) {
33515 setActiveNext : function()
33517 var i = this.indexOfItem(this.getActive());
33519 if (i > this.barItems.length) {
33523 this.setActiveItem(this.barItems[i+1]);
33526 setActivePrev : function()
33528 var i = this.indexOfItem(this.getActive());
33534 this.setActiveItem(this.barItems[i-1]);
33537 format : function()
33539 if(!this.barItems.length){
33543 var width = 100 / this.barItems.length;
33545 Roo.each(this.barItems, function(i){
33546 i.el.setStyle('width', width + '%');
33547 i.topEl.el.setStyle('width', width + '%');
33548 i.bottomEl.el.setStyle('width', width + '%');
33557 * Nav Progress Item
33562 * @class Roo.bootstrap.NavProgressItem
33563 * @extends Roo.bootstrap.Component
33564 * Bootstrap NavProgressItem class
33565 * @cfg {String} rid the reference id
33566 * @cfg {Boolean} active (true|false) Is item active default false
33567 * @cfg {Boolean} disabled (true|false) Is item active default false
33568 * @cfg {String} html
33569 * @cfg {String} position (top|bottom) text position default bottom
33570 * @cfg {String} icon show icon instead of number
33573 * Create a new NavProgressItem
33574 * @param {Object} config The config object
33576 Roo.bootstrap.NavProgressItem = function(config){
33577 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33582 * The raw click event for the entire grid.
33583 * @param {Roo.bootstrap.NavProgressItem} this
33584 * @param {Roo.EventObject} e
33591 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33597 position : 'bottom',
33600 getAutoCreate : function()
33602 var iconCls = 'roo-navigation-bar-item-icon';
33604 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33608 cls: 'roo-navigation-bar-item',
33618 cfg.cls += ' active';
33621 cfg.cls += ' disabled';
33627 disable : function()
33629 this.setDisabled(true);
33632 enable : function()
33634 this.setDisabled(false);
33637 initEvents: function()
33639 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33641 this.iconEl.on('click', this.onClick, this);
33644 onClick : function(e)
33646 e.preventDefault();
33652 if(this.fireEvent('click', this, e) === false){
33656 this.parent().setActiveItem(this);
33659 isActive: function ()
33661 return this.active;
33664 setActive : function(state)
33666 if(this.active == state){
33670 this.active = state;
33673 this.el.addClass('active');
33677 this.el.removeClass('active');
33682 setDisabled : function(state)
33684 if(this.disabled == state){
33688 this.disabled = state;
33691 this.el.addClass('disabled');
33695 this.el.removeClass('disabled');
33698 tooltipEl : function()
33700 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33713 * @class Roo.bootstrap.FieldLabel
33714 * @extends Roo.bootstrap.Component
33715 * Bootstrap FieldLabel class
33716 * @cfg {String} html contents of the element
33717 * @cfg {String} tag tag of the element default label
33718 * @cfg {String} cls class of the element
33719 * @cfg {String} target label target
33720 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33721 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33722 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33723 * @cfg {String} iconTooltip default "This field is required"
33724 * @cfg {String} indicatorpos (left|right) default left
33727 * Create a new FieldLabel
33728 * @param {Object} config The config object
33731 Roo.bootstrap.FieldLabel = function(config){
33732 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33737 * Fires after the field has been marked as invalid.
33738 * @param {Roo.form.FieldLabel} this
33739 * @param {String} msg The validation message
33744 * Fires after the field has been validated with no errors.
33745 * @param {Roo.form.FieldLabel} this
33751 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33758 invalidClass : 'has-warning',
33759 validClass : 'has-success',
33760 iconTooltip : 'This field is required',
33761 indicatorpos : 'left',
33763 getAutoCreate : function(){
33766 if (!this.allowBlank) {
33772 cls : 'roo-bootstrap-field-label ' + this.cls,
33777 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33778 tooltip : this.iconTooltip
33787 if(this.indicatorpos == 'right'){
33790 cls : 'roo-bootstrap-field-label ' + this.cls,
33799 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33800 tooltip : this.iconTooltip
33809 initEvents: function()
33811 Roo.bootstrap.Element.superclass.initEvents.call(this);
33813 this.indicator = this.indicatorEl();
33815 if(this.indicator){
33816 this.indicator.removeClass('visible');
33817 this.indicator.addClass('invisible');
33820 Roo.bootstrap.FieldLabel.register(this);
33823 indicatorEl : function()
33825 var indicator = this.el.select('i.roo-required-indicator',true).first();
33836 * Mark this field as valid
33838 markValid : function()
33840 if(this.indicator){
33841 this.indicator.removeClass('visible');
33842 this.indicator.addClass('invisible');
33844 if (Roo.bootstrap.version == 3) {
33845 this.el.removeClass(this.invalidClass);
33846 this.el.addClass(this.validClass);
33848 this.el.removeClass('is-invalid');
33849 this.el.addClass('is-valid');
33853 this.fireEvent('valid', this);
33857 * Mark this field as invalid
33858 * @param {String} msg The validation message
33860 markInvalid : function(msg)
33862 if(this.indicator){
33863 this.indicator.removeClass('invisible');
33864 this.indicator.addClass('visible');
33866 if (Roo.bootstrap.version == 3) {
33867 this.el.removeClass(this.validClass);
33868 this.el.addClass(this.invalidClass);
33870 this.el.removeClass('is-valid');
33871 this.el.addClass('is-invalid');
33875 this.fireEvent('invalid', this, msg);
33881 Roo.apply(Roo.bootstrap.FieldLabel, {
33886 * register a FieldLabel Group
33887 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33889 register : function(label)
33891 if(this.groups.hasOwnProperty(label.target)){
33895 this.groups[label.target] = label;
33899 * fetch a FieldLabel Group based on the target
33900 * @param {string} target
33901 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33903 get: function(target) {
33904 if (typeof(this.groups[target]) == 'undefined') {
33908 return this.groups[target] ;
33917 * page DateSplitField.
33923 * @class Roo.bootstrap.DateSplitField
33924 * @extends Roo.bootstrap.Component
33925 * Bootstrap DateSplitField class
33926 * @cfg {string} fieldLabel - the label associated
33927 * @cfg {Number} labelWidth set the width of label (0-12)
33928 * @cfg {String} labelAlign (top|left)
33929 * @cfg {Boolean} dayAllowBlank (true|false) default false
33930 * @cfg {Boolean} monthAllowBlank (true|false) default false
33931 * @cfg {Boolean} yearAllowBlank (true|false) default false
33932 * @cfg {string} dayPlaceholder
33933 * @cfg {string} monthPlaceholder
33934 * @cfg {string} yearPlaceholder
33935 * @cfg {string} dayFormat default 'd'
33936 * @cfg {string} monthFormat default 'm'
33937 * @cfg {string} yearFormat default 'Y'
33938 * @cfg {Number} labellg set the width of label (1-12)
33939 * @cfg {Number} labelmd set the width of label (1-12)
33940 * @cfg {Number} labelsm set the width of label (1-12)
33941 * @cfg {Number} labelxs set the width of label (1-12)
33945 * Create a new DateSplitField
33946 * @param {Object} config The config object
33949 Roo.bootstrap.DateSplitField = function(config){
33950 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33956 * getting the data of years
33957 * @param {Roo.bootstrap.DateSplitField} this
33958 * @param {Object} years
33963 * getting the data of days
33964 * @param {Roo.bootstrap.DateSplitField} this
33965 * @param {Object} days
33970 * Fires after the field has been marked as invalid.
33971 * @param {Roo.form.Field} this
33972 * @param {String} msg The validation message
33977 * Fires after the field has been validated with no errors.
33978 * @param {Roo.form.Field} this
33984 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33987 labelAlign : 'top',
33989 dayAllowBlank : false,
33990 monthAllowBlank : false,
33991 yearAllowBlank : false,
33992 dayPlaceholder : '',
33993 monthPlaceholder : '',
33994 yearPlaceholder : '',
33998 isFormField : true,
34004 getAutoCreate : function()
34008 cls : 'row roo-date-split-field-group',
34013 cls : 'form-hidden-field roo-date-split-field-group-value',
34019 var labelCls = 'col-md-12';
34020 var contentCls = 'col-md-4';
34022 if(this.fieldLabel){
34026 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34030 html : this.fieldLabel
34035 if(this.labelAlign == 'left'){
34037 if(this.labelWidth > 12){
34038 label.style = "width: " + this.labelWidth + 'px';
34041 if(this.labelWidth < 13 && this.labelmd == 0){
34042 this.labelmd = this.labelWidth;
34045 if(this.labellg > 0){
34046 labelCls = ' col-lg-' + this.labellg;
34047 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34050 if(this.labelmd > 0){
34051 labelCls = ' col-md-' + this.labelmd;
34052 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34055 if(this.labelsm > 0){
34056 labelCls = ' col-sm-' + this.labelsm;
34057 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34060 if(this.labelxs > 0){
34061 labelCls = ' col-xs-' + this.labelxs;
34062 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34066 label.cls += ' ' + labelCls;
34068 cfg.cn.push(label);
34071 Roo.each(['day', 'month', 'year'], function(t){
34074 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34081 inputEl: function ()
34083 return this.el.select('.roo-date-split-field-group-value', true).first();
34086 onRender : function(ct, position)
34090 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34092 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34094 this.dayField = new Roo.bootstrap.ComboBox({
34095 allowBlank : this.dayAllowBlank,
34096 alwaysQuery : true,
34097 displayField : 'value',
34100 forceSelection : true,
34102 placeholder : this.dayPlaceholder,
34103 selectOnFocus : true,
34104 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34105 triggerAction : 'all',
34107 valueField : 'value',
34108 store : new Roo.data.SimpleStore({
34109 data : (function() {
34111 _this.fireEvent('days', _this, days);
34114 fields : [ 'value' ]
34117 select : function (_self, record, index)
34119 _this.setValue(_this.getValue());
34124 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34126 this.monthField = new Roo.bootstrap.MonthField({
34127 after : '<i class=\"fa fa-calendar\"></i>',
34128 allowBlank : this.monthAllowBlank,
34129 placeholder : this.monthPlaceholder,
34132 render : function (_self)
34134 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34135 e.preventDefault();
34139 select : function (_self, oldvalue, newvalue)
34141 _this.setValue(_this.getValue());
34146 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34148 this.yearField = new Roo.bootstrap.ComboBox({
34149 allowBlank : this.yearAllowBlank,
34150 alwaysQuery : true,
34151 displayField : 'value',
34154 forceSelection : true,
34156 placeholder : this.yearPlaceholder,
34157 selectOnFocus : true,
34158 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34159 triggerAction : 'all',
34161 valueField : 'value',
34162 store : new Roo.data.SimpleStore({
34163 data : (function() {
34165 _this.fireEvent('years', _this, years);
34168 fields : [ 'value' ]
34171 select : function (_self, record, index)
34173 _this.setValue(_this.getValue());
34178 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34181 setValue : function(v, format)
34183 this.inputEl.dom.value = v;
34185 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34187 var d = Date.parseDate(v, f);
34194 this.setDay(d.format(this.dayFormat));
34195 this.setMonth(d.format(this.monthFormat));
34196 this.setYear(d.format(this.yearFormat));
34203 setDay : function(v)
34205 this.dayField.setValue(v);
34206 this.inputEl.dom.value = this.getValue();
34211 setMonth : function(v)
34213 this.monthField.setValue(v, true);
34214 this.inputEl.dom.value = this.getValue();
34219 setYear : function(v)
34221 this.yearField.setValue(v);
34222 this.inputEl.dom.value = this.getValue();
34227 getDay : function()
34229 return this.dayField.getValue();
34232 getMonth : function()
34234 return this.monthField.getValue();
34237 getYear : function()
34239 return this.yearField.getValue();
34242 getValue : function()
34244 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34246 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34256 this.inputEl.dom.value = '';
34261 validate : function()
34263 var d = this.dayField.validate();
34264 var m = this.monthField.validate();
34265 var y = this.yearField.validate();
34270 (!this.dayAllowBlank && !d) ||
34271 (!this.monthAllowBlank && !m) ||
34272 (!this.yearAllowBlank && !y)
34277 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34286 this.markInvalid();
34291 markValid : function()
34294 var label = this.el.select('label', true).first();
34295 var icon = this.el.select('i.fa-star', true).first();
34301 this.fireEvent('valid', this);
34305 * Mark this field as invalid
34306 * @param {String} msg The validation message
34308 markInvalid : function(msg)
34311 var label = this.el.select('label', true).first();
34312 var icon = this.el.select('i.fa-star', true).first();
34314 if(label && !icon){
34315 this.el.select('.roo-date-split-field-label', true).createChild({
34317 cls : 'text-danger fa fa-lg fa-star',
34318 tooltip : 'This field is required',
34319 style : 'margin-right:5px;'
34323 this.fireEvent('invalid', this, msg);
34326 clearInvalid : function()
34328 var label = this.el.select('label', true).first();
34329 var icon = this.el.select('i.fa-star', true).first();
34335 this.fireEvent('valid', this);
34338 getName: function()
34348 * http://masonry.desandro.com
34350 * The idea is to render all the bricks based on vertical width...
34352 * The original code extends 'outlayer' - we might need to use that....
34358 * @class Roo.bootstrap.LayoutMasonry
34359 * @extends Roo.bootstrap.Component
34360 * Bootstrap Layout Masonry class
34363 * Create a new Element
34364 * @param {Object} config The config object
34367 Roo.bootstrap.LayoutMasonry = function(config){
34369 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34373 Roo.bootstrap.LayoutMasonry.register(this);
34379 * Fire after layout the items
34380 * @param {Roo.bootstrap.LayoutMasonry} this
34381 * @param {Roo.EventObject} e
34388 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34391 * @cfg {Boolean} isLayoutInstant = no animation?
34393 isLayoutInstant : false, // needed?
34396 * @cfg {Number} boxWidth width of the columns
34401 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34406 * @cfg {Number} padWidth padding below box..
34411 * @cfg {Number} gutter gutter width..
34416 * @cfg {Number} maxCols maximum number of columns
34422 * @cfg {Boolean} isAutoInitial defalut true
34424 isAutoInitial : true,
34429 * @cfg {Boolean} isHorizontal defalut false
34431 isHorizontal : false,
34433 currentSize : null,
34439 bricks: null, //CompositeElement
34443 _isLayoutInited : false,
34445 // isAlternative : false, // only use for vertical layout...
34448 * @cfg {Number} alternativePadWidth padding below box..
34450 alternativePadWidth : 50,
34452 selectedBrick : [],
34454 getAutoCreate : function(){
34456 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34460 cls: 'blog-masonary-wrapper ' + this.cls,
34462 cls : 'mas-boxes masonary'
34469 getChildContainer: function( )
34471 if (this.boxesEl) {
34472 return this.boxesEl;
34475 this.boxesEl = this.el.select('.mas-boxes').first();
34477 return this.boxesEl;
34481 initEvents : function()
34485 if(this.isAutoInitial){
34486 Roo.log('hook children rendered');
34487 this.on('childrenrendered', function() {
34488 Roo.log('children rendered');
34494 initial : function()
34496 this.selectedBrick = [];
34498 this.currentSize = this.el.getBox(true);
34500 Roo.EventManager.onWindowResize(this.resize, this);
34502 if(!this.isAutoInitial){
34510 //this.layout.defer(500,this);
34514 resize : function()
34516 var cs = this.el.getBox(true);
34519 this.currentSize.width == cs.width &&
34520 this.currentSize.x == cs.x &&
34521 this.currentSize.height == cs.height &&
34522 this.currentSize.y == cs.y
34524 Roo.log("no change in with or X or Y");
34528 this.currentSize = cs;
34534 layout : function()
34536 this._resetLayout();
34538 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34540 this.layoutItems( isInstant );
34542 this._isLayoutInited = true;
34544 this.fireEvent('layout', this);
34548 _resetLayout : function()
34550 if(this.isHorizontal){
34551 this.horizontalMeasureColumns();
34555 this.verticalMeasureColumns();
34559 verticalMeasureColumns : function()
34561 this.getContainerWidth();
34563 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34564 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34568 var boxWidth = this.boxWidth + this.padWidth;
34570 if(this.containerWidth < this.boxWidth){
34571 boxWidth = this.containerWidth
34574 var containerWidth = this.containerWidth;
34576 var cols = Math.floor(containerWidth / boxWidth);
34578 this.cols = Math.max( cols, 1 );
34580 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34582 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34584 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34586 this.colWidth = boxWidth + avail - this.padWidth;
34588 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34589 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34592 horizontalMeasureColumns : function()
34594 this.getContainerWidth();
34596 var boxWidth = this.boxWidth;
34598 if(this.containerWidth < boxWidth){
34599 boxWidth = this.containerWidth;
34602 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34604 this.el.setHeight(boxWidth);
34608 getContainerWidth : function()
34610 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34613 layoutItems : function( isInstant )
34615 Roo.log(this.bricks);
34617 var items = Roo.apply([], this.bricks);
34619 if(this.isHorizontal){
34620 this._horizontalLayoutItems( items , isInstant );
34624 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34625 // this._verticalAlternativeLayoutItems( items , isInstant );
34629 this._verticalLayoutItems( items , isInstant );
34633 _verticalLayoutItems : function ( items , isInstant)
34635 if ( !items || !items.length ) {
34640 ['xs', 'xs', 'xs', 'tall'],
34641 ['xs', 'xs', 'tall'],
34642 ['xs', 'xs', 'sm'],
34643 ['xs', 'xs', 'xs'],
34649 ['sm', 'xs', 'xs'],
34653 ['tall', 'xs', 'xs', 'xs'],
34654 ['tall', 'xs', 'xs'],
34666 Roo.each(items, function(item, k){
34668 switch (item.size) {
34669 // these layouts take up a full box,
34680 boxes.push([item]);
34703 var filterPattern = function(box, length)
34711 var pattern = box.slice(0, length);
34715 Roo.each(pattern, function(i){
34716 format.push(i.size);
34719 Roo.each(standard, function(s){
34721 if(String(s) != String(format)){
34730 if(!match && length == 1){
34735 filterPattern(box, length - 1);
34739 queue.push(pattern);
34741 box = box.slice(length, box.length);
34743 filterPattern(box, 4);
34749 Roo.each(boxes, function(box, k){
34755 if(box.length == 1){
34760 filterPattern(box, 4);
34764 this._processVerticalLayoutQueue( queue, isInstant );
34768 // _verticalAlternativeLayoutItems : function( items , isInstant )
34770 // if ( !items || !items.length ) {
34774 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34778 _horizontalLayoutItems : function ( items , isInstant)
34780 if ( !items || !items.length || items.length < 3) {
34786 var eItems = items.slice(0, 3);
34788 items = items.slice(3, items.length);
34791 ['xs', 'xs', 'xs', 'wide'],
34792 ['xs', 'xs', 'wide'],
34793 ['xs', 'xs', 'sm'],
34794 ['xs', 'xs', 'xs'],
34800 ['sm', 'xs', 'xs'],
34804 ['wide', 'xs', 'xs', 'xs'],
34805 ['wide', 'xs', 'xs'],
34818 Roo.each(items, function(item, k){
34820 switch (item.size) {
34831 boxes.push([item]);
34855 var filterPattern = function(box, length)
34863 var pattern = box.slice(0, length);
34867 Roo.each(pattern, function(i){
34868 format.push(i.size);
34871 Roo.each(standard, function(s){
34873 if(String(s) != String(format)){
34882 if(!match && length == 1){
34887 filterPattern(box, length - 1);
34891 queue.push(pattern);
34893 box = box.slice(length, box.length);
34895 filterPattern(box, 4);
34901 Roo.each(boxes, function(box, k){
34907 if(box.length == 1){
34912 filterPattern(box, 4);
34919 var pos = this.el.getBox(true);
34923 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34925 var hit_end = false;
34927 Roo.each(queue, function(box){
34931 Roo.each(box, function(b){
34933 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34943 Roo.each(box, function(b){
34945 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34948 mx = Math.max(mx, b.x);
34952 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34956 Roo.each(box, function(b){
34958 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34972 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34975 /** Sets position of item in DOM
34976 * @param {Element} item
34977 * @param {Number} x - horizontal position
34978 * @param {Number} y - vertical position
34979 * @param {Boolean} isInstant - disables transitions
34981 _processVerticalLayoutQueue : function( queue, isInstant )
34983 var pos = this.el.getBox(true);
34988 for (var i = 0; i < this.cols; i++){
34992 Roo.each(queue, function(box, k){
34994 var col = k % this.cols;
34996 Roo.each(box, function(b,kk){
34998 b.el.position('absolute');
35000 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35001 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35003 if(b.size == 'md-left' || b.size == 'md-right'){
35004 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35005 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35008 b.el.setWidth(width);
35009 b.el.setHeight(height);
35011 b.el.select('iframe',true).setSize(width,height);
35015 for (var i = 0; i < this.cols; i++){
35017 if(maxY[i] < maxY[col]){
35022 col = Math.min(col, i);
35026 x = pos.x + col * (this.colWidth + this.padWidth);
35030 var positions = [];
35032 switch (box.length){
35034 positions = this.getVerticalOneBoxColPositions(x, y, box);
35037 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35040 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35043 positions = this.getVerticalFourBoxColPositions(x, y, box);
35049 Roo.each(box, function(b,kk){
35051 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35053 var sz = b.el.getSize();
35055 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35063 for (var i = 0; i < this.cols; i++){
35064 mY = Math.max(mY, maxY[i]);
35067 this.el.setHeight(mY - pos.y);
35071 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35073 // var pos = this.el.getBox(true);
35076 // var maxX = pos.right;
35078 // var maxHeight = 0;
35080 // Roo.each(items, function(item, k){
35084 // item.el.position('absolute');
35086 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35088 // item.el.setWidth(width);
35090 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35092 // item.el.setHeight(height);
35095 // item.el.setXY([x, y], isInstant ? false : true);
35097 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35100 // y = y + height + this.alternativePadWidth;
35102 // maxHeight = maxHeight + height + this.alternativePadWidth;
35106 // this.el.setHeight(maxHeight);
35110 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35112 var pos = this.el.getBox(true);
35117 var maxX = pos.right;
35119 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35121 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35123 Roo.each(queue, function(box, k){
35125 Roo.each(box, function(b, kk){
35127 b.el.position('absolute');
35129 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35130 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35132 if(b.size == 'md-left' || b.size == 'md-right'){
35133 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35134 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35137 b.el.setWidth(width);
35138 b.el.setHeight(height);
35146 var positions = [];
35148 switch (box.length){
35150 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35153 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35156 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35159 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35165 Roo.each(box, function(b,kk){
35167 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35169 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35177 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35179 Roo.each(eItems, function(b,k){
35181 b.size = (k == 0) ? 'sm' : 'xs';
35182 b.x = (k == 0) ? 2 : 1;
35183 b.y = (k == 0) ? 2 : 1;
35185 b.el.position('absolute');
35187 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35189 b.el.setWidth(width);
35191 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35193 b.el.setHeight(height);
35197 var positions = [];
35200 x : maxX - this.unitWidth * 2 - this.gutter,
35205 x : maxX - this.unitWidth,
35206 y : minY + (this.unitWidth + this.gutter) * 2
35210 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35214 Roo.each(eItems, function(b,k){
35216 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35222 getVerticalOneBoxColPositions : function(x, y, box)
35226 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35228 if(box[0].size == 'md-left'){
35232 if(box[0].size == 'md-right'){
35237 x : x + (this.unitWidth + this.gutter) * rand,
35244 getVerticalTwoBoxColPositions : function(x, y, box)
35248 if(box[0].size == 'xs'){
35252 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35256 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35270 x : x + (this.unitWidth + this.gutter) * 2,
35271 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35278 getVerticalThreeBoxColPositions : function(x, y, box)
35282 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35290 x : x + (this.unitWidth + this.gutter) * 1,
35295 x : x + (this.unitWidth + this.gutter) * 2,
35303 if(box[0].size == 'xs' && box[1].size == 'xs'){
35312 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35316 x : x + (this.unitWidth + this.gutter) * 1,
35330 x : x + (this.unitWidth + this.gutter) * 2,
35335 x : x + (this.unitWidth + this.gutter) * 2,
35336 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35343 getVerticalFourBoxColPositions : function(x, y, box)
35347 if(box[0].size == 'xs'){
35356 y : y + (this.unitHeight + this.gutter) * 1
35361 y : y + (this.unitHeight + this.gutter) * 2
35365 x : x + (this.unitWidth + this.gutter) * 1,
35379 x : x + (this.unitWidth + this.gutter) * 2,
35384 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35385 y : y + (this.unitHeight + this.gutter) * 1
35389 x : x + (this.unitWidth + this.gutter) * 2,
35390 y : y + (this.unitWidth + this.gutter) * 2
35397 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35401 if(box[0].size == 'md-left'){
35403 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35410 if(box[0].size == 'md-right'){
35412 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35413 y : minY + (this.unitWidth + this.gutter) * 1
35419 var rand = Math.floor(Math.random() * (4 - box[0].y));
35422 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35423 y : minY + (this.unitWidth + this.gutter) * rand
35430 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35434 if(box[0].size == 'xs'){
35437 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35442 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35443 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35451 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35456 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35457 y : minY + (this.unitWidth + this.gutter) * 2
35464 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35468 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35471 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35476 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35477 y : minY + (this.unitWidth + this.gutter) * 1
35481 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35482 y : minY + (this.unitWidth + this.gutter) * 2
35489 if(box[0].size == 'xs' && box[1].size == 'xs'){
35492 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35497 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35502 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35503 y : minY + (this.unitWidth + this.gutter) * 1
35511 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35516 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35517 y : minY + (this.unitWidth + this.gutter) * 2
35521 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35522 y : minY + (this.unitWidth + this.gutter) * 2
35529 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35533 if(box[0].size == 'xs'){
35536 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35541 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35546 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),
35551 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35552 y : minY + (this.unitWidth + this.gutter) * 1
35560 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35565 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35566 y : minY + (this.unitWidth + this.gutter) * 2
35570 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35571 y : minY + (this.unitWidth + this.gutter) * 2
35575 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),
35576 y : minY + (this.unitWidth + this.gutter) * 2
35584 * remove a Masonry Brick
35585 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35587 removeBrick : function(brick_id)
35593 for (var i = 0; i<this.bricks.length; i++) {
35594 if (this.bricks[i].id == brick_id) {
35595 this.bricks.splice(i,1);
35596 this.el.dom.removeChild(Roo.get(brick_id).dom);
35603 * adds a Masonry Brick
35604 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35606 addBrick : function(cfg)
35608 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35609 //this.register(cn);
35610 cn.parentId = this.id;
35611 cn.render(this.el);
35616 * register a Masonry Brick
35617 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35620 register : function(brick)
35622 this.bricks.push(brick);
35623 brick.masonryId = this.id;
35627 * clear all the Masonry Brick
35629 clearAll : function()
35632 //this.getChildContainer().dom.innerHTML = "";
35633 this.el.dom.innerHTML = '';
35636 getSelected : function()
35638 if (!this.selectedBrick) {
35642 return this.selectedBrick;
35646 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35650 * register a Masonry Layout
35651 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35654 register : function(layout)
35656 this.groups[layout.id] = layout;
35659 * fetch a Masonry Layout based on the masonry layout ID
35660 * @param {string} the masonry layout to add
35661 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35664 get: function(layout_id) {
35665 if (typeof(this.groups[layout_id]) == 'undefined') {
35668 return this.groups[layout_id] ;
35680 * http://masonry.desandro.com
35682 * The idea is to render all the bricks based on vertical width...
35684 * The original code extends 'outlayer' - we might need to use that....
35690 * @class Roo.bootstrap.LayoutMasonryAuto
35691 * @extends Roo.bootstrap.Component
35692 * Bootstrap Layout Masonry class
35695 * Create a new Element
35696 * @param {Object} config The config object
35699 Roo.bootstrap.LayoutMasonryAuto = function(config){
35700 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35703 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35706 * @cfg {Boolean} isFitWidth - resize the width..
35708 isFitWidth : false, // options..
35710 * @cfg {Boolean} isOriginLeft = left align?
35712 isOriginLeft : true,
35714 * @cfg {Boolean} isOriginTop = top align?
35716 isOriginTop : false,
35718 * @cfg {Boolean} isLayoutInstant = no animation?
35720 isLayoutInstant : false, // needed?
35722 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35724 isResizingContainer : true,
35726 * @cfg {Number} columnWidth width of the columns
35732 * @cfg {Number} maxCols maximum number of columns
35737 * @cfg {Number} padHeight padding below box..
35743 * @cfg {Boolean} isAutoInitial defalut true
35746 isAutoInitial : true,
35752 initialColumnWidth : 0,
35753 currentSize : null,
35755 colYs : null, // array.
35762 bricks: null, //CompositeElement
35763 cols : 0, // array?
35764 // element : null, // wrapped now this.el
35765 _isLayoutInited : null,
35768 getAutoCreate : function(){
35772 cls: 'blog-masonary-wrapper ' + this.cls,
35774 cls : 'mas-boxes masonary'
35781 getChildContainer: function( )
35783 if (this.boxesEl) {
35784 return this.boxesEl;
35787 this.boxesEl = this.el.select('.mas-boxes').first();
35789 return this.boxesEl;
35793 initEvents : function()
35797 if(this.isAutoInitial){
35798 Roo.log('hook children rendered');
35799 this.on('childrenrendered', function() {
35800 Roo.log('children rendered');
35807 initial : function()
35809 this.reloadItems();
35811 this.currentSize = this.el.getBox(true);
35813 /// was window resize... - let's see if this works..
35814 Roo.EventManager.onWindowResize(this.resize, this);
35816 if(!this.isAutoInitial){
35821 this.layout.defer(500,this);
35824 reloadItems: function()
35826 this.bricks = this.el.select('.masonry-brick', true);
35828 this.bricks.each(function(b) {
35829 //Roo.log(b.getSize());
35830 if (!b.attr('originalwidth')) {
35831 b.attr('originalwidth', b.getSize().width);
35836 Roo.log(this.bricks.elements.length);
35839 resize : function()
35842 var cs = this.el.getBox(true);
35844 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35845 Roo.log("no change in with or X");
35848 this.currentSize = cs;
35852 layout : function()
35855 this._resetLayout();
35856 //this._manageStamps();
35858 // don't animate first layout
35859 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35860 this.layoutItems( isInstant );
35862 // flag for initalized
35863 this._isLayoutInited = true;
35866 layoutItems : function( isInstant )
35868 //var items = this._getItemsForLayout( this.items );
35869 // original code supports filtering layout items.. we just ignore it..
35871 this._layoutItems( this.bricks , isInstant );
35873 this._postLayout();
35875 _layoutItems : function ( items , isInstant)
35877 //this.fireEvent( 'layout', this, items );
35880 if ( !items || !items.elements.length ) {
35881 // no items, emit event with empty array
35886 items.each(function(item) {
35887 Roo.log("layout item");
35889 // get x/y object from method
35890 var position = this._getItemLayoutPosition( item );
35892 position.item = item;
35893 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35894 queue.push( position );
35897 this._processLayoutQueue( queue );
35899 /** Sets position of item in DOM
35900 * @param {Element} item
35901 * @param {Number} x - horizontal position
35902 * @param {Number} y - vertical position
35903 * @param {Boolean} isInstant - disables transitions
35905 _processLayoutQueue : function( queue )
35907 for ( var i=0, len = queue.length; i < len; i++ ) {
35908 var obj = queue[i];
35909 obj.item.position('absolute');
35910 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35916 * Any logic you want to do after each layout,
35917 * i.e. size the container
35919 _postLayout : function()
35921 this.resizeContainer();
35924 resizeContainer : function()
35926 if ( !this.isResizingContainer ) {
35929 var size = this._getContainerSize();
35931 this.el.setSize(size.width,size.height);
35932 this.boxesEl.setSize(size.width,size.height);
35938 _resetLayout : function()
35940 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35941 this.colWidth = this.el.getWidth();
35942 //this.gutter = this.el.getWidth();
35944 this.measureColumns();
35950 this.colYs.push( 0 );
35956 measureColumns : function()
35958 this.getContainerWidth();
35959 // if columnWidth is 0, default to outerWidth of first item
35960 if ( !this.columnWidth ) {
35961 var firstItem = this.bricks.first();
35962 Roo.log(firstItem);
35963 this.columnWidth = this.containerWidth;
35964 if (firstItem && firstItem.attr('originalwidth') ) {
35965 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35967 // columnWidth fall back to item of first element
35968 Roo.log("set column width?");
35969 this.initialColumnWidth = this.columnWidth ;
35971 // if first elem has no width, default to size of container
35976 if (this.initialColumnWidth) {
35977 this.columnWidth = this.initialColumnWidth;
35982 // column width is fixed at the top - however if container width get's smaller we should
35985 // this bit calcs how man columns..
35987 var columnWidth = this.columnWidth += this.gutter;
35989 // calculate columns
35990 var containerWidth = this.containerWidth + this.gutter;
35992 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35993 // fix rounding errors, typically with gutters
35994 var excess = columnWidth - containerWidth % columnWidth;
35997 // if overshoot is less than a pixel, round up, otherwise floor it
35998 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35999 cols = Math[ mathMethod ]( cols );
36000 this.cols = Math.max( cols, 1 );
36001 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36003 // padding positioning..
36004 var totalColWidth = this.cols * this.columnWidth;
36005 var padavail = this.containerWidth - totalColWidth;
36006 // so for 2 columns - we need 3 'pads'
36008 var padNeeded = (1+this.cols) * this.padWidth;
36010 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36012 this.columnWidth += padExtra
36013 //this.padWidth = Math.floor(padavail / ( this.cols));
36015 // adjust colum width so that padding is fixed??
36017 // we have 3 columns ... total = width * 3
36018 // we have X left over... that should be used by
36020 //if (this.expandC) {
36028 getContainerWidth : function()
36030 /* // container is parent if fit width
36031 var container = this.isFitWidth ? this.element.parentNode : this.element;
36032 // check that this.size and size are there
36033 // IE8 triggers resize on body size change, so they might not be
36035 var size = getSize( container ); //FIXME
36036 this.containerWidth = size && size.innerWidth; //FIXME
36039 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36043 _getItemLayoutPosition : function( item ) // what is item?
36045 // we resize the item to our columnWidth..
36047 item.setWidth(this.columnWidth);
36048 item.autoBoxAdjust = false;
36050 var sz = item.getSize();
36052 // how many columns does this brick span
36053 var remainder = this.containerWidth % this.columnWidth;
36055 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36056 // round if off by 1 pixel, otherwise use ceil
36057 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36058 colSpan = Math.min( colSpan, this.cols );
36060 // normally this should be '1' as we dont' currently allow multi width columns..
36062 var colGroup = this._getColGroup( colSpan );
36063 // get the minimum Y value from the columns
36064 var minimumY = Math.min.apply( Math, colGroup );
36065 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36067 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36069 // position the brick
36071 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36072 y: this.currentSize.y + minimumY + this.padHeight
36076 // apply setHeight to necessary columns
36077 var setHeight = minimumY + sz.height + this.padHeight;
36078 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36080 var setSpan = this.cols + 1 - colGroup.length;
36081 for ( var i = 0; i < setSpan; i++ ) {
36082 this.colYs[ shortColIndex + i ] = setHeight ;
36089 * @param {Number} colSpan - number of columns the element spans
36090 * @returns {Array} colGroup
36092 _getColGroup : function( colSpan )
36094 if ( colSpan < 2 ) {
36095 // if brick spans only one column, use all the column Ys
36100 // how many different places could this brick fit horizontally
36101 var groupCount = this.cols + 1 - colSpan;
36102 // for each group potential horizontal position
36103 for ( var i = 0; i < groupCount; i++ ) {
36104 // make an array of colY values for that one group
36105 var groupColYs = this.colYs.slice( i, i + colSpan );
36106 // and get the max value of the array
36107 colGroup[i] = Math.max.apply( Math, groupColYs );
36112 _manageStamp : function( stamp )
36114 var stampSize = stamp.getSize();
36115 var offset = stamp.getBox();
36116 // get the columns that this stamp affects
36117 var firstX = this.isOriginLeft ? offset.x : offset.right;
36118 var lastX = firstX + stampSize.width;
36119 var firstCol = Math.floor( firstX / this.columnWidth );
36120 firstCol = Math.max( 0, firstCol );
36122 var lastCol = Math.floor( lastX / this.columnWidth );
36123 // lastCol should not go over if multiple of columnWidth #425
36124 lastCol -= lastX % this.columnWidth ? 0 : 1;
36125 lastCol = Math.min( this.cols - 1, lastCol );
36127 // set colYs to bottom of the stamp
36128 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36131 for ( var i = firstCol; i <= lastCol; i++ ) {
36132 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36137 _getContainerSize : function()
36139 this.maxY = Math.max.apply( Math, this.colYs );
36144 if ( this.isFitWidth ) {
36145 size.width = this._getContainerFitWidth();
36151 _getContainerFitWidth : function()
36153 var unusedCols = 0;
36154 // count unused columns
36157 if ( this.colYs[i] !== 0 ) {
36162 // fit container to columns that have been used
36163 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36166 needsResizeLayout : function()
36168 var previousWidth = this.containerWidth;
36169 this.getContainerWidth();
36170 return previousWidth !== this.containerWidth;
36185 * @class Roo.bootstrap.MasonryBrick
36186 * @extends Roo.bootstrap.Component
36187 * Bootstrap MasonryBrick class
36190 * Create a new MasonryBrick
36191 * @param {Object} config The config object
36194 Roo.bootstrap.MasonryBrick = function(config){
36196 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36198 Roo.bootstrap.MasonryBrick.register(this);
36204 * When a MasonryBrick is clcik
36205 * @param {Roo.bootstrap.MasonryBrick} this
36206 * @param {Roo.EventObject} e
36212 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36215 * @cfg {String} title
36219 * @cfg {String} html
36223 * @cfg {String} bgimage
36227 * @cfg {String} videourl
36231 * @cfg {String} cls
36235 * @cfg {String} href
36239 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36244 * @cfg {String} placetitle (center|bottom)
36249 * @cfg {Boolean} isFitContainer defalut true
36251 isFitContainer : true,
36254 * @cfg {Boolean} preventDefault defalut false
36256 preventDefault : false,
36259 * @cfg {Boolean} inverse defalut false
36261 maskInverse : false,
36263 getAutoCreate : function()
36265 if(!this.isFitContainer){
36266 return this.getSplitAutoCreate();
36269 var cls = 'masonry-brick masonry-brick-full';
36271 if(this.href.length){
36272 cls += ' masonry-brick-link';
36275 if(this.bgimage.length){
36276 cls += ' masonry-brick-image';
36279 if(this.maskInverse){
36280 cls += ' mask-inverse';
36283 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36284 cls += ' enable-mask';
36288 cls += ' masonry-' + this.size + '-brick';
36291 if(this.placetitle.length){
36293 switch (this.placetitle) {
36295 cls += ' masonry-center-title';
36298 cls += ' masonry-bottom-title';
36305 if(!this.html.length && !this.bgimage.length){
36306 cls += ' masonry-center-title';
36309 if(!this.html.length && this.bgimage.length){
36310 cls += ' masonry-bottom-title';
36315 cls += ' ' + this.cls;
36319 tag: (this.href.length) ? 'a' : 'div',
36324 cls: 'masonry-brick-mask'
36328 cls: 'masonry-brick-paragraph',
36334 if(this.href.length){
36335 cfg.href = this.href;
36338 var cn = cfg.cn[1].cn;
36340 if(this.title.length){
36343 cls: 'masonry-brick-title',
36348 if(this.html.length){
36351 cls: 'masonry-brick-text',
36356 if (!this.title.length && !this.html.length) {
36357 cfg.cn[1].cls += ' hide';
36360 if(this.bgimage.length){
36363 cls: 'masonry-brick-image-view',
36368 if(this.videourl.length){
36369 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36370 // youtube support only?
36373 cls: 'masonry-brick-image-view',
36376 allowfullscreen : true
36384 getSplitAutoCreate : function()
36386 var cls = 'masonry-brick masonry-brick-split';
36388 if(this.href.length){
36389 cls += ' masonry-brick-link';
36392 if(this.bgimage.length){
36393 cls += ' masonry-brick-image';
36397 cls += ' masonry-' + this.size + '-brick';
36400 switch (this.placetitle) {
36402 cls += ' masonry-center-title';
36405 cls += ' masonry-bottom-title';
36408 if(!this.bgimage.length){
36409 cls += ' masonry-center-title';
36412 if(this.bgimage.length){
36413 cls += ' masonry-bottom-title';
36419 cls += ' ' + this.cls;
36423 tag: (this.href.length) ? 'a' : 'div',
36428 cls: 'masonry-brick-split-head',
36432 cls: 'masonry-brick-paragraph',
36439 cls: 'masonry-brick-split-body',
36445 if(this.href.length){
36446 cfg.href = this.href;
36449 if(this.title.length){
36450 cfg.cn[0].cn[0].cn.push({
36452 cls: 'masonry-brick-title',
36457 if(this.html.length){
36458 cfg.cn[1].cn.push({
36460 cls: 'masonry-brick-text',
36465 if(this.bgimage.length){
36466 cfg.cn[0].cn.push({
36468 cls: 'masonry-brick-image-view',
36473 if(this.videourl.length){
36474 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36475 // youtube support only?
36476 cfg.cn[0].cn.cn.push({
36478 cls: 'masonry-brick-image-view',
36481 allowfullscreen : true
36488 initEvents: function()
36490 switch (this.size) {
36523 this.el.on('touchstart', this.onTouchStart, this);
36524 this.el.on('touchmove', this.onTouchMove, this);
36525 this.el.on('touchend', this.onTouchEnd, this);
36526 this.el.on('contextmenu', this.onContextMenu, this);
36528 this.el.on('mouseenter' ,this.enter, this);
36529 this.el.on('mouseleave', this.leave, this);
36530 this.el.on('click', this.onClick, this);
36533 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36534 this.parent().bricks.push(this);
36539 onClick: function(e, el)
36541 var time = this.endTimer - this.startTimer;
36542 // Roo.log(e.preventDefault());
36545 e.preventDefault();
36550 if(!this.preventDefault){
36554 e.preventDefault();
36556 if (this.activeClass != '') {
36557 this.selectBrick();
36560 this.fireEvent('click', this, e);
36563 enter: function(e, el)
36565 e.preventDefault();
36567 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36571 if(this.bgimage.length && this.html.length){
36572 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36576 leave: function(e, el)
36578 e.preventDefault();
36580 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36584 if(this.bgimage.length && this.html.length){
36585 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36589 onTouchStart: function(e, el)
36591 // e.preventDefault();
36593 this.touchmoved = false;
36595 if(!this.isFitContainer){
36599 if(!this.bgimage.length || !this.html.length){
36603 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36605 this.timer = new Date().getTime();
36609 onTouchMove: function(e, el)
36611 this.touchmoved = true;
36614 onContextMenu : function(e,el)
36616 e.preventDefault();
36617 e.stopPropagation();
36621 onTouchEnd: function(e, el)
36623 // e.preventDefault();
36625 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36632 if(!this.bgimage.length || !this.html.length){
36634 if(this.href.length){
36635 window.location.href = this.href;
36641 if(!this.isFitContainer){
36645 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36647 window.location.href = this.href;
36650 //selection on single brick only
36651 selectBrick : function() {
36653 if (!this.parentId) {
36657 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36658 var index = m.selectedBrick.indexOf(this.id);
36661 m.selectedBrick.splice(index,1);
36662 this.el.removeClass(this.activeClass);
36666 for(var i = 0; i < m.selectedBrick.length; i++) {
36667 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36668 b.el.removeClass(b.activeClass);
36671 m.selectedBrick = [];
36673 m.selectedBrick.push(this.id);
36674 this.el.addClass(this.activeClass);
36678 isSelected : function(){
36679 return this.el.hasClass(this.activeClass);
36684 Roo.apply(Roo.bootstrap.MasonryBrick, {
36687 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36689 * register a Masonry Brick
36690 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36693 register : function(brick)
36695 //this.groups[brick.id] = brick;
36696 this.groups.add(brick.id, brick);
36699 * fetch a masonry brick based on the masonry brick ID
36700 * @param {string} the masonry brick to add
36701 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36704 get: function(brick_id)
36706 // if (typeof(this.groups[brick_id]) == 'undefined') {
36709 // return this.groups[brick_id] ;
36711 if(this.groups.key(brick_id)) {
36712 return this.groups.key(brick_id);
36730 * @class Roo.bootstrap.Brick
36731 * @extends Roo.bootstrap.Component
36732 * Bootstrap Brick class
36735 * Create a new Brick
36736 * @param {Object} config The config object
36739 Roo.bootstrap.Brick = function(config){
36740 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36746 * When a Brick is click
36747 * @param {Roo.bootstrap.Brick} this
36748 * @param {Roo.EventObject} e
36754 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36757 * @cfg {String} title
36761 * @cfg {String} html
36765 * @cfg {String} bgimage
36769 * @cfg {String} cls
36773 * @cfg {String} href
36777 * @cfg {String} video
36781 * @cfg {Boolean} square
36785 getAutoCreate : function()
36787 var cls = 'roo-brick';
36789 if(this.href.length){
36790 cls += ' roo-brick-link';
36793 if(this.bgimage.length){
36794 cls += ' roo-brick-image';
36797 if(!this.html.length && !this.bgimage.length){
36798 cls += ' roo-brick-center-title';
36801 if(!this.html.length && this.bgimage.length){
36802 cls += ' roo-brick-bottom-title';
36806 cls += ' ' + this.cls;
36810 tag: (this.href.length) ? 'a' : 'div',
36815 cls: 'roo-brick-paragraph',
36821 if(this.href.length){
36822 cfg.href = this.href;
36825 var cn = cfg.cn[0].cn;
36827 if(this.title.length){
36830 cls: 'roo-brick-title',
36835 if(this.html.length){
36838 cls: 'roo-brick-text',
36845 if(this.bgimage.length){
36848 cls: 'roo-brick-image-view',
36856 initEvents: function()
36858 if(this.title.length || this.html.length){
36859 this.el.on('mouseenter' ,this.enter, this);
36860 this.el.on('mouseleave', this.leave, this);
36863 Roo.EventManager.onWindowResize(this.resize, this);
36865 if(this.bgimage.length){
36866 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36867 this.imageEl.on('load', this.onImageLoad, this);
36874 onImageLoad : function()
36879 resize : function()
36881 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36883 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36885 if(this.bgimage.length){
36886 var image = this.el.select('.roo-brick-image-view', true).first();
36888 image.setWidth(paragraph.getWidth());
36891 image.setHeight(paragraph.getWidth());
36894 this.el.setHeight(image.getHeight());
36895 paragraph.setHeight(image.getHeight());
36901 enter: function(e, el)
36903 e.preventDefault();
36905 if(this.bgimage.length){
36906 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36907 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36911 leave: function(e, el)
36913 e.preventDefault();
36915 if(this.bgimage.length){
36916 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36917 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36932 * @class Roo.bootstrap.NumberField
36933 * @extends Roo.bootstrap.Input
36934 * Bootstrap NumberField class
36940 * Create a new NumberField
36941 * @param {Object} config The config object
36944 Roo.bootstrap.NumberField = function(config){
36945 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36948 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36951 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36953 allowDecimals : true,
36955 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36957 decimalSeparator : ".",
36959 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36961 decimalPrecision : 2,
36963 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36965 allowNegative : true,
36968 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36972 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36974 minValue : Number.NEGATIVE_INFINITY,
36976 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36978 maxValue : Number.MAX_VALUE,
36980 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36982 minText : "The minimum value for this field is {0}",
36984 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36986 maxText : "The maximum value for this field is {0}",
36988 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36989 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36991 nanText : "{0} is not a valid number",
36993 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36995 thousandsDelimiter : false,
36997 * @cfg {String} valueAlign alignment of value
36999 valueAlign : "left",
37001 getAutoCreate : function()
37003 var hiddenInput = {
37007 cls: 'hidden-number-input'
37011 hiddenInput.name = this.name;
37016 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37018 this.name = hiddenInput.name;
37020 if(cfg.cn.length > 0) {
37021 cfg.cn.push(hiddenInput);
37028 initEvents : function()
37030 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37032 var allowed = "0123456789";
37034 if(this.allowDecimals){
37035 allowed += this.decimalSeparator;
37038 if(this.allowNegative){
37042 if(this.thousandsDelimiter) {
37046 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37048 var keyPress = function(e){
37050 var k = e.getKey();
37052 var c = e.getCharCode();
37055 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37056 allowed.indexOf(String.fromCharCode(c)) === -1
37062 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37066 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37071 this.el.on("keypress", keyPress, this);
37074 validateValue : function(value)
37077 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37081 var num = this.parseValue(value);
37084 this.markInvalid(String.format(this.nanText, value));
37088 if(num < this.minValue){
37089 this.markInvalid(String.format(this.minText, this.minValue));
37093 if(num > this.maxValue){
37094 this.markInvalid(String.format(this.maxText, this.maxValue));
37101 getValue : function()
37103 var v = this.hiddenEl().getValue();
37105 return this.fixPrecision(this.parseValue(v));
37108 parseValue : function(value)
37110 if(this.thousandsDelimiter) {
37112 r = new RegExp(",", "g");
37113 value = value.replace(r, "");
37116 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37117 return isNaN(value) ? '' : value;
37120 fixPrecision : function(value)
37122 if(this.thousandsDelimiter) {
37124 r = new RegExp(",", "g");
37125 value = value.replace(r, "");
37128 var nan = isNaN(value);
37130 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37131 return nan ? '' : value;
37133 return parseFloat(value).toFixed(this.decimalPrecision);
37136 setValue : function(v)
37138 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37144 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37146 this.inputEl().dom.value = (v == '') ? '' :
37147 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37149 if(!this.allowZero && v === '0') {
37150 this.hiddenEl().dom.value = '';
37151 this.inputEl().dom.value = '';
37158 decimalPrecisionFcn : function(v)
37160 return Math.floor(v);
37163 beforeBlur : function()
37165 var v = this.parseValue(this.getRawValue());
37167 if(v || v === 0 || v === ''){
37172 hiddenEl : function()
37174 return this.el.select('input.hidden-number-input',true).first();
37186 * @class Roo.bootstrap.DocumentSlider
37187 * @extends Roo.bootstrap.Component
37188 * Bootstrap DocumentSlider class
37191 * Create a new DocumentViewer
37192 * @param {Object} config The config object
37195 Roo.bootstrap.DocumentSlider = function(config){
37196 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37203 * Fire after initEvent
37204 * @param {Roo.bootstrap.DocumentSlider} this
37209 * Fire after update
37210 * @param {Roo.bootstrap.DocumentSlider} this
37216 * @param {Roo.bootstrap.DocumentSlider} this
37222 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37228 getAutoCreate : function()
37232 cls : 'roo-document-slider',
37236 cls : 'roo-document-slider-header',
37240 cls : 'roo-document-slider-header-title'
37246 cls : 'roo-document-slider-body',
37250 cls : 'roo-document-slider-prev',
37254 cls : 'fa fa-chevron-left'
37260 cls : 'roo-document-slider-thumb',
37264 cls : 'roo-document-slider-image'
37270 cls : 'roo-document-slider-next',
37274 cls : 'fa fa-chevron-right'
37286 initEvents : function()
37288 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37289 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37291 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37292 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37294 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37295 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37297 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37298 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37300 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37301 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37303 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37304 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37306 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37307 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37309 this.thumbEl.on('click', this.onClick, this);
37311 this.prevIndicator.on('click', this.prev, this);
37313 this.nextIndicator.on('click', this.next, this);
37317 initial : function()
37319 if(this.files.length){
37320 this.indicator = 1;
37324 this.fireEvent('initial', this);
37327 update : function()
37329 this.imageEl.attr('src', this.files[this.indicator - 1]);
37331 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37333 this.prevIndicator.show();
37335 if(this.indicator == 1){
37336 this.prevIndicator.hide();
37339 this.nextIndicator.show();
37341 if(this.indicator == this.files.length){
37342 this.nextIndicator.hide();
37345 this.thumbEl.scrollTo('top');
37347 this.fireEvent('update', this);
37350 onClick : function(e)
37352 e.preventDefault();
37354 this.fireEvent('click', this);
37359 e.preventDefault();
37361 this.indicator = Math.max(1, this.indicator - 1);
37368 e.preventDefault();
37370 this.indicator = Math.min(this.files.length, this.indicator + 1);
37384 * @class Roo.bootstrap.RadioSet
37385 * @extends Roo.bootstrap.Input
37386 * Bootstrap RadioSet class
37387 * @cfg {String} indicatorpos (left|right) default left
37388 * @cfg {Boolean} inline (true|false) inline the element (default true)
37389 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37391 * Create a new RadioSet
37392 * @param {Object} config The config object
37395 Roo.bootstrap.RadioSet = function(config){
37397 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37401 Roo.bootstrap.RadioSet.register(this);
37406 * Fires when the element is checked or unchecked.
37407 * @param {Roo.bootstrap.RadioSet} this This radio
37408 * @param {Roo.bootstrap.Radio} item The checked item
37413 * Fires when the element is click.
37414 * @param {Roo.bootstrap.RadioSet} this This radio set
37415 * @param {Roo.bootstrap.Radio} item The checked item
37416 * @param {Roo.EventObject} e The event object
37423 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37431 indicatorpos : 'left',
37433 getAutoCreate : function()
37437 cls : 'roo-radio-set-label',
37441 html : this.fieldLabel
37445 if (Roo.bootstrap.version == 3) {
37448 if(this.indicatorpos == 'left'){
37451 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37452 tooltip : 'This field is required'
37457 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37458 tooltip : 'This field is required'
37464 cls : 'roo-radio-set-items'
37467 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37469 if (align === 'left' && this.fieldLabel.length) {
37472 cls : "roo-radio-set-right",
37478 if(this.labelWidth > 12){
37479 label.style = "width: " + this.labelWidth + 'px';
37482 if(this.labelWidth < 13 && this.labelmd == 0){
37483 this.labelmd = this.labelWidth;
37486 if(this.labellg > 0){
37487 label.cls += ' col-lg-' + this.labellg;
37488 items.cls += ' col-lg-' + (12 - this.labellg);
37491 if(this.labelmd > 0){
37492 label.cls += ' col-md-' + this.labelmd;
37493 items.cls += ' col-md-' + (12 - this.labelmd);
37496 if(this.labelsm > 0){
37497 label.cls += ' col-sm-' + this.labelsm;
37498 items.cls += ' col-sm-' + (12 - this.labelsm);
37501 if(this.labelxs > 0){
37502 label.cls += ' col-xs-' + this.labelxs;
37503 items.cls += ' col-xs-' + (12 - this.labelxs);
37509 cls : 'roo-radio-set',
37513 cls : 'roo-radio-set-input',
37516 value : this.value ? this.value : ''
37523 if(this.weight.length){
37524 cfg.cls += ' roo-radio-' + this.weight;
37528 cfg.cls += ' roo-radio-set-inline';
37532 ['xs','sm','md','lg'].map(function(size){
37533 if (settings[size]) {
37534 cfg.cls += ' col-' + size + '-' + settings[size];
37542 initEvents : function()
37544 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37545 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37547 if(!this.fieldLabel.length){
37548 this.labelEl.hide();
37551 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37552 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37554 this.indicator = this.indicatorEl();
37556 if(this.indicator){
37557 this.indicator.addClass('invisible');
37560 this.originalValue = this.getValue();
37564 inputEl: function ()
37566 return this.el.select('.roo-radio-set-input', true).first();
37569 getChildContainer : function()
37571 return this.itemsEl;
37574 register : function(item)
37576 this.radioes.push(item);
37580 validate : function()
37582 if(this.getVisibilityEl().hasClass('hidden')){
37588 Roo.each(this.radioes, function(i){
37597 if(this.allowBlank) {
37601 if(this.disabled || valid){
37606 this.markInvalid();
37611 markValid : function()
37613 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37614 this.indicatorEl().removeClass('visible');
37615 this.indicatorEl().addClass('invisible');
37619 if (Roo.bootstrap.version == 3) {
37620 this.el.removeClass([this.invalidClass, this.validClass]);
37621 this.el.addClass(this.validClass);
37623 this.el.removeClass(['is-invalid','is-valid']);
37624 this.el.addClass(['is-valid']);
37626 this.fireEvent('valid', this);
37629 markInvalid : function(msg)
37631 if(this.allowBlank || this.disabled){
37635 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37636 this.indicatorEl().removeClass('invisible');
37637 this.indicatorEl().addClass('visible');
37639 if (Roo.bootstrap.version == 3) {
37640 this.el.removeClass([this.invalidClass, this.validClass]);
37641 this.el.addClass(this.invalidClass);
37643 this.el.removeClass(['is-invalid','is-valid']);
37644 this.el.addClass(['is-invalid']);
37647 this.fireEvent('invalid', this, msg);
37651 setValue : function(v, suppressEvent)
37653 if(this.value === v){
37660 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37663 Roo.each(this.radioes, function(i){
37665 i.el.removeClass('checked');
37668 Roo.each(this.radioes, function(i){
37670 if(i.value === v || i.value.toString() === v.toString()){
37672 i.el.addClass('checked');
37674 if(suppressEvent !== true){
37675 this.fireEvent('check', this, i);
37686 clearInvalid : function(){
37688 if(!this.el || this.preventMark){
37692 this.el.removeClass([this.invalidClass]);
37694 this.fireEvent('valid', this);
37699 Roo.apply(Roo.bootstrap.RadioSet, {
37703 register : function(set)
37705 this.groups[set.name] = set;
37708 get: function(name)
37710 if (typeof(this.groups[name]) == 'undefined') {
37714 return this.groups[name] ;
37720 * Ext JS Library 1.1.1
37721 * Copyright(c) 2006-2007, Ext JS, LLC.
37723 * Originally Released Under LGPL - original licence link has changed is not relivant.
37726 * <script type="text/javascript">
37731 * @class Roo.bootstrap.SplitBar
37732 * @extends Roo.util.Observable
37733 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37737 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37738 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37739 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37740 split.minSize = 100;
37741 split.maxSize = 600;
37742 split.animate = true;
37743 split.on('moved', splitterMoved);
37746 * Create a new SplitBar
37747 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37748 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37749 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37750 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37751 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37752 position of the SplitBar).
37754 Roo.bootstrap.SplitBar = function(cfg){
37759 // dragElement : elm
37760 // resizingElement: el,
37762 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37763 // placement : Roo.bootstrap.SplitBar.LEFT ,
37764 // existingProxy ???
37767 this.el = Roo.get(cfg.dragElement, true);
37768 this.el.dom.unselectable = "on";
37770 this.resizingEl = Roo.get(cfg.resizingElement, true);
37774 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37775 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37778 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37781 * The minimum size of the resizing element. (Defaults to 0)
37787 * The maximum size of the resizing element. (Defaults to 2000)
37790 this.maxSize = 2000;
37793 * Whether to animate the transition to the new size
37796 this.animate = false;
37799 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37802 this.useShim = false;
37807 if(!cfg.existingProxy){
37809 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37811 this.proxy = Roo.get(cfg.existingProxy).dom;
37814 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37817 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37820 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37823 this.dragSpecs = {};
37826 * @private The adapter to use to positon and resize elements
37828 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37829 this.adapter.init(this);
37831 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37833 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37834 this.el.addClass("roo-splitbar-h");
37837 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37838 this.el.addClass("roo-splitbar-v");
37844 * Fires when the splitter is moved (alias for {@link #event-moved})
37845 * @param {Roo.bootstrap.SplitBar} this
37846 * @param {Number} newSize the new width or height
37851 * Fires when the splitter is moved
37852 * @param {Roo.bootstrap.SplitBar} this
37853 * @param {Number} newSize the new width or height
37857 * @event beforeresize
37858 * Fires before the splitter is dragged
37859 * @param {Roo.bootstrap.SplitBar} this
37861 "beforeresize" : true,
37863 "beforeapply" : true
37866 Roo.util.Observable.call(this);
37869 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37870 onStartProxyDrag : function(x, y){
37871 this.fireEvent("beforeresize", this);
37873 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37875 o.enableDisplayMode("block");
37876 // all splitbars share the same overlay
37877 Roo.bootstrap.SplitBar.prototype.overlay = o;
37879 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37880 this.overlay.show();
37881 Roo.get(this.proxy).setDisplayed("block");
37882 var size = this.adapter.getElementSize(this);
37883 this.activeMinSize = this.getMinimumSize();;
37884 this.activeMaxSize = this.getMaximumSize();;
37885 var c1 = size - this.activeMinSize;
37886 var c2 = Math.max(this.activeMaxSize - size, 0);
37887 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37888 this.dd.resetConstraints();
37889 this.dd.setXConstraint(
37890 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37891 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37893 this.dd.setYConstraint(0, 0);
37895 this.dd.resetConstraints();
37896 this.dd.setXConstraint(0, 0);
37897 this.dd.setYConstraint(
37898 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37899 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37902 this.dragSpecs.startSize = size;
37903 this.dragSpecs.startPoint = [x, y];
37904 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37908 * @private Called after the drag operation by the DDProxy
37910 onEndProxyDrag : function(e){
37911 Roo.get(this.proxy).setDisplayed(false);
37912 var endPoint = Roo.lib.Event.getXY(e);
37914 this.overlay.hide();
37917 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37918 newSize = this.dragSpecs.startSize +
37919 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37920 endPoint[0] - this.dragSpecs.startPoint[0] :
37921 this.dragSpecs.startPoint[0] - endPoint[0]
37924 newSize = this.dragSpecs.startSize +
37925 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37926 endPoint[1] - this.dragSpecs.startPoint[1] :
37927 this.dragSpecs.startPoint[1] - endPoint[1]
37930 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37931 if(newSize != this.dragSpecs.startSize){
37932 if(this.fireEvent('beforeapply', this, newSize) !== false){
37933 this.adapter.setElementSize(this, newSize);
37934 this.fireEvent("moved", this, newSize);
37935 this.fireEvent("resize", this, newSize);
37941 * Get the adapter this SplitBar uses
37942 * @return The adapter object
37944 getAdapter : function(){
37945 return this.adapter;
37949 * Set the adapter this SplitBar uses
37950 * @param {Object} adapter A SplitBar adapter object
37952 setAdapter : function(adapter){
37953 this.adapter = adapter;
37954 this.adapter.init(this);
37958 * Gets the minimum size for the resizing element
37959 * @return {Number} The minimum size
37961 getMinimumSize : function(){
37962 return this.minSize;
37966 * Sets the minimum size for the resizing element
37967 * @param {Number} minSize The minimum size
37969 setMinimumSize : function(minSize){
37970 this.minSize = minSize;
37974 * Gets the maximum size for the resizing element
37975 * @return {Number} The maximum size
37977 getMaximumSize : function(){
37978 return this.maxSize;
37982 * Sets the maximum size for the resizing element
37983 * @param {Number} maxSize The maximum size
37985 setMaximumSize : function(maxSize){
37986 this.maxSize = maxSize;
37990 * Sets the initialize size for the resizing element
37991 * @param {Number} size The initial size
37993 setCurrentSize : function(size){
37994 var oldAnimate = this.animate;
37995 this.animate = false;
37996 this.adapter.setElementSize(this, size);
37997 this.animate = oldAnimate;
38001 * Destroy this splitbar.
38002 * @param {Boolean} removeEl True to remove the element
38004 destroy : function(removeEl){
38006 this.shim.remove();
38009 this.proxy.parentNode.removeChild(this.proxy);
38017 * @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.
38019 Roo.bootstrap.SplitBar.createProxy = function(dir){
38020 var proxy = new Roo.Element(document.createElement("div"));
38021 proxy.unselectable();
38022 var cls = 'roo-splitbar-proxy';
38023 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38024 document.body.appendChild(proxy.dom);
38029 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38030 * Default Adapter. It assumes the splitter and resizing element are not positioned
38031 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38033 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38036 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38037 // do nothing for now
38038 init : function(s){
38042 * Called before drag operations to get the current size of the resizing element.
38043 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38045 getElementSize : function(s){
38046 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38047 return s.resizingEl.getWidth();
38049 return s.resizingEl.getHeight();
38054 * Called after drag operations to set the size of the resizing element.
38055 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38056 * @param {Number} newSize The new size to set
38057 * @param {Function} onComplete A function to be invoked when resizing is complete
38059 setElementSize : function(s, newSize, onComplete){
38060 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38062 s.resizingEl.setWidth(newSize);
38064 onComplete(s, newSize);
38067 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38072 s.resizingEl.setHeight(newSize);
38074 onComplete(s, newSize);
38077 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38084 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38085 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38086 * Adapter that moves the splitter element to align with the resized sizing element.
38087 * Used with an absolute positioned SplitBar.
38088 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38089 * document.body, make sure you assign an id to the body element.
38091 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38092 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38093 this.container = Roo.get(container);
38096 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38097 init : function(s){
38098 this.basic.init(s);
38101 getElementSize : function(s){
38102 return this.basic.getElementSize(s);
38105 setElementSize : function(s, newSize, onComplete){
38106 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38109 moveSplitter : function(s){
38110 var yes = Roo.bootstrap.SplitBar;
38111 switch(s.placement){
38113 s.el.setX(s.resizingEl.getRight());
38116 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38119 s.el.setY(s.resizingEl.getBottom());
38122 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38129 * Orientation constant - Create a vertical SplitBar
38133 Roo.bootstrap.SplitBar.VERTICAL = 1;
38136 * Orientation constant - Create a horizontal SplitBar
38140 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38143 * Placement constant - The resizing element is to the left of the splitter element
38147 Roo.bootstrap.SplitBar.LEFT = 1;
38150 * Placement constant - The resizing element is to the right of the splitter element
38154 Roo.bootstrap.SplitBar.RIGHT = 2;
38157 * Placement constant - The resizing element is positioned above the splitter element
38161 Roo.bootstrap.SplitBar.TOP = 3;
38164 * Placement constant - The resizing element is positioned under splitter element
38168 Roo.bootstrap.SplitBar.BOTTOM = 4;
38169 Roo.namespace("Roo.bootstrap.layout");/*
38171 * Ext JS Library 1.1.1
38172 * Copyright(c) 2006-2007, Ext JS, LLC.
38174 * Originally Released Under LGPL - original licence link has changed is not relivant.
38177 * <script type="text/javascript">
38181 * @class Roo.bootstrap.layout.Manager
38182 * @extends Roo.bootstrap.Component
38183 * Base class for layout managers.
38185 Roo.bootstrap.layout.Manager = function(config)
38187 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38193 /** false to disable window resize monitoring @type Boolean */
38194 this.monitorWindowResize = true;
38199 * Fires when a layout is performed.
38200 * @param {Roo.LayoutManager} this
38204 * @event regionresized
38205 * Fires when the user resizes a region.
38206 * @param {Roo.LayoutRegion} region The resized region
38207 * @param {Number} newSize The new size (width for east/west, height for north/south)
38209 "regionresized" : true,
38211 * @event regioncollapsed
38212 * Fires when a region is collapsed.
38213 * @param {Roo.LayoutRegion} region The collapsed region
38215 "regioncollapsed" : true,
38217 * @event regionexpanded
38218 * Fires when a region is expanded.
38219 * @param {Roo.LayoutRegion} region The expanded region
38221 "regionexpanded" : true
38223 this.updating = false;
38226 this.el = Roo.get(config.el);
38232 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38237 monitorWindowResize : true,
38243 onRender : function(ct, position)
38246 this.el = Roo.get(ct);
38249 //this.fireEvent('render',this);
38253 initEvents: function()
38257 // ie scrollbar fix
38258 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38259 document.body.scroll = "no";
38260 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38261 this.el.position('relative');
38263 this.id = this.el.id;
38264 this.el.addClass("roo-layout-container");
38265 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38266 if(this.el.dom != document.body ) {
38267 this.el.on('resize', this.layout,this);
38268 this.el.on('show', this.layout,this);
38274 * Returns true if this layout is currently being updated
38275 * @return {Boolean}
38277 isUpdating : function(){
38278 return this.updating;
38282 * Suspend the LayoutManager from doing auto-layouts while
38283 * making multiple add or remove calls
38285 beginUpdate : function(){
38286 this.updating = true;
38290 * Restore auto-layouts and optionally disable the manager from performing a layout
38291 * @param {Boolean} noLayout true to disable a layout update
38293 endUpdate : function(noLayout){
38294 this.updating = false;
38300 layout: function(){
38304 onRegionResized : function(region, newSize){
38305 this.fireEvent("regionresized", region, newSize);
38309 onRegionCollapsed : function(region){
38310 this.fireEvent("regioncollapsed", region);
38313 onRegionExpanded : function(region){
38314 this.fireEvent("regionexpanded", region);
38318 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38319 * performs box-model adjustments.
38320 * @return {Object} The size as an object {width: (the width), height: (the height)}
38322 getViewSize : function()
38325 if(this.el.dom != document.body){
38326 size = this.el.getSize();
38328 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38330 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38331 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38336 * Returns the Element this layout is bound to.
38337 * @return {Roo.Element}
38339 getEl : function(){
38344 * Returns the specified region.
38345 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38346 * @return {Roo.LayoutRegion}
38348 getRegion : function(target){
38349 return this.regions[target.toLowerCase()];
38352 onWindowResize : function(){
38353 if(this.monitorWindowResize){
38360 * Ext JS Library 1.1.1
38361 * Copyright(c) 2006-2007, Ext JS, LLC.
38363 * Originally Released Under LGPL - original licence link has changed is not relivant.
38366 * <script type="text/javascript">
38369 * @class Roo.bootstrap.layout.Border
38370 * @extends Roo.bootstrap.layout.Manager
38371 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38372 * please see: examples/bootstrap/nested.html<br><br>
38374 <b>The container the layout is rendered into can be either the body element or any other element.
38375 If it is not the body element, the container needs to either be an absolute positioned element,
38376 or you will need to add "position:relative" to the css of the container. You will also need to specify
38377 the container size if it is not the body element.</b>
38380 * Create a new Border
38381 * @param {Object} config Configuration options
38383 Roo.bootstrap.layout.Border = function(config){
38384 config = config || {};
38385 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38389 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38390 if(config[region]){
38391 config[region].region = region;
38392 this.addRegion(config[region]);
38398 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38400 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38402 parent : false, // this might point to a 'nest' or a ???
38405 * Creates and adds a new region if it doesn't already exist.
38406 * @param {String} target The target region key (north, south, east, west or center).
38407 * @param {Object} config The regions config object
38408 * @return {BorderLayoutRegion} The new region
38410 addRegion : function(config)
38412 if(!this.regions[config.region]){
38413 var r = this.factory(config);
38414 this.bindRegion(r);
38416 return this.regions[config.region];
38420 bindRegion : function(r){
38421 this.regions[r.config.region] = r;
38423 r.on("visibilitychange", this.layout, this);
38424 r.on("paneladded", this.layout, this);
38425 r.on("panelremoved", this.layout, this);
38426 r.on("invalidated", this.layout, this);
38427 r.on("resized", this.onRegionResized, this);
38428 r.on("collapsed", this.onRegionCollapsed, this);
38429 r.on("expanded", this.onRegionExpanded, this);
38433 * Performs a layout update.
38435 layout : function()
38437 if(this.updating) {
38441 // render all the rebions if they have not been done alreayd?
38442 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38443 if(this.regions[region] && !this.regions[region].bodyEl){
38444 this.regions[region].onRender(this.el)
38448 var size = this.getViewSize();
38449 var w = size.width;
38450 var h = size.height;
38455 //var x = 0, y = 0;
38457 var rs = this.regions;
38458 var north = rs["north"];
38459 var south = rs["south"];
38460 var west = rs["west"];
38461 var east = rs["east"];
38462 var center = rs["center"];
38463 //if(this.hideOnLayout){ // not supported anymore
38464 //c.el.setStyle("display", "none");
38466 if(north && north.isVisible()){
38467 var b = north.getBox();
38468 var m = north.getMargins();
38469 b.width = w - (m.left+m.right);
38472 centerY = b.height + b.y + m.bottom;
38473 centerH -= centerY;
38474 north.updateBox(this.safeBox(b));
38476 if(south && south.isVisible()){
38477 var b = south.getBox();
38478 var m = south.getMargins();
38479 b.width = w - (m.left+m.right);
38481 var totalHeight = (b.height + m.top + m.bottom);
38482 b.y = h - totalHeight + m.top;
38483 centerH -= totalHeight;
38484 south.updateBox(this.safeBox(b));
38486 if(west && west.isVisible()){
38487 var b = west.getBox();
38488 var m = west.getMargins();
38489 b.height = centerH - (m.top+m.bottom);
38491 b.y = centerY + m.top;
38492 var totalWidth = (b.width + m.left + m.right);
38493 centerX += totalWidth;
38494 centerW -= totalWidth;
38495 west.updateBox(this.safeBox(b));
38497 if(east && east.isVisible()){
38498 var b = east.getBox();
38499 var m = east.getMargins();
38500 b.height = centerH - (m.top+m.bottom);
38501 var totalWidth = (b.width + m.left + m.right);
38502 b.x = w - totalWidth + m.left;
38503 b.y = centerY + m.top;
38504 centerW -= totalWidth;
38505 east.updateBox(this.safeBox(b));
38508 var m = center.getMargins();
38510 x: centerX + m.left,
38511 y: centerY + m.top,
38512 width: centerW - (m.left+m.right),
38513 height: centerH - (m.top+m.bottom)
38515 //if(this.hideOnLayout){
38516 //center.el.setStyle("display", "block");
38518 center.updateBox(this.safeBox(centerBox));
38521 this.fireEvent("layout", this);
38525 safeBox : function(box){
38526 box.width = Math.max(0, box.width);
38527 box.height = Math.max(0, box.height);
38532 * Adds a ContentPanel (or subclass) to this layout.
38533 * @param {String} target The target region key (north, south, east, west or center).
38534 * @param {Roo.ContentPanel} panel The panel to add
38535 * @return {Roo.ContentPanel} The added panel
38537 add : function(target, panel){
38539 target = target.toLowerCase();
38540 return this.regions[target].add(panel);
38544 * Remove a ContentPanel (or subclass) to this layout.
38545 * @param {String} target The target region key (north, south, east, west or center).
38546 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38547 * @return {Roo.ContentPanel} The removed panel
38549 remove : function(target, panel){
38550 target = target.toLowerCase();
38551 return this.regions[target].remove(panel);
38555 * Searches all regions for a panel with the specified id
38556 * @param {String} panelId
38557 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38559 findPanel : function(panelId){
38560 var rs = this.regions;
38561 for(var target in rs){
38562 if(typeof rs[target] != "function"){
38563 var p = rs[target].getPanel(panelId);
38573 * Searches all regions for a panel with the specified id and activates (shows) it.
38574 * @param {String/ContentPanel} panelId The panels id or the panel itself
38575 * @return {Roo.ContentPanel} The shown panel or null
38577 showPanel : function(panelId) {
38578 var rs = this.regions;
38579 for(var target in rs){
38580 var r = rs[target];
38581 if(typeof r != "function"){
38582 if(r.hasPanel(panelId)){
38583 return r.showPanel(panelId);
38591 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38592 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38595 restoreState : function(provider){
38597 provider = Roo.state.Manager;
38599 var sm = new Roo.LayoutStateManager();
38600 sm.init(this, provider);
38606 * Adds a xtype elements to the layout.
38610 xtype : 'ContentPanel',
38617 xtype : 'NestedLayoutPanel',
38623 items : [ ... list of content panels or nested layout panels.. ]
38627 * @param {Object} cfg Xtype definition of item to add.
38629 addxtype : function(cfg)
38631 // basically accepts a pannel...
38632 // can accept a layout region..!?!?
38633 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38636 // theory? children can only be panels??
38638 //if (!cfg.xtype.match(/Panel$/)) {
38643 if (typeof(cfg.region) == 'undefined') {
38644 Roo.log("Failed to add Panel, region was not set");
38648 var region = cfg.region;
38654 xitems = cfg.items;
38659 if ( region == 'center') {
38660 Roo.log("Center: " + cfg.title);
38666 case 'Content': // ContentPanel (el, cfg)
38667 case 'Scroll': // ContentPanel (el, cfg)
38669 cfg.autoCreate = cfg.autoCreate || true;
38670 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38672 // var el = this.el.createChild();
38673 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38676 this.add(region, ret);
38680 case 'TreePanel': // our new panel!
38681 cfg.el = this.el.createChild();
38682 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38683 this.add(region, ret);
38688 // create a new Layout (which is a Border Layout...
38690 var clayout = cfg.layout;
38691 clayout.el = this.el.createChild();
38692 clayout.items = clayout.items || [];
38696 // replace this exitems with the clayout ones..
38697 xitems = clayout.items;
38699 // force background off if it's in center...
38700 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38701 cfg.background = false;
38703 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38706 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38707 //console.log('adding nested layout panel ' + cfg.toSource());
38708 this.add(region, ret);
38709 nb = {}; /// find first...
38714 // needs grid and region
38716 //var el = this.getRegion(region).el.createChild();
38718 *var el = this.el.createChild();
38719 // create the grid first...
38720 cfg.grid.container = el;
38721 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38724 if (region == 'center' && this.active ) {
38725 cfg.background = false;
38728 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38730 this.add(region, ret);
38732 if (cfg.background) {
38733 // render grid on panel activation (if panel background)
38734 ret.on('activate', function(gp) {
38735 if (!gp.grid.rendered) {
38736 // gp.grid.render(el);
38740 // cfg.grid.render(el);
38746 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38747 // it was the old xcomponent building that caused this before.
38748 // espeically if border is the top element in the tree.
38758 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38760 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38761 this.add(region, ret);
38765 throw "Can not add '" + cfg.xtype + "' to Border";
38771 this.beginUpdate();
38775 Roo.each(xitems, function(i) {
38776 region = nb && i.region ? i.region : false;
38778 var add = ret.addxtype(i);
38781 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38782 if (!i.background) {
38783 abn[region] = nb[region] ;
38790 // make the last non-background panel active..
38791 //if (nb) { Roo.log(abn); }
38794 for(var r in abn) {
38795 region = this.getRegion(r);
38797 // tried using nb[r], but it does not work..
38799 region.showPanel(abn[r]);
38810 factory : function(cfg)
38813 var validRegions = Roo.bootstrap.layout.Border.regions;
38815 var target = cfg.region;
38818 var r = Roo.bootstrap.layout;
38822 return new r.North(cfg);
38824 return new r.South(cfg);
38826 return new r.East(cfg);
38828 return new r.West(cfg);
38830 return new r.Center(cfg);
38832 throw 'Layout region "'+target+'" not supported.';
38839 * Ext JS Library 1.1.1
38840 * Copyright(c) 2006-2007, Ext JS, LLC.
38842 * Originally Released Under LGPL - original licence link has changed is not relivant.
38845 * <script type="text/javascript">
38849 * @class Roo.bootstrap.layout.Basic
38850 * @extends Roo.util.Observable
38851 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38852 * and does not have a titlebar, tabs or any other features. All it does is size and position
38853 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38854 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38855 * @cfg {string} region the region that it inhabits..
38856 * @cfg {bool} skipConfig skip config?
38860 Roo.bootstrap.layout.Basic = function(config){
38862 this.mgr = config.mgr;
38864 this.position = config.region;
38866 var skipConfig = config.skipConfig;
38870 * @scope Roo.BasicLayoutRegion
38874 * @event beforeremove
38875 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38876 * @param {Roo.LayoutRegion} this
38877 * @param {Roo.ContentPanel} panel The panel
38878 * @param {Object} e The cancel event object
38880 "beforeremove" : true,
38882 * @event invalidated
38883 * Fires when the layout for this region is changed.
38884 * @param {Roo.LayoutRegion} this
38886 "invalidated" : true,
38888 * @event visibilitychange
38889 * Fires when this region is shown or hidden
38890 * @param {Roo.LayoutRegion} this
38891 * @param {Boolean} visibility true or false
38893 "visibilitychange" : true,
38895 * @event paneladded
38896 * Fires when a panel is added.
38897 * @param {Roo.LayoutRegion} this
38898 * @param {Roo.ContentPanel} panel The panel
38900 "paneladded" : true,
38902 * @event panelremoved
38903 * Fires when a panel is removed.
38904 * @param {Roo.LayoutRegion} this
38905 * @param {Roo.ContentPanel} panel The panel
38907 "panelremoved" : true,
38909 * @event beforecollapse
38910 * Fires when this region before collapse.
38911 * @param {Roo.LayoutRegion} this
38913 "beforecollapse" : true,
38916 * Fires when this region is collapsed.
38917 * @param {Roo.LayoutRegion} this
38919 "collapsed" : true,
38922 * Fires when this region is expanded.
38923 * @param {Roo.LayoutRegion} this
38928 * Fires when this region is slid into view.
38929 * @param {Roo.LayoutRegion} this
38931 "slideshow" : true,
38934 * Fires when this region slides out of view.
38935 * @param {Roo.LayoutRegion} this
38937 "slidehide" : true,
38939 * @event panelactivated
38940 * Fires when a panel is activated.
38941 * @param {Roo.LayoutRegion} this
38942 * @param {Roo.ContentPanel} panel The activated panel
38944 "panelactivated" : true,
38947 * Fires when the user resizes this region.
38948 * @param {Roo.LayoutRegion} this
38949 * @param {Number} newSize The new size (width for east/west, height for north/south)
38953 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38954 this.panels = new Roo.util.MixedCollection();
38955 this.panels.getKey = this.getPanelId.createDelegate(this);
38957 this.activePanel = null;
38958 // ensure listeners are added...
38960 if (config.listeners || config.events) {
38961 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38962 listeners : config.listeners || {},
38963 events : config.events || {}
38967 if(skipConfig !== true){
38968 this.applyConfig(config);
38972 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38974 getPanelId : function(p){
38978 applyConfig : function(config){
38979 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38980 this.config = config;
38985 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38986 * the width, for horizontal (north, south) the height.
38987 * @param {Number} newSize The new width or height
38989 resizeTo : function(newSize){
38990 var el = this.el ? this.el :
38991 (this.activePanel ? this.activePanel.getEl() : null);
38993 switch(this.position){
38996 el.setWidth(newSize);
38997 this.fireEvent("resized", this, newSize);
39001 el.setHeight(newSize);
39002 this.fireEvent("resized", this, newSize);
39008 getBox : function(){
39009 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39012 getMargins : function(){
39013 return this.margins;
39016 updateBox : function(box){
39018 var el = this.activePanel.getEl();
39019 el.dom.style.left = box.x + "px";
39020 el.dom.style.top = box.y + "px";
39021 this.activePanel.setSize(box.width, box.height);
39025 * Returns the container element for this region.
39026 * @return {Roo.Element}
39028 getEl : function(){
39029 return this.activePanel;
39033 * Returns true if this region is currently visible.
39034 * @return {Boolean}
39036 isVisible : function(){
39037 return this.activePanel ? true : false;
39040 setActivePanel : function(panel){
39041 panel = this.getPanel(panel);
39042 if(this.activePanel && this.activePanel != panel){
39043 this.activePanel.setActiveState(false);
39044 this.activePanel.getEl().setLeftTop(-10000,-10000);
39046 this.activePanel = panel;
39047 panel.setActiveState(true);
39049 panel.setSize(this.box.width, this.box.height);
39051 this.fireEvent("panelactivated", this, panel);
39052 this.fireEvent("invalidated");
39056 * Show the specified panel.
39057 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39058 * @return {Roo.ContentPanel} The shown panel or null
39060 showPanel : function(panel){
39061 panel = this.getPanel(panel);
39063 this.setActivePanel(panel);
39069 * Get the active panel for this region.
39070 * @return {Roo.ContentPanel} The active panel or null
39072 getActivePanel : function(){
39073 return this.activePanel;
39077 * Add the passed ContentPanel(s)
39078 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39079 * @return {Roo.ContentPanel} The panel added (if only one was added)
39081 add : function(panel){
39082 if(arguments.length > 1){
39083 for(var i = 0, len = arguments.length; i < len; i++) {
39084 this.add(arguments[i]);
39088 if(this.hasPanel(panel)){
39089 this.showPanel(panel);
39092 var el = panel.getEl();
39093 if(el.dom.parentNode != this.mgr.el.dom){
39094 this.mgr.el.dom.appendChild(el.dom);
39096 if(panel.setRegion){
39097 panel.setRegion(this);
39099 this.panels.add(panel);
39100 el.setStyle("position", "absolute");
39101 if(!panel.background){
39102 this.setActivePanel(panel);
39103 if(this.config.initialSize && this.panels.getCount()==1){
39104 this.resizeTo(this.config.initialSize);
39107 this.fireEvent("paneladded", this, panel);
39112 * Returns true if the panel is in this region.
39113 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39114 * @return {Boolean}
39116 hasPanel : function(panel){
39117 if(typeof panel == "object"){ // must be panel obj
39118 panel = panel.getId();
39120 return this.getPanel(panel) ? true : false;
39124 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39125 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39126 * @param {Boolean} preservePanel Overrides the config preservePanel option
39127 * @return {Roo.ContentPanel} The panel that was removed
39129 remove : function(panel, preservePanel){
39130 panel = this.getPanel(panel);
39135 this.fireEvent("beforeremove", this, panel, e);
39136 if(e.cancel === true){
39139 var panelId = panel.getId();
39140 this.panels.removeKey(panelId);
39145 * Returns the panel specified or null if it's not in this region.
39146 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39147 * @return {Roo.ContentPanel}
39149 getPanel : function(id){
39150 if(typeof id == "object"){ // must be panel obj
39153 return this.panels.get(id);
39157 * Returns this regions position (north/south/east/west/center).
39160 getPosition: function(){
39161 return this.position;
39165 * Ext JS Library 1.1.1
39166 * Copyright(c) 2006-2007, Ext JS, LLC.
39168 * Originally Released Under LGPL - original licence link has changed is not relivant.
39171 * <script type="text/javascript">
39175 * @class Roo.bootstrap.layout.Region
39176 * @extends Roo.bootstrap.layout.Basic
39177 * This class represents a region in a layout manager.
39179 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39180 * @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})
39181 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39182 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39183 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39184 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39185 * @cfg {String} title The title for the region (overrides panel titles)
39186 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39187 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39188 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39189 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39190 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39191 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39192 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39193 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39194 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39195 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39197 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39198 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39199 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39200 * @cfg {Number} width For East/West panels
39201 * @cfg {Number} height For North/South panels
39202 * @cfg {Boolean} split To show the splitter
39203 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39205 * @cfg {string} cls Extra CSS classes to add to region
39207 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39208 * @cfg {string} region the region that it inhabits..
39211 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39212 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39214 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39215 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39216 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39218 Roo.bootstrap.layout.Region = function(config)
39220 this.applyConfig(config);
39222 var mgr = config.mgr;
39223 var pos = config.region;
39224 config.skipConfig = true;
39225 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39228 this.onRender(mgr.el);
39231 this.visible = true;
39232 this.collapsed = false;
39233 this.unrendered_panels = [];
39236 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39238 position: '', // set by wrapper (eg. north/south etc..)
39239 unrendered_panels : null, // unrendered panels.
39241 tabPosition : false,
39243 mgr: false, // points to 'Border'
39246 createBody : function(){
39247 /** This region's body element
39248 * @type Roo.Element */
39249 this.bodyEl = this.el.createChild({
39251 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39255 onRender: function(ctr, pos)
39257 var dh = Roo.DomHelper;
39258 /** This region's container element
39259 * @type Roo.Element */
39260 this.el = dh.append(ctr.dom, {
39262 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39264 /** This region's title element
39265 * @type Roo.Element */
39267 this.titleEl = dh.append(this.el.dom, {
39269 unselectable: "on",
39270 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39272 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39273 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39277 this.titleEl.enableDisplayMode();
39278 /** This region's title text element
39279 * @type HTMLElement */
39280 this.titleTextEl = this.titleEl.dom.firstChild;
39281 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39283 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39284 this.closeBtn.enableDisplayMode();
39285 this.closeBtn.on("click", this.closeClicked, this);
39286 this.closeBtn.hide();
39288 this.createBody(this.config);
39289 if(this.config.hideWhenEmpty){
39291 this.on("paneladded", this.validateVisibility, this);
39292 this.on("panelremoved", this.validateVisibility, this);
39294 if(this.autoScroll){
39295 this.bodyEl.setStyle("overflow", "auto");
39297 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39299 //if(c.titlebar !== false){
39300 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39301 this.titleEl.hide();
39303 this.titleEl.show();
39304 if(this.config.title){
39305 this.titleTextEl.innerHTML = this.config.title;
39309 if(this.config.collapsed){
39310 this.collapse(true);
39312 if(this.config.hidden){
39316 if (this.unrendered_panels && this.unrendered_panels.length) {
39317 for (var i =0;i< this.unrendered_panels.length; i++) {
39318 this.add(this.unrendered_panels[i]);
39320 this.unrendered_panels = null;
39326 applyConfig : function(c)
39329 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39330 var dh = Roo.DomHelper;
39331 if(c.titlebar !== false){
39332 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39333 this.collapseBtn.on("click", this.collapse, this);
39334 this.collapseBtn.enableDisplayMode();
39336 if(c.showPin === true || this.showPin){
39337 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39338 this.stickBtn.enableDisplayMode();
39339 this.stickBtn.on("click", this.expand, this);
39340 this.stickBtn.hide();
39345 /** This region's collapsed element
39346 * @type Roo.Element */
39349 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39350 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39353 if(c.floatable !== false){
39354 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39355 this.collapsedEl.on("click", this.collapseClick, this);
39358 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39359 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39360 id: "message", unselectable: "on", style:{"float":"left"}});
39361 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39363 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39364 this.expandBtn.on("click", this.expand, this);
39368 if(this.collapseBtn){
39369 this.collapseBtn.setVisible(c.collapsible == true);
39372 this.cmargins = c.cmargins || this.cmargins ||
39373 (this.position == "west" || this.position == "east" ?
39374 {top: 0, left: 2, right:2, bottom: 0} :
39375 {top: 2, left: 0, right:0, bottom: 2});
39377 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39380 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39382 this.autoScroll = c.autoScroll || false;
39387 this.duration = c.duration || .30;
39388 this.slideDuration = c.slideDuration || .45;
39393 * Returns true if this region is currently visible.
39394 * @return {Boolean}
39396 isVisible : function(){
39397 return this.visible;
39401 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39402 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39404 //setCollapsedTitle : function(title){
39405 // title = title || " ";
39406 // if(this.collapsedTitleTextEl){
39407 // this.collapsedTitleTextEl.innerHTML = title;
39411 getBox : function(){
39413 // if(!this.collapsed){
39414 b = this.el.getBox(false, true);
39416 // b = this.collapsedEl.getBox(false, true);
39421 getMargins : function(){
39422 return this.margins;
39423 //return this.collapsed ? this.cmargins : this.margins;
39426 highlight : function(){
39427 this.el.addClass("x-layout-panel-dragover");
39430 unhighlight : function(){
39431 this.el.removeClass("x-layout-panel-dragover");
39434 updateBox : function(box)
39436 if (!this.bodyEl) {
39437 return; // not rendered yet..
39441 if(!this.collapsed){
39442 this.el.dom.style.left = box.x + "px";
39443 this.el.dom.style.top = box.y + "px";
39444 this.updateBody(box.width, box.height);
39446 this.collapsedEl.dom.style.left = box.x + "px";
39447 this.collapsedEl.dom.style.top = box.y + "px";
39448 this.collapsedEl.setSize(box.width, box.height);
39451 this.tabs.autoSizeTabs();
39455 updateBody : function(w, h)
39458 this.el.setWidth(w);
39459 w -= this.el.getBorderWidth("rl");
39460 if(this.config.adjustments){
39461 w += this.config.adjustments[0];
39464 if(h !== null && h > 0){
39465 this.el.setHeight(h);
39466 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39467 h -= this.el.getBorderWidth("tb");
39468 if(this.config.adjustments){
39469 h += this.config.adjustments[1];
39471 this.bodyEl.setHeight(h);
39473 h = this.tabs.syncHeight(h);
39476 if(this.panelSize){
39477 w = w !== null ? w : this.panelSize.width;
39478 h = h !== null ? h : this.panelSize.height;
39480 if(this.activePanel){
39481 var el = this.activePanel.getEl();
39482 w = w !== null ? w : el.getWidth();
39483 h = h !== null ? h : el.getHeight();
39484 this.panelSize = {width: w, height: h};
39485 this.activePanel.setSize(w, h);
39487 if(Roo.isIE && this.tabs){
39488 this.tabs.el.repaint();
39493 * Returns the container element for this region.
39494 * @return {Roo.Element}
39496 getEl : function(){
39501 * Hides this region.
39504 //if(!this.collapsed){
39505 this.el.dom.style.left = "-2000px";
39508 // this.collapsedEl.dom.style.left = "-2000px";
39509 // this.collapsedEl.hide();
39511 this.visible = false;
39512 this.fireEvent("visibilitychange", this, false);
39516 * Shows this region if it was previously hidden.
39519 //if(!this.collapsed){
39522 // this.collapsedEl.show();
39524 this.visible = true;
39525 this.fireEvent("visibilitychange", this, true);
39528 closeClicked : function(){
39529 if(this.activePanel){
39530 this.remove(this.activePanel);
39534 collapseClick : function(e){
39536 e.stopPropagation();
39539 e.stopPropagation();
39545 * Collapses this region.
39546 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39549 collapse : function(skipAnim, skipCheck = false){
39550 if(this.collapsed) {
39554 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39556 this.collapsed = true;
39558 this.split.el.hide();
39560 if(this.config.animate && skipAnim !== true){
39561 this.fireEvent("invalidated", this);
39562 this.animateCollapse();
39564 this.el.setLocation(-20000,-20000);
39566 this.collapsedEl.show();
39567 this.fireEvent("collapsed", this);
39568 this.fireEvent("invalidated", this);
39574 animateCollapse : function(){
39579 * Expands this region if it was previously collapsed.
39580 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39581 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39584 expand : function(e, skipAnim){
39586 e.stopPropagation();
39588 if(!this.collapsed || this.el.hasActiveFx()) {
39592 this.afterSlideIn();
39595 this.collapsed = false;
39596 if(this.config.animate && skipAnim !== true){
39597 this.animateExpand();
39601 this.split.el.show();
39603 this.collapsedEl.setLocation(-2000,-2000);
39604 this.collapsedEl.hide();
39605 this.fireEvent("invalidated", this);
39606 this.fireEvent("expanded", this);
39610 animateExpand : function(){
39614 initTabs : function()
39616 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39618 var ts = new Roo.bootstrap.panel.Tabs({
39619 el: this.bodyEl.dom,
39621 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39622 disableTooltips: this.config.disableTabTips,
39623 toolbar : this.config.toolbar
39626 if(this.config.hideTabs){
39627 ts.stripWrap.setDisplayed(false);
39630 ts.resizeTabs = this.config.resizeTabs === true;
39631 ts.minTabWidth = this.config.minTabWidth || 40;
39632 ts.maxTabWidth = this.config.maxTabWidth || 250;
39633 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39634 ts.monitorResize = false;
39635 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39636 ts.bodyEl.addClass('roo-layout-tabs-body');
39637 this.panels.each(this.initPanelAsTab, this);
39640 initPanelAsTab : function(panel){
39641 var ti = this.tabs.addTab(
39645 this.config.closeOnTab && panel.isClosable(),
39648 if(panel.tabTip !== undefined){
39649 ti.setTooltip(panel.tabTip);
39651 ti.on("activate", function(){
39652 this.setActivePanel(panel);
39655 if(this.config.closeOnTab){
39656 ti.on("beforeclose", function(t, e){
39658 this.remove(panel);
39662 panel.tabItem = ti;
39667 updatePanelTitle : function(panel, title)
39669 if(this.activePanel == panel){
39670 this.updateTitle(title);
39673 var ti = this.tabs.getTab(panel.getEl().id);
39675 if(panel.tabTip !== undefined){
39676 ti.setTooltip(panel.tabTip);
39681 updateTitle : function(title){
39682 if(this.titleTextEl && !this.config.title){
39683 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39687 setActivePanel : function(panel)
39689 panel = this.getPanel(panel);
39690 if(this.activePanel && this.activePanel != panel){
39691 if(this.activePanel.setActiveState(false) === false){
39695 this.activePanel = panel;
39696 panel.setActiveState(true);
39697 if(this.panelSize){
39698 panel.setSize(this.panelSize.width, this.panelSize.height);
39701 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39703 this.updateTitle(panel.getTitle());
39705 this.fireEvent("invalidated", this);
39707 this.fireEvent("panelactivated", this, panel);
39711 * Shows the specified panel.
39712 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39713 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39715 showPanel : function(panel)
39717 panel = this.getPanel(panel);
39720 var tab = this.tabs.getTab(panel.getEl().id);
39721 if(tab.isHidden()){
39722 this.tabs.unhideTab(tab.id);
39726 this.setActivePanel(panel);
39733 * Get the active panel for this region.
39734 * @return {Roo.ContentPanel} The active panel or null
39736 getActivePanel : function(){
39737 return this.activePanel;
39740 validateVisibility : function(){
39741 if(this.panels.getCount() < 1){
39742 this.updateTitle(" ");
39743 this.closeBtn.hide();
39746 if(!this.isVisible()){
39753 * Adds the passed ContentPanel(s) to this region.
39754 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39755 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39757 add : function(panel)
39759 if(arguments.length > 1){
39760 for(var i = 0, len = arguments.length; i < len; i++) {
39761 this.add(arguments[i]);
39766 // if we have not been rendered yet, then we can not really do much of this..
39767 if (!this.bodyEl) {
39768 this.unrendered_panels.push(panel);
39775 if(this.hasPanel(panel)){
39776 this.showPanel(panel);
39779 panel.setRegion(this);
39780 this.panels.add(panel);
39781 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39782 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39783 // and hide them... ???
39784 this.bodyEl.dom.appendChild(panel.getEl().dom);
39785 if(panel.background !== true){
39786 this.setActivePanel(panel);
39788 this.fireEvent("paneladded", this, panel);
39795 this.initPanelAsTab(panel);
39799 if(panel.background !== true){
39800 this.tabs.activate(panel.getEl().id);
39802 this.fireEvent("paneladded", this, panel);
39807 * Hides the tab for the specified panel.
39808 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39810 hidePanel : function(panel){
39811 if(this.tabs && (panel = this.getPanel(panel))){
39812 this.tabs.hideTab(panel.getEl().id);
39817 * Unhides the tab for a previously hidden panel.
39818 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39820 unhidePanel : function(panel){
39821 if(this.tabs && (panel = this.getPanel(panel))){
39822 this.tabs.unhideTab(panel.getEl().id);
39826 clearPanels : function(){
39827 while(this.panels.getCount() > 0){
39828 this.remove(this.panels.first());
39833 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39834 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39835 * @param {Boolean} preservePanel Overrides the config preservePanel option
39836 * @return {Roo.ContentPanel} The panel that was removed
39838 remove : function(panel, preservePanel)
39840 panel = this.getPanel(panel);
39845 this.fireEvent("beforeremove", this, panel, e);
39846 if(e.cancel === true){
39849 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39850 var panelId = panel.getId();
39851 this.panels.removeKey(panelId);
39853 document.body.appendChild(panel.getEl().dom);
39856 this.tabs.removeTab(panel.getEl().id);
39857 }else if (!preservePanel){
39858 this.bodyEl.dom.removeChild(panel.getEl().dom);
39860 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39861 var p = this.panels.first();
39862 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39863 tempEl.appendChild(p.getEl().dom);
39864 this.bodyEl.update("");
39865 this.bodyEl.dom.appendChild(p.getEl().dom);
39867 this.updateTitle(p.getTitle());
39869 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39870 this.setActivePanel(p);
39872 panel.setRegion(null);
39873 if(this.activePanel == panel){
39874 this.activePanel = null;
39876 if(this.config.autoDestroy !== false && preservePanel !== true){
39877 try{panel.destroy();}catch(e){}
39879 this.fireEvent("panelremoved", this, panel);
39884 * Returns the TabPanel component used by this region
39885 * @return {Roo.TabPanel}
39887 getTabs : function(){
39891 createTool : function(parentEl, className){
39892 var btn = Roo.DomHelper.append(parentEl, {
39894 cls: "x-layout-tools-button",
39897 cls: "roo-layout-tools-button-inner " + className,
39901 btn.addClassOnOver("roo-layout-tools-button-over");
39906 * Ext JS Library 1.1.1
39907 * Copyright(c) 2006-2007, Ext JS, LLC.
39909 * Originally Released Under LGPL - original licence link has changed is not relivant.
39912 * <script type="text/javascript">
39918 * @class Roo.SplitLayoutRegion
39919 * @extends Roo.LayoutRegion
39920 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39922 Roo.bootstrap.layout.Split = function(config){
39923 this.cursor = config.cursor;
39924 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39927 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39929 splitTip : "Drag to resize.",
39930 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39931 useSplitTips : false,
39933 applyConfig : function(config){
39934 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39937 onRender : function(ctr,pos) {
39939 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39940 if(!this.config.split){
39945 var splitEl = Roo.DomHelper.append(ctr.dom, {
39947 id: this.el.id + "-split",
39948 cls: "roo-layout-split roo-layout-split-"+this.position,
39951 /** The SplitBar for this region
39952 * @type Roo.SplitBar */
39953 // does not exist yet...
39954 Roo.log([this.position, this.orientation]);
39956 this.split = new Roo.bootstrap.SplitBar({
39957 dragElement : splitEl,
39958 resizingElement: this.el,
39959 orientation : this.orientation
39962 this.split.on("moved", this.onSplitMove, this);
39963 this.split.useShim = this.config.useShim === true;
39964 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39965 if(this.useSplitTips){
39966 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39968 //if(config.collapsible){
39969 // this.split.el.on("dblclick", this.collapse, this);
39972 if(typeof this.config.minSize != "undefined"){
39973 this.split.minSize = this.config.minSize;
39975 if(typeof this.config.maxSize != "undefined"){
39976 this.split.maxSize = this.config.maxSize;
39978 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39979 this.hideSplitter();
39984 getHMaxSize : function(){
39985 var cmax = this.config.maxSize || 10000;
39986 var center = this.mgr.getRegion("center");
39987 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39990 getVMaxSize : function(){
39991 var cmax = this.config.maxSize || 10000;
39992 var center = this.mgr.getRegion("center");
39993 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39996 onSplitMove : function(split, newSize){
39997 this.fireEvent("resized", this, newSize);
40001 * Returns the {@link Roo.SplitBar} for this region.
40002 * @return {Roo.SplitBar}
40004 getSplitBar : function(){
40009 this.hideSplitter();
40010 Roo.bootstrap.layout.Split.superclass.hide.call(this);
40013 hideSplitter : function(){
40015 this.split.el.setLocation(-2000,-2000);
40016 this.split.el.hide();
40022 this.split.el.show();
40024 Roo.bootstrap.layout.Split.superclass.show.call(this);
40027 beforeSlide: function(){
40028 if(Roo.isGecko){// firefox overflow auto bug workaround
40029 this.bodyEl.clip();
40031 this.tabs.bodyEl.clip();
40033 if(this.activePanel){
40034 this.activePanel.getEl().clip();
40036 if(this.activePanel.beforeSlide){
40037 this.activePanel.beforeSlide();
40043 afterSlide : function(){
40044 if(Roo.isGecko){// firefox overflow auto bug workaround
40045 this.bodyEl.unclip();
40047 this.tabs.bodyEl.unclip();
40049 if(this.activePanel){
40050 this.activePanel.getEl().unclip();
40051 if(this.activePanel.afterSlide){
40052 this.activePanel.afterSlide();
40058 initAutoHide : function(){
40059 if(this.autoHide !== false){
40060 if(!this.autoHideHd){
40061 var st = new Roo.util.DelayedTask(this.slideIn, this);
40062 this.autoHideHd = {
40063 "mouseout": function(e){
40064 if(!e.within(this.el, true)){
40068 "mouseover" : function(e){
40074 this.el.on(this.autoHideHd);
40078 clearAutoHide : function(){
40079 if(this.autoHide !== false){
40080 this.el.un("mouseout", this.autoHideHd.mouseout);
40081 this.el.un("mouseover", this.autoHideHd.mouseover);
40085 clearMonitor : function(){
40086 Roo.get(document).un("click", this.slideInIf, this);
40089 // these names are backwards but not changed for compat
40090 slideOut : function(){
40091 if(this.isSlid || this.el.hasActiveFx()){
40094 this.isSlid = true;
40095 if(this.collapseBtn){
40096 this.collapseBtn.hide();
40098 this.closeBtnState = this.closeBtn.getStyle('display');
40099 this.closeBtn.hide();
40101 this.stickBtn.show();
40104 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40105 this.beforeSlide();
40106 this.el.setStyle("z-index", 10001);
40107 this.el.slideIn(this.getSlideAnchor(), {
40108 callback: function(){
40110 this.initAutoHide();
40111 Roo.get(document).on("click", this.slideInIf, this);
40112 this.fireEvent("slideshow", this);
40119 afterSlideIn : function(){
40120 this.clearAutoHide();
40121 this.isSlid = false;
40122 this.clearMonitor();
40123 this.el.setStyle("z-index", "");
40124 if(this.collapseBtn){
40125 this.collapseBtn.show();
40127 this.closeBtn.setStyle('display', this.closeBtnState);
40129 this.stickBtn.hide();
40131 this.fireEvent("slidehide", this);
40134 slideIn : function(cb){
40135 if(!this.isSlid || this.el.hasActiveFx()){
40139 this.isSlid = false;
40140 this.beforeSlide();
40141 this.el.slideOut(this.getSlideAnchor(), {
40142 callback: function(){
40143 this.el.setLeftTop(-10000, -10000);
40145 this.afterSlideIn();
40153 slideInIf : function(e){
40154 if(!e.within(this.el)){
40159 animateCollapse : function(){
40160 this.beforeSlide();
40161 this.el.setStyle("z-index", 20000);
40162 var anchor = this.getSlideAnchor();
40163 this.el.slideOut(anchor, {
40164 callback : function(){
40165 this.el.setStyle("z-index", "");
40166 this.collapsedEl.slideIn(anchor, {duration:.3});
40168 this.el.setLocation(-10000,-10000);
40170 this.fireEvent("collapsed", this);
40177 animateExpand : function(){
40178 this.beforeSlide();
40179 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40180 this.el.setStyle("z-index", 20000);
40181 this.collapsedEl.hide({
40184 this.el.slideIn(this.getSlideAnchor(), {
40185 callback : function(){
40186 this.el.setStyle("z-index", "");
40189 this.split.el.show();
40191 this.fireEvent("invalidated", this);
40192 this.fireEvent("expanded", this);
40220 getAnchor : function(){
40221 return this.anchors[this.position];
40224 getCollapseAnchor : function(){
40225 return this.canchors[this.position];
40228 getSlideAnchor : function(){
40229 return this.sanchors[this.position];
40232 getAlignAdj : function(){
40233 var cm = this.cmargins;
40234 switch(this.position){
40250 getExpandAdj : function(){
40251 var c = this.collapsedEl, cm = this.cmargins;
40252 switch(this.position){
40254 return [-(cm.right+c.getWidth()+cm.left), 0];
40257 return [cm.right+c.getWidth()+cm.left, 0];
40260 return [0, -(cm.top+cm.bottom+c.getHeight())];
40263 return [0, cm.top+cm.bottom+c.getHeight()];
40269 * Ext JS Library 1.1.1
40270 * Copyright(c) 2006-2007, Ext JS, LLC.
40272 * Originally Released Under LGPL - original licence link has changed is not relivant.
40275 * <script type="text/javascript">
40278 * These classes are private internal classes
40280 Roo.bootstrap.layout.Center = function(config){
40281 config.region = "center";
40282 Roo.bootstrap.layout.Region.call(this, config);
40283 this.visible = true;
40284 this.minWidth = config.minWidth || 20;
40285 this.minHeight = config.minHeight || 20;
40288 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40290 // center panel can't be hidden
40294 // center panel can't be hidden
40297 getMinWidth: function(){
40298 return this.minWidth;
40301 getMinHeight: function(){
40302 return this.minHeight;
40316 Roo.bootstrap.layout.North = function(config)
40318 config.region = 'north';
40319 config.cursor = 'n-resize';
40321 Roo.bootstrap.layout.Split.call(this, config);
40325 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40326 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40327 this.split.el.addClass("roo-layout-split-v");
40329 //var size = config.initialSize || config.height;
40330 //if(this.el && typeof size != "undefined"){
40331 // this.el.setHeight(size);
40334 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40336 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40339 onRender : function(ctr, pos)
40341 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40342 var size = this.config.initialSize || this.config.height;
40343 if(this.el && typeof size != "undefined"){
40344 this.el.setHeight(size);
40349 getBox : function(){
40350 if(this.collapsed){
40351 return this.collapsedEl.getBox();
40353 var box = this.el.getBox();
40355 box.height += this.split.el.getHeight();
40360 updateBox : function(box){
40361 if(this.split && !this.collapsed){
40362 box.height -= this.split.el.getHeight();
40363 this.split.el.setLeft(box.x);
40364 this.split.el.setTop(box.y+box.height);
40365 this.split.el.setWidth(box.width);
40367 if(this.collapsed){
40368 this.updateBody(box.width, null);
40370 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40378 Roo.bootstrap.layout.South = function(config){
40379 config.region = 'south';
40380 config.cursor = 's-resize';
40381 Roo.bootstrap.layout.Split.call(this, config);
40383 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40384 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40385 this.split.el.addClass("roo-layout-split-v");
40390 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40391 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40393 onRender : function(ctr, pos)
40395 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40396 var size = this.config.initialSize || this.config.height;
40397 if(this.el && typeof size != "undefined"){
40398 this.el.setHeight(size);
40403 getBox : function(){
40404 if(this.collapsed){
40405 return this.collapsedEl.getBox();
40407 var box = this.el.getBox();
40409 var sh = this.split.el.getHeight();
40416 updateBox : function(box){
40417 if(this.split && !this.collapsed){
40418 var sh = this.split.el.getHeight();
40421 this.split.el.setLeft(box.x);
40422 this.split.el.setTop(box.y-sh);
40423 this.split.el.setWidth(box.width);
40425 if(this.collapsed){
40426 this.updateBody(box.width, null);
40428 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40432 Roo.bootstrap.layout.East = function(config){
40433 config.region = "east";
40434 config.cursor = "e-resize";
40435 Roo.bootstrap.layout.Split.call(this, config);
40437 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40438 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40439 this.split.el.addClass("roo-layout-split-h");
40443 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40444 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40446 onRender : function(ctr, pos)
40448 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40449 var size = this.config.initialSize || this.config.width;
40450 if(this.el && typeof size != "undefined"){
40451 this.el.setWidth(size);
40456 getBox : function(){
40457 if(this.collapsed){
40458 return this.collapsedEl.getBox();
40460 var box = this.el.getBox();
40462 var sw = this.split.el.getWidth();
40469 updateBox : function(box){
40470 if(this.split && !this.collapsed){
40471 var sw = this.split.el.getWidth();
40473 this.split.el.setLeft(box.x);
40474 this.split.el.setTop(box.y);
40475 this.split.el.setHeight(box.height);
40478 if(this.collapsed){
40479 this.updateBody(null, box.height);
40481 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40485 Roo.bootstrap.layout.West = function(config){
40486 config.region = "west";
40487 config.cursor = "w-resize";
40489 Roo.bootstrap.layout.Split.call(this, config);
40491 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40492 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40493 this.split.el.addClass("roo-layout-split-h");
40497 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40498 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40500 onRender: function(ctr, pos)
40502 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40503 var size = this.config.initialSize || this.config.width;
40504 if(typeof size != "undefined"){
40505 this.el.setWidth(size);
40509 getBox : function(){
40510 if(this.collapsed){
40511 return this.collapsedEl.getBox();
40513 var box = this.el.getBox();
40514 if (box.width == 0) {
40515 box.width = this.config.width; // kludge?
40518 box.width += this.split.el.getWidth();
40523 updateBox : function(box){
40524 if(this.split && !this.collapsed){
40525 var sw = this.split.el.getWidth();
40527 this.split.el.setLeft(box.x+box.width);
40528 this.split.el.setTop(box.y);
40529 this.split.el.setHeight(box.height);
40531 if(this.collapsed){
40532 this.updateBody(null, box.height);
40534 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40536 });Roo.namespace("Roo.bootstrap.panel");/*
40538 * Ext JS Library 1.1.1
40539 * Copyright(c) 2006-2007, Ext JS, LLC.
40541 * Originally Released Under LGPL - original licence link has changed is not relivant.
40544 * <script type="text/javascript">
40547 * @class Roo.ContentPanel
40548 * @extends Roo.util.Observable
40549 * A basic ContentPanel element.
40550 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40551 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40552 * @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
40553 * @cfg {Boolean} closable True if the panel can be closed/removed
40554 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40555 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40556 * @cfg {Toolbar} toolbar A toolbar for this panel
40557 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40558 * @cfg {String} title The title for this panel
40559 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40560 * @cfg {String} url Calls {@link #setUrl} with this value
40561 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40562 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40563 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40564 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40565 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40566 * @cfg {Boolean} badges render the badges
40567 * @cfg {String} cls extra classes to use
40568 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40571 * Create a new ContentPanel.
40572 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40573 * @param {String/Object} config A string to set only the title or a config object
40574 * @param {String} content (optional) Set the HTML content for this panel
40575 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40577 Roo.bootstrap.panel.Content = function( config){
40579 this.tpl = config.tpl || false;
40581 var el = config.el;
40582 var content = config.content;
40584 if(config.autoCreate){ // xtype is available if this is called from factory
40587 this.el = Roo.get(el);
40588 if(!this.el && config && config.autoCreate){
40589 if(typeof config.autoCreate == "object"){
40590 if(!config.autoCreate.id){
40591 config.autoCreate.id = config.id||el;
40593 this.el = Roo.DomHelper.append(document.body,
40594 config.autoCreate, true);
40598 cls: (config.cls || '') +
40599 (config.background ? ' bg-' + config.background : '') +
40600 " roo-layout-inactive-content",
40603 if (config.iframe) {
40607 style : 'border: 0px',
40608 src : 'about:blank'
40614 elcfg.html = config.html;
40618 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40619 if (config.iframe) {
40620 this.iframeEl = this.el.select('iframe',true).first();
40625 this.closable = false;
40626 this.loaded = false;
40627 this.active = false;
40630 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40632 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40634 this.wrapEl = this.el; //this.el.wrap();
40636 if (config.toolbar.items) {
40637 ti = config.toolbar.items ;
40638 delete config.toolbar.items ;
40642 this.toolbar.render(this.wrapEl, 'before');
40643 for(var i =0;i < ti.length;i++) {
40644 // Roo.log(['add child', items[i]]);
40645 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40647 this.toolbar.items = nitems;
40648 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40649 delete config.toolbar;
40653 // xtype created footer. - not sure if will work as we normally have to render first..
40654 if (this.footer && !this.footer.el && this.footer.xtype) {
40655 if (!this.wrapEl) {
40656 this.wrapEl = this.el.wrap();
40659 this.footer.container = this.wrapEl.createChild();
40661 this.footer = Roo.factory(this.footer, Roo);
40666 if(typeof config == "string"){
40667 this.title = config;
40669 Roo.apply(this, config);
40673 this.resizeEl = Roo.get(this.resizeEl, true);
40675 this.resizeEl = this.el;
40677 // handle view.xtype
40685 * Fires when this panel is activated.
40686 * @param {Roo.ContentPanel} this
40690 * @event deactivate
40691 * Fires when this panel is activated.
40692 * @param {Roo.ContentPanel} this
40694 "deactivate" : true,
40698 * Fires when this panel is resized if fitToFrame is true.
40699 * @param {Roo.ContentPanel} this
40700 * @param {Number} width The width after any component adjustments
40701 * @param {Number} height The height after any component adjustments
40707 * Fires when this tab is created
40708 * @param {Roo.ContentPanel} this
40714 * Fires when this content is scrolled
40715 * @param {Roo.ContentPanel} this
40716 * @param {Event} scrollEvent
40727 if(this.autoScroll && !this.iframe){
40728 this.resizeEl.setStyle("overflow", "auto");
40729 this.resizeEl.on('scroll', this.onScroll, this);
40731 // fix randome scrolling
40732 //this.el.on('scroll', function() {
40733 // Roo.log('fix random scolling');
40734 // this.scrollTo('top',0);
40737 content = content || this.content;
40739 this.setContent(content);
40741 if(config && config.url){
40742 this.setUrl(this.url, this.params, this.loadOnce);
40747 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40749 if (this.view && typeof(this.view.xtype) != 'undefined') {
40750 this.view.el = this.el.appendChild(document.createElement("div"));
40751 this.view = Roo.factory(this.view);
40752 this.view.render && this.view.render(false, '');
40756 this.fireEvent('render', this);
40759 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40769 /* Resize Element - use this to work out scroll etc. */
40772 setRegion : function(region){
40773 this.region = region;
40774 this.setActiveClass(region && !this.background);
40778 setActiveClass: function(state)
40781 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40782 this.el.setStyle('position','relative');
40784 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40785 this.el.setStyle('position', 'absolute');
40790 * Returns the toolbar for this Panel if one was configured.
40791 * @return {Roo.Toolbar}
40793 getToolbar : function(){
40794 return this.toolbar;
40797 setActiveState : function(active)
40799 this.active = active;
40800 this.setActiveClass(active);
40802 if(this.fireEvent("deactivate", this) === false){
40807 this.fireEvent("activate", this);
40811 * Updates this panel's element (not for iframe)
40812 * @param {String} content The new content
40813 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40815 setContent : function(content, loadScripts){
40820 this.el.update(content, loadScripts);
40823 ignoreResize : function(w, h){
40824 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40827 this.lastSize = {width: w, height: h};
40832 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40833 * @return {Roo.UpdateManager} The UpdateManager
40835 getUpdateManager : function(){
40839 return this.el.getUpdateManager();
40842 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40843 * Does not work with IFRAME contents
40844 * @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:
40847 url: "your-url.php",
40848 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40849 callback: yourFunction,
40850 scope: yourObject, //(optional scope)
40853 text: "Loading...",
40859 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40860 * 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.
40861 * @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}
40862 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40863 * @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.
40864 * @return {Roo.ContentPanel} this
40872 var um = this.el.getUpdateManager();
40873 um.update.apply(um, arguments);
40879 * 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.
40880 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40881 * @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)
40882 * @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)
40883 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40885 setUrl : function(url, params, loadOnce){
40887 this.iframeEl.dom.src = url;
40891 if(this.refreshDelegate){
40892 this.removeListener("activate", this.refreshDelegate);
40894 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40895 this.on("activate", this.refreshDelegate);
40896 return this.el.getUpdateManager();
40899 _handleRefresh : function(url, params, loadOnce){
40900 if(!loadOnce || !this.loaded){
40901 var updater = this.el.getUpdateManager();
40902 updater.update(url, params, this._setLoaded.createDelegate(this));
40906 _setLoaded : function(){
40907 this.loaded = true;
40911 * Returns this panel's id
40914 getId : function(){
40919 * Returns this panel's element - used by regiosn to add.
40920 * @return {Roo.Element}
40922 getEl : function(){
40923 return this.wrapEl || this.el;
40928 adjustForComponents : function(width, height)
40930 //Roo.log('adjustForComponents ');
40931 if(this.resizeEl != this.el){
40932 width -= this.el.getFrameWidth('lr');
40933 height -= this.el.getFrameWidth('tb');
40936 var te = this.toolbar.getEl();
40937 te.setWidth(width);
40938 height -= te.getHeight();
40941 var te = this.footer.getEl();
40942 te.setWidth(width);
40943 height -= te.getHeight();
40947 if(this.adjustments){
40948 width += this.adjustments[0];
40949 height += this.adjustments[1];
40951 return {"width": width, "height": height};
40954 setSize : function(width, height){
40955 if(this.fitToFrame && !this.ignoreResize(width, height)){
40956 if(this.fitContainer && this.resizeEl != this.el){
40957 this.el.setSize(width, height);
40959 var size = this.adjustForComponents(width, height);
40961 this.iframeEl.setSize(width,height);
40964 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40965 this.fireEvent('resize', this, size.width, size.height);
40972 * Returns this panel's title
40975 getTitle : function(){
40977 if (typeof(this.title) != 'object') {
40982 for (var k in this.title) {
40983 if (!this.title.hasOwnProperty(k)) {
40987 if (k.indexOf('-') >= 0) {
40988 var s = k.split('-');
40989 for (var i = 0; i<s.length; i++) {
40990 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40993 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41000 * Set this panel's title
41001 * @param {String} title
41003 setTitle : function(title){
41004 this.title = title;
41006 this.region.updatePanelTitle(this, title);
41011 * Returns true is this panel was configured to be closable
41012 * @return {Boolean}
41014 isClosable : function(){
41015 return this.closable;
41018 beforeSlide : function(){
41020 this.resizeEl.clip();
41023 afterSlide : function(){
41025 this.resizeEl.unclip();
41029 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41030 * Will fail silently if the {@link #setUrl} method has not been called.
41031 * This does not activate the panel, just updates its content.
41033 refresh : function(){
41034 if(this.refreshDelegate){
41035 this.loaded = false;
41036 this.refreshDelegate();
41041 * Destroys this panel
41043 destroy : function(){
41044 this.el.removeAllListeners();
41045 var tempEl = document.createElement("span");
41046 tempEl.appendChild(this.el.dom);
41047 tempEl.innerHTML = "";
41053 * form - if the content panel contains a form - this is a reference to it.
41054 * @type {Roo.form.Form}
41058 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41059 * This contains a reference to it.
41065 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41075 * @param {Object} cfg Xtype definition of item to add.
41079 getChildContainer: function () {
41080 return this.getEl();
41084 onScroll : function(e)
41086 this.fireEvent('scroll', this, e);
41091 var ret = new Roo.factory(cfg);
41096 if (cfg.xtype.match(/^Form$/)) {
41099 //if (this.footer) {
41100 // el = this.footer.container.insertSibling(false, 'before');
41102 el = this.el.createChild();
41105 this.form = new Roo.form.Form(cfg);
41108 if ( this.form.allItems.length) {
41109 this.form.render(el.dom);
41113 // should only have one of theses..
41114 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41115 // views.. should not be just added - used named prop 'view''
41117 cfg.el = this.el.appendChild(document.createElement("div"));
41120 var ret = new Roo.factory(cfg);
41122 ret.render && ret.render(false, ''); // render blank..
41132 * @class Roo.bootstrap.panel.Grid
41133 * @extends Roo.bootstrap.panel.Content
41135 * Create a new GridPanel.
41136 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41137 * @param {Object} config A the config object
41143 Roo.bootstrap.panel.Grid = function(config)
41147 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41148 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41150 config.el = this.wrapper;
41151 //this.el = this.wrapper;
41153 if (config.container) {
41154 // ctor'ed from a Border/panel.grid
41157 this.wrapper.setStyle("overflow", "hidden");
41158 this.wrapper.addClass('roo-grid-container');
41163 if(config.toolbar){
41164 var tool_el = this.wrapper.createChild();
41165 this.toolbar = Roo.factory(config.toolbar);
41167 if (config.toolbar.items) {
41168 ti = config.toolbar.items ;
41169 delete config.toolbar.items ;
41173 this.toolbar.render(tool_el);
41174 for(var i =0;i < ti.length;i++) {
41175 // Roo.log(['add child', items[i]]);
41176 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41178 this.toolbar.items = nitems;
41180 delete config.toolbar;
41183 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41184 config.grid.scrollBody = true;;
41185 config.grid.monitorWindowResize = false; // turn off autosizing
41186 config.grid.autoHeight = false;
41187 config.grid.autoWidth = false;
41189 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41191 if (config.background) {
41192 // render grid on panel activation (if panel background)
41193 this.on('activate', function(gp) {
41194 if (!gp.grid.rendered) {
41195 gp.grid.render(this.wrapper);
41196 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41201 this.grid.render(this.wrapper);
41202 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41205 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41206 // ??? needed ??? config.el = this.wrapper;
41211 // xtype created footer. - not sure if will work as we normally have to render first..
41212 if (this.footer && !this.footer.el && this.footer.xtype) {
41214 var ctr = this.grid.getView().getFooterPanel(true);
41215 this.footer.dataSource = this.grid.dataSource;
41216 this.footer = Roo.factory(this.footer, Roo);
41217 this.footer.render(ctr);
41227 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41228 getId : function(){
41229 return this.grid.id;
41233 * Returns the grid for this panel
41234 * @return {Roo.bootstrap.Table}
41236 getGrid : function(){
41240 setSize : function(width, height){
41241 if(!this.ignoreResize(width, height)){
41242 var grid = this.grid;
41243 var size = this.adjustForComponents(width, height);
41244 // tfoot is not a footer?
41247 var gridel = grid.getGridEl();
41248 gridel.setSize(size.width, size.height);
41250 var tbd = grid.getGridEl().select('tbody', true).first();
41251 var thd = grid.getGridEl().select('thead',true).first();
41252 var tbf= grid.getGridEl().select('tfoot', true).first();
41255 size.height -= tbf.getHeight();
41258 size.height -= thd.getHeight();
41261 tbd.setSize(size.width, size.height );
41262 // this is for the account management tab -seems to work there.
41263 var thd = grid.getGridEl().select('thead',true).first();
41265 // tbd.setSize(size.width, size.height - thd.getHeight());
41274 beforeSlide : function(){
41275 this.grid.getView().scroller.clip();
41278 afterSlide : function(){
41279 this.grid.getView().scroller.unclip();
41282 destroy : function(){
41283 this.grid.destroy();
41285 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41290 * @class Roo.bootstrap.panel.Nest
41291 * @extends Roo.bootstrap.panel.Content
41293 * Create a new Panel, that can contain a layout.Border.
41296 * @param {Roo.BorderLayout} layout The layout for this panel
41297 * @param {String/Object} config A string to set only the title or a config object
41299 Roo.bootstrap.panel.Nest = function(config)
41301 // construct with only one argument..
41302 /* FIXME - implement nicer consturctors
41303 if (layout.layout) {
41305 layout = config.layout;
41306 delete config.layout;
41308 if (layout.xtype && !layout.getEl) {
41309 // then layout needs constructing..
41310 layout = Roo.factory(layout, Roo);
41314 config.el = config.layout.getEl();
41316 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41318 config.layout.monitorWindowResize = false; // turn off autosizing
41319 this.layout = config.layout;
41320 this.layout.getEl().addClass("roo-layout-nested-layout");
41321 this.layout.parent = this;
41328 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41330 setSize : function(width, height){
41331 if(!this.ignoreResize(width, height)){
41332 var size = this.adjustForComponents(width, height);
41333 var el = this.layout.getEl();
41334 if (size.height < 1) {
41335 el.setWidth(size.width);
41337 el.setSize(size.width, size.height);
41339 var touch = el.dom.offsetWidth;
41340 this.layout.layout();
41341 // ie requires a double layout on the first pass
41342 if(Roo.isIE && !this.initialized){
41343 this.initialized = true;
41344 this.layout.layout();
41349 // activate all subpanels if not currently active..
41351 setActiveState : function(active){
41352 this.active = active;
41353 this.setActiveClass(active);
41356 this.fireEvent("deactivate", this);
41360 this.fireEvent("activate", this);
41361 // not sure if this should happen before or after..
41362 if (!this.layout) {
41363 return; // should not happen..
41366 for (var r in this.layout.regions) {
41367 reg = this.layout.getRegion(r);
41368 if (reg.getActivePanel()) {
41369 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41370 reg.setActivePanel(reg.getActivePanel());
41373 if (!reg.panels.length) {
41376 reg.showPanel(reg.getPanel(0));
41385 * Returns the nested BorderLayout for this panel
41386 * @return {Roo.BorderLayout}
41388 getLayout : function(){
41389 return this.layout;
41393 * Adds a xtype elements to the layout of the nested panel
41397 xtype : 'ContentPanel',
41404 xtype : 'NestedLayoutPanel',
41410 items : [ ... list of content panels or nested layout panels.. ]
41414 * @param {Object} cfg Xtype definition of item to add.
41416 addxtype : function(cfg) {
41417 return this.layout.addxtype(cfg);
41422 * Ext JS Library 1.1.1
41423 * Copyright(c) 2006-2007, Ext JS, LLC.
41425 * Originally Released Under LGPL - original licence link has changed is not relivant.
41428 * <script type="text/javascript">
41431 * @class Roo.TabPanel
41432 * @extends Roo.util.Observable
41433 * A lightweight tab container.
41437 // basic tabs 1, built from existing content
41438 var tabs = new Roo.TabPanel("tabs1");
41439 tabs.addTab("script", "View Script");
41440 tabs.addTab("markup", "View Markup");
41441 tabs.activate("script");
41443 // more advanced tabs, built from javascript
41444 var jtabs = new Roo.TabPanel("jtabs");
41445 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41447 // set up the UpdateManager
41448 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41449 var updater = tab2.getUpdateManager();
41450 updater.setDefaultUrl("ajax1.htm");
41451 tab2.on('activate', updater.refresh, updater, true);
41453 // Use setUrl for Ajax loading
41454 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41455 tab3.setUrl("ajax2.htm", null, true);
41458 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41461 jtabs.activate("jtabs-1");
41464 * Create a new TabPanel.
41465 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41466 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41468 Roo.bootstrap.panel.Tabs = function(config){
41470 * The container element for this TabPanel.
41471 * @type Roo.Element
41473 this.el = Roo.get(config.el);
41476 if(typeof config == "boolean"){
41477 this.tabPosition = config ? "bottom" : "top";
41479 Roo.apply(this, config);
41483 if(this.tabPosition == "bottom"){
41484 // if tabs are at the bottom = create the body first.
41485 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41486 this.el.addClass("roo-tabs-bottom");
41488 // next create the tabs holders
41490 if (this.tabPosition == "west"){
41492 var reg = this.region; // fake it..
41494 if (!reg.mgr.parent) {
41497 reg = reg.mgr.parent.region;
41499 Roo.log("got nest?");
41501 if (reg.mgr.getRegion('west')) {
41502 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41503 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41504 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41505 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41506 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41514 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41515 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41516 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41517 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41522 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41525 // finally - if tabs are at the top, then create the body last..
41526 if(this.tabPosition != "bottom"){
41527 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41528 * @type Roo.Element
41530 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41531 this.el.addClass("roo-tabs-top");
41535 this.bodyEl.setStyle("position", "relative");
41537 this.active = null;
41538 this.activateDelegate = this.activate.createDelegate(this);
41543 * Fires when the active tab changes
41544 * @param {Roo.TabPanel} this
41545 * @param {Roo.TabPanelItem} activePanel The new active tab
41549 * @event beforetabchange
41550 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41551 * @param {Roo.TabPanel} this
41552 * @param {Object} e Set cancel to true on this object to cancel the tab change
41553 * @param {Roo.TabPanelItem} tab The tab being changed to
41555 "beforetabchange" : true
41558 Roo.EventManager.onWindowResize(this.onResize, this);
41559 this.cpad = this.el.getPadding("lr");
41560 this.hiddenCount = 0;
41563 // toolbar on the tabbar support...
41564 if (this.toolbar) {
41565 alert("no toolbar support yet");
41566 this.toolbar = false;
41568 var tcfg = this.toolbar;
41569 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41570 this.toolbar = new Roo.Toolbar(tcfg);
41571 if (Roo.isSafari) {
41572 var tbl = tcfg.container.child('table', true);
41573 tbl.setAttribute('width', '100%');
41581 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41584 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41586 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41588 tabPosition : "top",
41590 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41592 currentTabWidth : 0,
41594 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41598 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41602 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41604 preferredTabWidth : 175,
41606 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41608 resizeTabs : false,
41610 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41612 monitorResize : true,
41614 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41616 toolbar : false, // set by caller..
41618 region : false, /// set by caller
41620 disableTooltips : true, // not used yet...
41623 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41624 * @param {String} id The id of the div to use <b>or create</b>
41625 * @param {String} text The text for the tab
41626 * @param {String} content (optional) Content to put in the TabPanelItem body
41627 * @param {Boolean} closable (optional) True to create a close icon on the tab
41628 * @return {Roo.TabPanelItem} The created TabPanelItem
41630 addTab : function(id, text, content, closable, tpl)
41632 var item = new Roo.bootstrap.panel.TabItem({
41636 closable : closable,
41639 this.addTabItem(item);
41641 item.setContent(content);
41647 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41648 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41649 * @return {Roo.TabPanelItem}
41651 getTab : function(id){
41652 return this.items[id];
41656 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41657 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41659 hideTab : function(id){
41660 var t = this.items[id];
41663 this.hiddenCount++;
41664 this.autoSizeTabs();
41669 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41670 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41672 unhideTab : function(id){
41673 var t = this.items[id];
41675 t.setHidden(false);
41676 this.hiddenCount--;
41677 this.autoSizeTabs();
41682 * Adds an existing {@link Roo.TabPanelItem}.
41683 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41685 addTabItem : function(item)
41687 this.items[item.id] = item;
41688 this.items.push(item);
41689 this.autoSizeTabs();
41690 // if(this.resizeTabs){
41691 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41692 // this.autoSizeTabs();
41694 // item.autoSize();
41699 * Removes a {@link Roo.TabPanelItem}.
41700 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41702 removeTab : function(id){
41703 var items = this.items;
41704 var tab = items[id];
41705 if(!tab) { return; }
41706 var index = items.indexOf(tab);
41707 if(this.active == tab && items.length > 1){
41708 var newTab = this.getNextAvailable(index);
41713 this.stripEl.dom.removeChild(tab.pnode.dom);
41714 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41715 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41717 items.splice(index, 1);
41718 delete this.items[tab.id];
41719 tab.fireEvent("close", tab);
41720 tab.purgeListeners();
41721 this.autoSizeTabs();
41724 getNextAvailable : function(start){
41725 var items = this.items;
41727 // look for a next tab that will slide over to
41728 // replace the one being removed
41729 while(index < items.length){
41730 var item = items[++index];
41731 if(item && !item.isHidden()){
41735 // if one isn't found select the previous tab (on the left)
41738 var item = items[--index];
41739 if(item && !item.isHidden()){
41747 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41748 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41750 disableTab : function(id){
41751 var tab = this.items[id];
41752 if(tab && this.active != tab){
41758 * Enables a {@link Roo.TabPanelItem} that is disabled.
41759 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41761 enableTab : function(id){
41762 var tab = this.items[id];
41767 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41768 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41769 * @return {Roo.TabPanelItem} The TabPanelItem.
41771 activate : function(id)
41773 //Roo.log('activite:' + id);
41775 var tab = this.items[id];
41779 if(tab == this.active || tab.disabled){
41783 this.fireEvent("beforetabchange", this, e, tab);
41784 if(e.cancel !== true && !tab.disabled){
41786 this.active.hide();
41788 this.active = this.items[id];
41789 this.active.show();
41790 this.fireEvent("tabchange", this, this.active);
41796 * Gets the active {@link Roo.TabPanelItem}.
41797 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41799 getActiveTab : function(){
41800 return this.active;
41804 * Updates the tab body element to fit the height of the container element
41805 * for overflow scrolling
41806 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41808 syncHeight : function(targetHeight){
41809 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41810 var bm = this.bodyEl.getMargins();
41811 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41812 this.bodyEl.setHeight(newHeight);
41816 onResize : function(){
41817 if(this.monitorResize){
41818 this.autoSizeTabs();
41823 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41825 beginUpdate : function(){
41826 this.updating = true;
41830 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41832 endUpdate : function(){
41833 this.updating = false;
41834 this.autoSizeTabs();
41838 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41840 autoSizeTabs : function()
41842 var count = this.items.length;
41843 var vcount = count - this.hiddenCount;
41846 this.stripEl.hide();
41848 this.stripEl.show();
41851 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41856 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41857 var availWidth = Math.floor(w / vcount);
41858 var b = this.stripBody;
41859 if(b.getWidth() > w){
41860 var tabs = this.items;
41861 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41862 if(availWidth < this.minTabWidth){
41863 /*if(!this.sleft){ // incomplete scrolling code
41864 this.createScrollButtons();
41867 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41870 if(this.currentTabWidth < this.preferredTabWidth){
41871 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41877 * Returns the number of tabs in this TabPanel.
41880 getCount : function(){
41881 return this.items.length;
41885 * Resizes all the tabs to the passed width
41886 * @param {Number} The new width
41888 setTabWidth : function(width){
41889 this.currentTabWidth = width;
41890 for(var i = 0, len = this.items.length; i < len; i++) {
41891 if(!this.items[i].isHidden()) {
41892 this.items[i].setWidth(width);
41898 * Destroys this TabPanel
41899 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41901 destroy : function(removeEl){
41902 Roo.EventManager.removeResizeListener(this.onResize, this);
41903 for(var i = 0, len = this.items.length; i < len; i++){
41904 this.items[i].purgeListeners();
41906 if(removeEl === true){
41907 this.el.update("");
41912 createStrip : function(container)
41914 var strip = document.createElement("nav");
41915 strip.className = Roo.bootstrap.version == 4 ?
41916 "navbar-light bg-light" :
41917 "navbar navbar-default"; //"x-tabs-wrap";
41918 container.appendChild(strip);
41922 createStripList : function(strip)
41924 // div wrapper for retard IE
41925 // returns the "tr" element.
41926 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41927 //'<div class="x-tabs-strip-wrap">'+
41928 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41929 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41930 return strip.firstChild; //.firstChild.firstChild.firstChild;
41932 createBody : function(container)
41934 var body = document.createElement("div");
41935 Roo.id(body, "tab-body");
41936 //Roo.fly(body).addClass("x-tabs-body");
41937 Roo.fly(body).addClass("tab-content");
41938 container.appendChild(body);
41941 createItemBody :function(bodyEl, id){
41942 var body = Roo.getDom(id);
41944 body = document.createElement("div");
41947 //Roo.fly(body).addClass("x-tabs-item-body");
41948 Roo.fly(body).addClass("tab-pane");
41949 bodyEl.insertBefore(body, bodyEl.firstChild);
41953 createStripElements : function(stripEl, text, closable, tpl)
41955 var td = document.createElement("li"); // was td..
41956 td.className = 'nav-item';
41958 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41961 stripEl.appendChild(td);
41963 td.className = "x-tabs-closable";
41964 if(!this.closeTpl){
41965 this.closeTpl = new Roo.Template(
41966 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41967 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41968 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41971 var el = this.closeTpl.overwrite(td, {"text": text});
41972 var close = el.getElementsByTagName("div")[0];
41973 var inner = el.getElementsByTagName("em")[0];
41974 return {"el": el, "close": close, "inner": inner};
41977 // not sure what this is..
41978 // if(!this.tabTpl){
41979 //this.tabTpl = new Roo.Template(
41980 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41981 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41983 // this.tabTpl = new Roo.Template(
41984 // '<a href="#">' +
41985 // '<span unselectable="on"' +
41986 // (this.disableTooltips ? '' : ' title="{text}"') +
41987 // ' >{text}</span></a>'
41993 var template = tpl || this.tabTpl || false;
41996 template = new Roo.Template(
41997 Roo.bootstrap.version == 4 ?
41999 '<a class="nav-link" href="#" unselectable="on"' +
42000 (this.disableTooltips ? '' : ' title="{text}"') +
42003 '<a class="nav-link" href="#">' +
42004 '<span unselectable="on"' +
42005 (this.disableTooltips ? '' : ' title="{text}"') +
42006 ' >{text}</span></a>'
42011 switch (typeof(template)) {
42015 template = new Roo.Template(template);
42021 var el = template.overwrite(td, {"text": text});
42023 var inner = el.getElementsByTagName("span")[0];
42025 return {"el": el, "inner": inner};
42033 * @class Roo.TabPanelItem
42034 * @extends Roo.util.Observable
42035 * Represents an individual item (tab plus body) in a TabPanel.
42036 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42037 * @param {String} id The id of this TabPanelItem
42038 * @param {String} text The text for the tab of this TabPanelItem
42039 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42041 Roo.bootstrap.panel.TabItem = function(config){
42043 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42044 * @type Roo.TabPanel
42046 this.tabPanel = config.panel;
42048 * The id for this TabPanelItem
42051 this.id = config.id;
42053 this.disabled = false;
42055 this.text = config.text;
42057 this.loaded = false;
42058 this.closable = config.closable;
42061 * The body element for this TabPanelItem.
42062 * @type Roo.Element
42064 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42065 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42066 this.bodyEl.setStyle("display", "block");
42067 this.bodyEl.setStyle("zoom", "1");
42068 //this.hideAction();
42070 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42072 this.el = Roo.get(els.el);
42073 this.inner = Roo.get(els.inner, true);
42074 this.textEl = Roo.bootstrap.version == 4 ?
42075 this.el : Roo.get(this.el.dom.firstChild, true);
42077 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42078 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42081 // this.el.on("mousedown", this.onTabMouseDown, this);
42082 this.el.on("click", this.onTabClick, this);
42084 if(config.closable){
42085 var c = Roo.get(els.close, true);
42086 c.dom.title = this.closeText;
42087 c.addClassOnOver("close-over");
42088 c.on("click", this.closeClick, this);
42094 * Fires when this tab becomes the active tab.
42095 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42096 * @param {Roo.TabPanelItem} this
42100 * @event beforeclose
42101 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42102 * @param {Roo.TabPanelItem} this
42103 * @param {Object} e Set cancel to true on this object to cancel the close.
42105 "beforeclose": true,
42108 * Fires when this tab is closed.
42109 * @param {Roo.TabPanelItem} this
42113 * @event deactivate
42114 * Fires when this tab is no longer the active tab.
42115 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42116 * @param {Roo.TabPanelItem} this
42118 "deactivate" : true
42120 this.hidden = false;
42122 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42125 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42127 purgeListeners : function(){
42128 Roo.util.Observable.prototype.purgeListeners.call(this);
42129 this.el.removeAllListeners();
42132 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42135 this.status_node.addClass("active");
42138 this.tabPanel.stripWrap.repaint();
42140 this.fireEvent("activate", this.tabPanel, this);
42144 * Returns true if this tab is the active tab.
42145 * @return {Boolean}
42147 isActive : function(){
42148 return this.tabPanel.getActiveTab() == this;
42152 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42155 this.status_node.removeClass("active");
42157 this.fireEvent("deactivate", this.tabPanel, this);
42160 hideAction : function(){
42161 this.bodyEl.hide();
42162 this.bodyEl.setStyle("position", "absolute");
42163 this.bodyEl.setLeft("-20000px");
42164 this.bodyEl.setTop("-20000px");
42167 showAction : function(){
42168 this.bodyEl.setStyle("position", "relative");
42169 this.bodyEl.setTop("");
42170 this.bodyEl.setLeft("");
42171 this.bodyEl.show();
42175 * Set the tooltip for the tab.
42176 * @param {String} tooltip The tab's tooltip
42178 setTooltip : function(text){
42179 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42180 this.textEl.dom.qtip = text;
42181 this.textEl.dom.removeAttribute('title');
42183 this.textEl.dom.title = text;
42187 onTabClick : function(e){
42188 e.preventDefault();
42189 this.tabPanel.activate(this.id);
42192 onTabMouseDown : function(e){
42193 e.preventDefault();
42194 this.tabPanel.activate(this.id);
42197 getWidth : function(){
42198 return this.inner.getWidth();
42201 setWidth : function(width){
42202 var iwidth = width - this.linode.getPadding("lr");
42203 this.inner.setWidth(iwidth);
42204 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42205 this.linode.setWidth(width);
42209 * Show or hide the tab
42210 * @param {Boolean} hidden True to hide or false to show.
42212 setHidden : function(hidden){
42213 this.hidden = hidden;
42214 this.linode.setStyle("display", hidden ? "none" : "");
42218 * Returns true if this tab is "hidden"
42219 * @return {Boolean}
42221 isHidden : function(){
42222 return this.hidden;
42226 * Returns the text for this tab
42229 getText : function(){
42233 autoSize : function(){
42234 //this.el.beginMeasure();
42235 this.textEl.setWidth(1);
42237 * #2804 [new] Tabs in Roojs
42238 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42240 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42241 //this.el.endMeasure();
42245 * Sets the text for the tab (Note: this also sets the tooltip text)
42246 * @param {String} text The tab's text and tooltip
42248 setText : function(text){
42250 this.textEl.update(text);
42251 this.setTooltip(text);
42252 //if(!this.tabPanel.resizeTabs){
42253 // this.autoSize();
42257 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42259 activate : function(){
42260 this.tabPanel.activate(this.id);
42264 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42266 disable : function(){
42267 if(this.tabPanel.active != this){
42268 this.disabled = true;
42269 this.status_node.addClass("disabled");
42274 * Enables this TabPanelItem if it was previously disabled.
42276 enable : function(){
42277 this.disabled = false;
42278 this.status_node.removeClass("disabled");
42282 * Sets the content for this TabPanelItem.
42283 * @param {String} content The content
42284 * @param {Boolean} loadScripts true to look for and load scripts
42286 setContent : function(content, loadScripts){
42287 this.bodyEl.update(content, loadScripts);
42291 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42292 * @return {Roo.UpdateManager} The UpdateManager
42294 getUpdateManager : function(){
42295 return this.bodyEl.getUpdateManager();
42299 * Set a URL to be used to load the content for this TabPanelItem.
42300 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42301 * @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)
42302 * @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)
42303 * @return {Roo.UpdateManager} The UpdateManager
42305 setUrl : function(url, params, loadOnce){
42306 if(this.refreshDelegate){
42307 this.un('activate', this.refreshDelegate);
42309 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42310 this.on("activate", this.refreshDelegate);
42311 return this.bodyEl.getUpdateManager();
42315 _handleRefresh : function(url, params, loadOnce){
42316 if(!loadOnce || !this.loaded){
42317 var updater = this.bodyEl.getUpdateManager();
42318 updater.update(url, params, this._setLoaded.createDelegate(this));
42323 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42324 * Will fail silently if the setUrl method has not been called.
42325 * This does not activate the panel, just updates its content.
42327 refresh : function(){
42328 if(this.refreshDelegate){
42329 this.loaded = false;
42330 this.refreshDelegate();
42335 _setLoaded : function(){
42336 this.loaded = true;
42340 closeClick : function(e){
42343 this.fireEvent("beforeclose", this, o);
42344 if(o.cancel !== true){
42345 this.tabPanel.removeTab(this.id);
42349 * The text displayed in the tooltip for the close icon.
42352 closeText : "Close this tab"
42355 * This script refer to:
42356 * Title: International Telephone Input
42357 * Author: Jack O'Connor
42358 * Code version: v12.1.12
42359 * Availability: https://github.com/jackocnr/intl-tel-input.git
42362 Roo.bootstrap.PhoneInputData = function() {
42365 "Afghanistan (افغانستان)",
42370 "Albania (Shqipëri)",
42375 "Algeria (الجزائر)",
42400 "Antigua and Barbuda",
42410 "Armenia (Հայաստան)",
42426 "Austria (Österreich)",
42431 "Azerbaijan (Azərbaycan)",
42441 "Bahrain (البحرين)",
42446 "Bangladesh (বাংলাদেশ)",
42456 "Belarus (Беларусь)",
42461 "Belgium (België)",
42491 "Bosnia and Herzegovina (Босна и Херцеговина)",
42506 "British Indian Ocean Territory",
42511 "British Virgin Islands",
42521 "Bulgaria (България)",
42531 "Burundi (Uburundi)",
42536 "Cambodia (កម្ពុជា)",
42541 "Cameroon (Cameroun)",
42550 ["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"]
42553 "Cape Verde (Kabu Verdi)",
42558 "Caribbean Netherlands",
42569 "Central African Republic (République centrafricaine)",
42589 "Christmas Island",
42595 "Cocos (Keeling) Islands",
42606 "Comoros (جزر القمر)",
42611 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42616 "Congo (Republic) (Congo-Brazzaville)",
42636 "Croatia (Hrvatska)",
42657 "Czech Republic (Česká republika)",
42662 "Denmark (Danmark)",
42677 "Dominican Republic (República Dominicana)",
42681 ["809", "829", "849"]
42699 "Equatorial Guinea (Guinea Ecuatorial)",
42719 "Falkland Islands (Islas Malvinas)",
42724 "Faroe Islands (Føroyar)",
42745 "French Guiana (Guyane française)",
42750 "French Polynesia (Polynésie française)",
42765 "Georgia (საქართველო)",
42770 "Germany (Deutschland)",
42790 "Greenland (Kalaallit Nunaat)",
42827 "Guinea-Bissau (Guiné Bissau)",
42852 "Hungary (Magyarország)",
42857 "Iceland (Ísland)",
42877 "Iraq (العراق)",
42893 "Israel (ישראל)",
42920 "Jordan (الأردن)",
42925 "Kazakhstan (Казахстан)",
42946 "Kuwait (الكويت)",
42951 "Kyrgyzstan (Кыргызстан)",
42961 "Latvia (Latvija)",
42966 "Lebanon (لبنان)",
42981 "Libya (ليبيا)",
42991 "Lithuania (Lietuva)",
43006 "Macedonia (FYROM) (Македонија)",
43011 "Madagascar (Madagasikara)",
43041 "Marshall Islands",
43051 "Mauritania (موريتانيا)",
43056 "Mauritius (Moris)",
43077 "Moldova (Republica Moldova)",
43087 "Mongolia (Монгол)",
43092 "Montenegro (Crna Gora)",
43102 "Morocco (المغرب)",
43108 "Mozambique (Moçambique)",
43113 "Myanmar (Burma) (မြန်မာ)",
43118 "Namibia (Namibië)",
43133 "Netherlands (Nederland)",
43138 "New Caledonia (Nouvelle-Calédonie)",
43173 "North Korea (조선 민주주의 인민 공화국)",
43178 "Northern Mariana Islands",
43194 "Pakistan (پاکستان)",
43204 "Palestine (فلسطين)",
43214 "Papua New Guinea",
43256 "Réunion (La Réunion)",
43262 "Romania (România)",
43278 "Saint Barthélemy",
43289 "Saint Kitts and Nevis",
43299 "Saint Martin (Saint-Martin (partie française))",
43305 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43310 "Saint Vincent and the Grenadines",
43325 "São Tomé and Príncipe (São Tomé e Príncipe)",
43330 "Saudi Arabia (المملكة العربية السعودية)",
43335 "Senegal (Sénégal)",
43365 "Slovakia (Slovensko)",
43370 "Slovenia (Slovenija)",
43380 "Somalia (Soomaaliya)",
43390 "South Korea (대한민국)",
43395 "South Sudan (جنوب السودان)",
43405 "Sri Lanka (ශ්රී ලංකාව)",
43410 "Sudan (السودان)",
43420 "Svalbard and Jan Mayen",
43431 "Sweden (Sverige)",
43436 "Switzerland (Schweiz)",
43441 "Syria (سوريا)",
43486 "Trinidad and Tobago",
43491 "Tunisia (تونس)",
43496 "Turkey (Türkiye)",
43506 "Turks and Caicos Islands",
43516 "U.S. Virgin Islands",
43526 "Ukraine (Україна)",
43531 "United Arab Emirates (الإمارات العربية المتحدة)",
43553 "Uzbekistan (Oʻzbekiston)",
43563 "Vatican City (Città del Vaticano)",
43574 "Vietnam (Việt Nam)",
43579 "Wallis and Futuna (Wallis-et-Futuna)",
43584 "Western Sahara (الصحراء الغربية)",
43590 "Yemen (اليمن)",
43614 * This script refer to:
43615 * Title: International Telephone Input
43616 * Author: Jack O'Connor
43617 * Code version: v12.1.12
43618 * Availability: https://github.com/jackocnr/intl-tel-input.git
43622 * @class Roo.bootstrap.PhoneInput
43623 * @extends Roo.bootstrap.TriggerField
43624 * An input with International dial-code selection
43626 * @cfg {String} defaultDialCode default '+852'
43627 * @cfg {Array} preferedCountries default []
43630 * Create a new PhoneInput.
43631 * @param {Object} config Configuration options
43634 Roo.bootstrap.PhoneInput = function(config) {
43635 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43638 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43640 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43642 listWidth: undefined,
43644 selectedClass: 'active',
43646 invalidClass : "has-warning",
43648 validClass: 'has-success',
43650 allowed: '0123456789',
43655 * @cfg {String} defaultDialCode The default dial code when initializing the input
43657 defaultDialCode: '+852',
43660 * @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
43662 preferedCountries: false,
43664 getAutoCreate : function()
43666 var data = Roo.bootstrap.PhoneInputData();
43667 var align = this.labelAlign || this.parentLabelAlign();
43670 this.allCountries = [];
43671 this.dialCodeMapping = [];
43673 for (var i = 0; i < data.length; i++) {
43675 this.allCountries[i] = {
43679 priority: c[3] || 0,
43680 areaCodes: c[4] || null
43682 this.dialCodeMapping[c[2]] = {
43685 priority: c[3] || 0,
43686 areaCodes: c[4] || null
43698 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43699 maxlength: this.max_length,
43700 cls : 'form-control tel-input',
43701 autocomplete: 'new-password'
43704 var hiddenInput = {
43707 cls: 'hidden-tel-input'
43711 hiddenInput.name = this.name;
43714 if (this.disabled) {
43715 input.disabled = true;
43718 var flag_container = {
43735 cls: this.hasFeedback ? 'has-feedback' : '',
43741 cls: 'dial-code-holder',
43748 cls: 'roo-select2-container input-group',
43755 if (this.fieldLabel.length) {
43758 tooltip: 'This field is required'
43764 cls: 'control-label',
43770 html: this.fieldLabel
43773 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43779 if(this.indicatorpos == 'right') {
43780 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43787 if(align == 'left') {
43795 if(this.labelWidth > 12){
43796 label.style = "width: " + this.labelWidth + 'px';
43798 if(this.labelWidth < 13 && this.labelmd == 0){
43799 this.labelmd = this.labelWidth;
43801 if(this.labellg > 0){
43802 label.cls += ' col-lg-' + this.labellg;
43803 input.cls += ' col-lg-' + (12 - this.labellg);
43805 if(this.labelmd > 0){
43806 label.cls += ' col-md-' + this.labelmd;
43807 container.cls += ' col-md-' + (12 - this.labelmd);
43809 if(this.labelsm > 0){
43810 label.cls += ' col-sm-' + this.labelsm;
43811 container.cls += ' col-sm-' + (12 - this.labelsm);
43813 if(this.labelxs > 0){
43814 label.cls += ' col-xs-' + this.labelxs;
43815 container.cls += ' col-xs-' + (12 - this.labelxs);
43825 var settings = this;
43827 ['xs','sm','md','lg'].map(function(size){
43828 if (settings[size]) {
43829 cfg.cls += ' col-' + size + '-' + settings[size];
43833 this.store = new Roo.data.Store({
43834 proxy : new Roo.data.MemoryProxy({}),
43835 reader : new Roo.data.JsonReader({
43846 'name' : 'dialCode',
43850 'name' : 'priority',
43854 'name' : 'areaCodes',
43861 if(!this.preferedCountries) {
43862 this.preferedCountries = [
43869 var p = this.preferedCountries.reverse();
43872 for (var i = 0; i < p.length; i++) {
43873 for (var j = 0; j < this.allCountries.length; j++) {
43874 if(this.allCountries[j].iso2 == p[i]) {
43875 var t = this.allCountries[j];
43876 this.allCountries.splice(j,1);
43877 this.allCountries.unshift(t);
43883 this.store.proxy.data = {
43885 data: this.allCountries
43891 initEvents : function()
43894 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43896 this.indicator = this.indicatorEl();
43897 this.flag = this.flagEl();
43898 this.dialCodeHolder = this.dialCodeHolderEl();
43900 this.trigger = this.el.select('div.flag-box',true).first();
43901 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43906 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43907 _this.list.setWidth(lw);
43910 this.list.on('mouseover', this.onViewOver, this);
43911 this.list.on('mousemove', this.onViewMove, this);
43912 this.inputEl().on("keyup", this.onKeyUp, this);
43913 this.inputEl().on("keypress", this.onKeyPress, this);
43915 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43917 this.view = new Roo.View(this.list, this.tpl, {
43918 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43921 this.view.on('click', this.onViewClick, this);
43922 this.setValue(this.defaultDialCode);
43925 onTriggerClick : function(e)
43927 Roo.log('trigger click');
43932 if(this.isExpanded()){
43934 this.hasFocus = false;
43936 this.store.load({});
43937 this.hasFocus = true;
43942 isExpanded : function()
43944 return this.list.isVisible();
43947 collapse : function()
43949 if(!this.isExpanded()){
43953 Roo.get(document).un('mousedown', this.collapseIf, this);
43954 Roo.get(document).un('mousewheel', this.collapseIf, this);
43955 this.fireEvent('collapse', this);
43959 expand : function()
43963 if(this.isExpanded() || !this.hasFocus){
43967 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43968 this.list.setWidth(lw);
43971 this.restrictHeight();
43973 Roo.get(document).on('mousedown', this.collapseIf, this);
43974 Roo.get(document).on('mousewheel', this.collapseIf, this);
43976 this.fireEvent('expand', this);
43979 restrictHeight : function()
43981 this.list.alignTo(this.inputEl(), this.listAlign);
43982 this.list.alignTo(this.inputEl(), this.listAlign);
43985 onViewOver : function(e, t)
43987 if(this.inKeyMode){
43990 var item = this.view.findItemFromChild(t);
43993 var index = this.view.indexOf(item);
43994 this.select(index, false);
43999 onViewClick : function(view, doFocus, el, e)
44001 var index = this.view.getSelectedIndexes()[0];
44003 var r = this.store.getAt(index);
44006 this.onSelect(r, index);
44008 if(doFocus !== false && !this.blockFocus){
44009 this.inputEl().focus();
44013 onViewMove : function(e, t)
44015 this.inKeyMode = false;
44018 select : function(index, scrollIntoView)
44020 this.selectedIndex = index;
44021 this.view.select(index);
44022 if(scrollIntoView !== false){
44023 var el = this.view.getNode(index);
44025 this.list.scrollChildIntoView(el, false);
44030 createList : function()
44032 this.list = Roo.get(document.body).createChild({
44034 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44035 style: 'display:none'
44038 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44041 collapseIf : function(e)
44043 var in_combo = e.within(this.el);
44044 var in_list = e.within(this.list);
44045 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44047 if (in_combo || in_list || is_list) {
44053 onSelect : function(record, index)
44055 if(this.fireEvent('beforeselect', this, record, index) !== false){
44057 this.setFlagClass(record.data.iso2);
44058 this.setDialCode(record.data.dialCode);
44059 this.hasFocus = false;
44061 this.fireEvent('select', this, record, index);
44065 flagEl : function()
44067 var flag = this.el.select('div.flag',true).first();
44074 dialCodeHolderEl : function()
44076 var d = this.el.select('input.dial-code-holder',true).first();
44083 setDialCode : function(v)
44085 this.dialCodeHolder.dom.value = '+'+v;
44088 setFlagClass : function(n)
44090 this.flag.dom.className = 'flag '+n;
44093 getValue : function()
44095 var v = this.inputEl().getValue();
44096 if(this.dialCodeHolder) {
44097 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44102 setValue : function(v)
44104 var d = this.getDialCode(v);
44106 //invalid dial code
44107 if(v.length == 0 || !d || d.length == 0) {
44109 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44110 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44116 this.setFlagClass(this.dialCodeMapping[d].iso2);
44117 this.setDialCode(d);
44118 this.inputEl().dom.value = v.replace('+'+d,'');
44119 this.hiddenEl().dom.value = this.getValue();
44124 getDialCode : function(v)
44128 if (v.length == 0) {
44129 return this.dialCodeHolder.dom.value;
44133 if (v.charAt(0) != "+") {
44136 var numericChars = "";
44137 for (var i = 1; i < v.length; i++) {
44138 var c = v.charAt(i);
44141 if (this.dialCodeMapping[numericChars]) {
44142 dialCode = v.substr(1, i);
44144 if (numericChars.length == 4) {
44154 this.setValue(this.defaultDialCode);
44158 hiddenEl : function()
44160 return this.el.select('input.hidden-tel-input',true).first();
44163 // after setting val
44164 onKeyUp : function(e){
44165 this.setValue(this.getValue());
44168 onKeyPress : function(e){
44169 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44176 * @class Roo.bootstrap.MoneyField
44177 * @extends Roo.bootstrap.ComboBox
44178 * Bootstrap MoneyField class
44181 * Create a new MoneyField.
44182 * @param {Object} config Configuration options
44185 Roo.bootstrap.MoneyField = function(config) {
44187 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44191 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44194 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44196 allowDecimals : true,
44198 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44200 decimalSeparator : ".",
44202 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44204 decimalPrecision : 0,
44206 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44208 allowNegative : true,
44210 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44214 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44216 minValue : Number.NEGATIVE_INFINITY,
44218 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44220 maxValue : Number.MAX_VALUE,
44222 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44224 minText : "The minimum value for this field is {0}",
44226 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44228 maxText : "The maximum value for this field is {0}",
44230 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44231 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44233 nanText : "{0} is not a valid number",
44235 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44239 * @cfg {String} defaults currency of the MoneyField
44240 * value should be in lkey
44242 defaultCurrency : false,
44244 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44246 thousandsDelimiter : false,
44248 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44259 getAutoCreate : function()
44261 var align = this.labelAlign || this.parentLabelAlign();
44273 cls : 'form-control roo-money-amount-input',
44274 autocomplete: 'new-password'
44277 var hiddenInput = {
44281 cls: 'hidden-number-input'
44284 if(this.max_length) {
44285 input.maxlength = this.max_length;
44289 hiddenInput.name = this.name;
44292 if (this.disabled) {
44293 input.disabled = true;
44296 var clg = 12 - this.inputlg;
44297 var cmd = 12 - this.inputmd;
44298 var csm = 12 - this.inputsm;
44299 var cxs = 12 - this.inputxs;
44303 cls : 'row roo-money-field',
44307 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44311 cls: 'roo-select2-container input-group',
44315 cls : 'form-control roo-money-currency-input',
44316 autocomplete: 'new-password',
44318 name : this.currencyName
44322 cls : 'input-group-addon',
44336 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44340 cls: this.hasFeedback ? 'has-feedback' : '',
44351 if (this.fieldLabel.length) {
44354 tooltip: 'This field is required'
44360 cls: 'control-label',
44366 html: this.fieldLabel
44369 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44375 if(this.indicatorpos == 'right') {
44376 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44383 if(align == 'left') {
44391 if(this.labelWidth > 12){
44392 label.style = "width: " + this.labelWidth + 'px';
44394 if(this.labelWidth < 13 && this.labelmd == 0){
44395 this.labelmd = this.labelWidth;
44397 if(this.labellg > 0){
44398 label.cls += ' col-lg-' + this.labellg;
44399 input.cls += ' col-lg-' + (12 - this.labellg);
44401 if(this.labelmd > 0){
44402 label.cls += ' col-md-' + this.labelmd;
44403 container.cls += ' col-md-' + (12 - this.labelmd);
44405 if(this.labelsm > 0){
44406 label.cls += ' col-sm-' + this.labelsm;
44407 container.cls += ' col-sm-' + (12 - this.labelsm);
44409 if(this.labelxs > 0){
44410 label.cls += ' col-xs-' + this.labelxs;
44411 container.cls += ' col-xs-' + (12 - this.labelxs);
44422 var settings = this;
44424 ['xs','sm','md','lg'].map(function(size){
44425 if (settings[size]) {
44426 cfg.cls += ' col-' + size + '-' + settings[size];
44433 initEvents : function()
44435 this.indicator = this.indicatorEl();
44437 this.initCurrencyEvent();
44439 this.initNumberEvent();
44442 initCurrencyEvent : function()
44445 throw "can not find store for combo";
44448 this.store = Roo.factory(this.store, Roo.data);
44449 this.store.parent = this;
44453 this.triggerEl = this.el.select('.input-group-addon', true).first();
44455 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44460 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44461 _this.list.setWidth(lw);
44464 this.list.on('mouseover', this.onViewOver, this);
44465 this.list.on('mousemove', this.onViewMove, this);
44466 this.list.on('scroll', this.onViewScroll, this);
44469 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44472 this.view = new Roo.View(this.list, this.tpl, {
44473 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44476 this.view.on('click', this.onViewClick, this);
44478 this.store.on('beforeload', this.onBeforeLoad, this);
44479 this.store.on('load', this.onLoad, this);
44480 this.store.on('loadexception', this.onLoadException, this);
44482 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44483 "up" : function(e){
44484 this.inKeyMode = true;
44488 "down" : function(e){
44489 if(!this.isExpanded()){
44490 this.onTriggerClick();
44492 this.inKeyMode = true;
44497 "enter" : function(e){
44500 if(this.fireEvent("specialkey", this, e)){
44501 this.onViewClick(false);
44507 "esc" : function(e){
44511 "tab" : function(e){
44514 if(this.fireEvent("specialkey", this, e)){
44515 this.onViewClick(false);
44523 doRelay : function(foo, bar, hname){
44524 if(hname == 'down' || this.scope.isExpanded()){
44525 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44533 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44537 initNumberEvent : function(e)
44539 this.inputEl().on("keydown" , this.fireKey, this);
44540 this.inputEl().on("focus", this.onFocus, this);
44541 this.inputEl().on("blur", this.onBlur, this);
44543 this.inputEl().relayEvent('keyup', this);
44545 if(this.indicator){
44546 this.indicator.addClass('invisible');
44549 this.originalValue = this.getValue();
44551 if(this.validationEvent == 'keyup'){
44552 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44553 this.inputEl().on('keyup', this.filterValidation, this);
44555 else if(this.validationEvent !== false){
44556 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44559 if(this.selectOnFocus){
44560 this.on("focus", this.preFocus, this);
44563 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44564 this.inputEl().on("keypress", this.filterKeys, this);
44566 this.inputEl().relayEvent('keypress', this);
44569 var allowed = "0123456789";
44571 if(this.allowDecimals){
44572 allowed += this.decimalSeparator;
44575 if(this.allowNegative){
44579 if(this.thousandsDelimiter) {
44583 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44585 var keyPress = function(e){
44587 var k = e.getKey();
44589 var c = e.getCharCode();
44592 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44593 allowed.indexOf(String.fromCharCode(c)) === -1
44599 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44603 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44608 this.inputEl().on("keypress", keyPress, this);
44612 onTriggerClick : function(e)
44619 this.loadNext = false;
44621 if(this.isExpanded()){
44626 this.hasFocus = true;
44628 if(this.triggerAction == 'all') {
44629 this.doQuery(this.allQuery, true);
44633 this.doQuery(this.getRawValue());
44636 getCurrency : function()
44638 var v = this.currencyEl().getValue();
44643 restrictHeight : function()
44645 this.list.alignTo(this.currencyEl(), this.listAlign);
44646 this.list.alignTo(this.currencyEl(), this.listAlign);
44649 onViewClick : function(view, doFocus, el, e)
44651 var index = this.view.getSelectedIndexes()[0];
44653 var r = this.store.getAt(index);
44656 this.onSelect(r, index);
44660 onSelect : function(record, index){
44662 if(this.fireEvent('beforeselect', this, record, index) !== false){
44664 this.setFromCurrencyData(index > -1 ? record.data : false);
44668 this.fireEvent('select', this, record, index);
44672 setFromCurrencyData : function(o)
44676 this.lastCurrency = o;
44678 if (this.currencyField) {
44679 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44681 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44684 this.lastSelectionText = currency;
44686 //setting default currency
44687 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44688 this.setCurrency(this.defaultCurrency);
44692 this.setCurrency(currency);
44695 setFromData : function(o)
44699 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44701 this.setFromCurrencyData(c);
44706 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44708 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44711 this.setValue(value);
44715 setCurrency : function(v)
44717 this.currencyValue = v;
44720 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44725 setValue : function(v)
44727 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44733 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44735 this.inputEl().dom.value = (v == '') ? '' :
44736 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44738 if(!this.allowZero && v === '0') {
44739 this.hiddenEl().dom.value = '';
44740 this.inputEl().dom.value = '';
44747 getRawValue : function()
44749 var v = this.inputEl().getValue();
44754 getValue : function()
44756 return this.fixPrecision(this.parseValue(this.getRawValue()));
44759 parseValue : function(value)
44761 if(this.thousandsDelimiter) {
44763 r = new RegExp(",", "g");
44764 value = value.replace(r, "");
44767 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44768 return isNaN(value) ? '' : value;
44772 fixPrecision : function(value)
44774 if(this.thousandsDelimiter) {
44776 r = new RegExp(",", "g");
44777 value = value.replace(r, "");
44780 var nan = isNaN(value);
44782 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44783 return nan ? '' : value;
44785 return parseFloat(value).toFixed(this.decimalPrecision);
44788 decimalPrecisionFcn : function(v)
44790 return Math.floor(v);
44793 validateValue : function(value)
44795 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44799 var num = this.parseValue(value);
44802 this.markInvalid(String.format(this.nanText, value));
44806 if(num < this.minValue){
44807 this.markInvalid(String.format(this.minText, this.minValue));
44811 if(num > this.maxValue){
44812 this.markInvalid(String.format(this.maxText, this.maxValue));
44819 validate : function()
44821 if(this.disabled || this.allowBlank){
44826 var currency = this.getCurrency();
44828 if(this.validateValue(this.getRawValue()) && currency.length){
44833 this.markInvalid();
44837 getName: function()
44842 beforeBlur : function()
44848 var v = this.parseValue(this.getRawValue());
44855 onBlur : function()
44859 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44860 //this.el.removeClass(this.focusClass);
44863 this.hasFocus = false;
44865 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44869 var v = this.getValue();
44871 if(String(v) !== String(this.startValue)){
44872 this.fireEvent('change', this, v, this.startValue);
44875 this.fireEvent("blur", this);
44878 inputEl : function()
44880 return this.el.select('.roo-money-amount-input', true).first();
44883 currencyEl : function()
44885 return this.el.select('.roo-money-currency-input', true).first();
44888 hiddenEl : function()
44890 return this.el.select('input.hidden-number-input',true).first();
44894 * @class Roo.bootstrap.BezierSignature
44895 * @extends Roo.bootstrap.Component
44896 * Bootstrap BezierSignature class
44897 * This script refer to:
44898 * Title: Signature Pad
44900 * Availability: https://github.com/szimek/signature_pad
44903 * Create a new BezierSignature
44904 * @param {Object} config The config object
44907 Roo.bootstrap.BezierSignature = function(config){
44908 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44914 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44921 mouse_btn_down: true,
44924 * @cfg {int} canvas height
44926 canvas_height: '200px',
44929 * @cfg {float|function} Radius of a single dot.
44934 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44939 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44944 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44949 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44954 * @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.
44956 bg_color: 'rgba(0, 0, 0, 0)',
44959 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44961 dot_color: 'black',
44964 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44966 velocity_filter_weight: 0.7,
44969 * @cfg {function} Callback when stroke begin.
44974 * @cfg {function} Callback when stroke end.
44978 getAutoCreate : function()
44980 var cls = 'roo-signature column';
44983 cls += ' ' + this.cls;
44993 for(var i = 0; i < col_sizes.length; i++) {
44994 if(this[col_sizes[i]]) {
44995 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45005 cls: 'roo-signature-body',
45009 cls: 'roo-signature-body-canvas',
45010 height: this.canvas_height,
45011 width: this.canvas_width
45018 style: 'display: none'
45026 initEvents: function()
45028 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45030 var canvas = this.canvasEl();
45032 // mouse && touch event swapping...
45033 canvas.dom.style.touchAction = 'none';
45034 canvas.dom.style.msTouchAction = 'none';
45036 this.mouse_btn_down = false;
45037 canvas.on('mousedown', this._handleMouseDown, this);
45038 canvas.on('mousemove', this._handleMouseMove, this);
45039 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45041 if (window.PointerEvent) {
45042 canvas.on('pointerdown', this._handleMouseDown, this);
45043 canvas.on('pointermove', this._handleMouseMove, this);
45044 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45047 if ('ontouchstart' in window) {
45048 canvas.on('touchstart', this._handleTouchStart, this);
45049 canvas.on('touchmove', this._handleTouchMove, this);
45050 canvas.on('touchend', this._handleTouchEnd, this);
45053 Roo.EventManager.onWindowResize(this.resize, this, true);
45055 // file input event
45056 this.fileEl().on('change', this.uploadImage, this);
45063 resize: function(){
45065 var canvas = this.canvasEl().dom;
45066 var ctx = this.canvasElCtx();
45067 var img_data = false;
45069 if(canvas.width > 0) {
45070 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45072 // setting canvas width will clean img data
45075 var style = window.getComputedStyle ?
45076 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45078 var padding_left = parseInt(style.paddingLeft) || 0;
45079 var padding_right = parseInt(style.paddingRight) || 0;
45081 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45084 ctx.putImageData(img_data, 0, 0);
45088 _handleMouseDown: function(e)
45090 if (e.browserEvent.which === 1) {
45091 this.mouse_btn_down = true;
45092 this.strokeBegin(e);
45096 _handleMouseMove: function (e)
45098 if (this.mouse_btn_down) {
45099 this.strokeMoveUpdate(e);
45103 _handleMouseUp: function (e)
45105 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45106 this.mouse_btn_down = false;
45111 _handleTouchStart: function (e) {
45113 e.preventDefault();
45114 if (e.browserEvent.targetTouches.length === 1) {
45115 // var touch = e.browserEvent.changedTouches[0];
45116 // this.strokeBegin(touch);
45118 this.strokeBegin(e); // assume e catching the correct xy...
45122 _handleTouchMove: function (e) {
45123 e.preventDefault();
45124 // var touch = event.targetTouches[0];
45125 // _this._strokeMoveUpdate(touch);
45126 this.strokeMoveUpdate(e);
45129 _handleTouchEnd: function (e) {
45130 var wasCanvasTouched = e.target === this.canvasEl().dom;
45131 if (wasCanvasTouched) {
45132 e.preventDefault();
45133 // var touch = event.changedTouches[0];
45134 // _this._strokeEnd(touch);
45139 reset: function () {
45140 this._lastPoints = [];
45141 this._lastVelocity = 0;
45142 this._lastWidth = (this.min_width + this.max_width) / 2;
45143 this.canvasElCtx().fillStyle = this.dot_color;
45146 strokeMoveUpdate: function(e)
45148 this.strokeUpdate(e);
45150 if (this.throttle) {
45151 this.throttleStroke(this.strokeUpdate, this.throttle);
45154 this.strokeUpdate(e);
45158 strokeBegin: function(e)
45160 var newPointGroup = {
45161 color: this.dot_color,
45165 if (typeof this.onBegin === 'function') {
45169 this.curve_data.push(newPointGroup);
45171 this.strokeUpdate(e);
45174 strokeUpdate: function(e)
45176 var rect = this.canvasEl().dom.getBoundingClientRect();
45177 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45178 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45179 var lastPoints = lastPointGroup.points;
45180 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45181 var isLastPointTooClose = lastPoint
45182 ? point.distanceTo(lastPoint) <= this.min_distance
45184 var color = lastPointGroup.color;
45185 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45186 var curve = this.addPoint(point);
45188 this.drawDot({color: color, point: point});
45191 this.drawCurve({color: color, curve: curve});
45201 strokeEnd: function(e)
45203 this.strokeUpdate(e);
45204 if (typeof this.onEnd === 'function') {
45209 addPoint: function (point) {
45210 var _lastPoints = this._lastPoints;
45211 _lastPoints.push(point);
45212 if (_lastPoints.length > 2) {
45213 if (_lastPoints.length === 3) {
45214 _lastPoints.unshift(_lastPoints[0]);
45216 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45217 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45218 _lastPoints.shift();
45224 calculateCurveWidths: function (startPoint, endPoint) {
45225 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45226 (1 - this.velocity_filter_weight) * this._lastVelocity;
45228 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45231 start: this._lastWidth
45234 this._lastVelocity = velocity;
45235 this._lastWidth = newWidth;
45239 drawDot: function (_a) {
45240 var color = _a.color, point = _a.point;
45241 var ctx = this.canvasElCtx();
45242 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45244 this.drawCurveSegment(point.x, point.y, width);
45246 ctx.fillStyle = color;
45250 drawCurve: function (_a) {
45251 var color = _a.color, curve = _a.curve;
45252 var ctx = this.canvasElCtx();
45253 var widthDelta = curve.endWidth - curve.startWidth;
45254 var drawSteps = Math.floor(curve.length()) * 2;
45256 ctx.fillStyle = color;
45257 for (var i = 0; i < drawSteps; i += 1) {
45258 var t = i / drawSteps;
45264 var x = uuu * curve.startPoint.x;
45265 x += 3 * uu * t * curve.control1.x;
45266 x += 3 * u * tt * curve.control2.x;
45267 x += ttt * curve.endPoint.x;
45268 var y = uuu * curve.startPoint.y;
45269 y += 3 * uu * t * curve.control1.y;
45270 y += 3 * u * tt * curve.control2.y;
45271 y += ttt * curve.endPoint.y;
45272 var width = curve.startWidth + ttt * widthDelta;
45273 this.drawCurveSegment(x, y, width);
45279 drawCurveSegment: function (x, y, width) {
45280 var ctx = this.canvasElCtx();
45282 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45283 this.is_empty = false;
45288 var ctx = this.canvasElCtx();
45289 var canvas = this.canvasEl().dom;
45290 ctx.fillStyle = this.bg_color;
45291 ctx.clearRect(0, 0, canvas.width, canvas.height);
45292 ctx.fillRect(0, 0, canvas.width, canvas.height);
45293 this.curve_data = [];
45295 this.is_empty = true;
45300 return this.el.select('input',true).first();
45303 canvasEl: function()
45305 return this.el.select('canvas',true).first();
45308 canvasElCtx: function()
45310 return this.el.select('canvas',true).first().dom.getContext('2d');
45313 getImage: function(type)
45315 if(this.is_empty) {
45320 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45323 drawFromImage: function(img_src)
45325 var img = new Image();
45327 img.onload = function(){
45328 this.canvasElCtx().drawImage(img, 0, 0);
45333 this.is_empty = false;
45336 selectImage: function()
45338 this.fileEl().dom.click();
45341 uploadImage: function(e)
45343 var reader = new FileReader();
45345 reader.onload = function(e){
45346 var img = new Image();
45347 img.onload = function(){
45349 this.canvasElCtx().drawImage(img, 0, 0);
45351 img.src = e.target.result;
45354 reader.readAsDataURL(e.target.files[0]);
45357 // Bezier Point Constructor
45358 Point: (function () {
45359 function Point(x, y, time) {
45362 this.time = time || Date.now();
45364 Point.prototype.distanceTo = function (start) {
45365 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45367 Point.prototype.equals = function (other) {
45368 return this.x === other.x && this.y === other.y && this.time === other.time;
45370 Point.prototype.velocityFrom = function (start) {
45371 return this.time !== start.time
45372 ? this.distanceTo(start) / (this.time - start.time)
45379 // Bezier Constructor
45380 Bezier: (function () {
45381 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45382 this.startPoint = startPoint;
45383 this.control2 = control2;
45384 this.control1 = control1;
45385 this.endPoint = endPoint;
45386 this.startWidth = startWidth;
45387 this.endWidth = endWidth;
45389 Bezier.fromPoints = function (points, widths, scope) {
45390 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45391 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45392 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45394 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45395 var dx1 = s1.x - s2.x;
45396 var dy1 = s1.y - s2.y;
45397 var dx2 = s2.x - s3.x;
45398 var dy2 = s2.y - s3.y;
45399 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45400 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45401 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45402 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45403 var dxm = m1.x - m2.x;
45404 var dym = m1.y - m2.y;
45405 var k = l2 / (l1 + l2);
45406 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45407 var tx = s2.x - cm.x;
45408 var ty = s2.y - cm.y;
45410 c1: new scope.Point(m1.x + tx, m1.y + ty),
45411 c2: new scope.Point(m2.x + tx, m2.y + ty)
45414 Bezier.prototype.length = function () {
45419 for (var i = 0; i <= steps; i += 1) {
45421 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45422 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45424 var xdiff = cx - px;
45425 var ydiff = cy - py;
45426 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45433 Bezier.prototype.point = function (t, start, c1, c2, end) {
45434 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45435 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45436 + (3.0 * c2 * (1.0 - t) * t * t)
45437 + (end * t * t * t);
45442 throttleStroke: function(fn, wait) {
45443 if (wait === void 0) { wait = 250; }
45445 var timeout = null;
45449 var later = function () {
45450 previous = Date.now();
45452 result = fn.apply(storedContext, storedArgs);
45454 storedContext = null;
45458 return function wrapper() {
45460 for (var _i = 0; _i < arguments.length; _i++) {
45461 args[_i] = arguments[_i];
45463 var now = Date.now();
45464 var remaining = wait - (now - previous);
45465 storedContext = this;
45467 if (remaining <= 0 || remaining > wait) {
45469 clearTimeout(timeout);
45473 result = fn.apply(storedContext, storedArgs);
45475 storedContext = null;
45479 else if (!timeout) {
45480 timeout = window.setTimeout(later, remaining);