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.DataReader} 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
15521 * Base class for reading structured data from a data source. This class is intended to be
15522 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15525 Roo.data.DataReader = function(meta, recordType){
15529 this.recordType = recordType instanceof Array ?
15530 Roo.data.Record.create(recordType) : recordType;
15533 Roo.data.DataReader.prototype = {
15536 readerType : 'Data',
15538 * Create an empty record
15539 * @param {Object} data (optional) - overlay some values
15540 * @return {Roo.data.Record} record created.
15542 newRow : function(d) {
15544 this.recordType.prototype.fields.each(function(c) {
15546 case 'int' : da[c.name] = 0; break;
15547 case 'date' : da[c.name] = new Date(); break;
15548 case 'float' : da[c.name] = 0.0; break;
15549 case 'boolean' : da[c.name] = false; break;
15550 default : da[c.name] = ""; break;
15554 return new this.recordType(Roo.apply(da, d));
15560 * Ext JS Library 1.1.1
15561 * Copyright(c) 2006-2007, Ext JS, LLC.
15563 * Originally Released Under LGPL - original licence link has changed is not relivant.
15566 * <script type="text/javascript">
15570 * @class Roo.data.DataProxy
15571 * @extends Roo.data.Observable
15573 * This class is an abstract base class for implementations which provide retrieval of
15574 * unformatted data objects.<br>
15576 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15577 * (of the appropriate type which knows how to parse the data object) to provide a block of
15578 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15580 * Custom implementations must implement the load method as described in
15581 * {@link Roo.data.HttpProxy#load}.
15583 Roo.data.DataProxy = function(){
15586 * @event beforeload
15587 * Fires before a network request is made to retrieve a data object.
15588 * @param {Object} This DataProxy object.
15589 * @param {Object} params The params parameter to the load function.
15594 * Fires before the load method's callback is called.
15595 * @param {Object} This DataProxy object.
15596 * @param {Object} o The data object.
15597 * @param {Object} arg The callback argument object passed to the load function.
15601 * @event loadexception
15602 * Fires if an Exception occurs during data retrieval.
15603 * @param {Object} This DataProxy object.
15604 * @param {Object} o The data object.
15605 * @param {Object} arg The callback argument object passed to the load function.
15606 * @param {Object} e The Exception.
15608 loadexception : true
15610 Roo.data.DataProxy.superclass.constructor.call(this);
15613 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15616 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15620 * Ext JS Library 1.1.1
15621 * Copyright(c) 2006-2007, Ext JS, LLC.
15623 * Originally Released Under LGPL - original licence link has changed is not relivant.
15626 * <script type="text/javascript">
15629 * @class Roo.data.MemoryProxy
15630 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15631 * to the Reader when its load method is called.
15633 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15635 Roo.data.MemoryProxy = function(data){
15639 Roo.data.MemoryProxy.superclass.constructor.call(this);
15643 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15646 * Load data from the requested source (in this case an in-memory
15647 * data object passed to the constructor), read the data object into
15648 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15649 * process that block using the passed callback.
15650 * @param {Object} params This parameter is not used by the MemoryProxy class.
15651 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15652 * object into a block of Roo.data.Records.
15653 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15654 * The function must be passed <ul>
15655 * <li>The Record block object</li>
15656 * <li>The "arg" argument from the load function</li>
15657 * <li>A boolean success indicator</li>
15659 * @param {Object} scope The scope in which to call the callback
15660 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15662 load : function(params, reader, callback, scope, arg){
15663 params = params || {};
15666 result = reader.readRecords(params.data ? params.data :this.data);
15668 this.fireEvent("loadexception", this, arg, null, e);
15669 callback.call(scope, null, arg, false);
15672 callback.call(scope, result, arg, true);
15676 update : function(params, records){
15681 * Ext JS Library 1.1.1
15682 * Copyright(c) 2006-2007, Ext JS, LLC.
15684 * Originally Released Under LGPL - original licence link has changed is not relivant.
15687 * <script type="text/javascript">
15690 * @class Roo.data.HttpProxy
15691 * @extends Roo.data.DataProxy
15692 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15693 * configured to reference a certain URL.<br><br>
15695 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15696 * from which the running page was served.<br><br>
15698 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15700 * Be aware that to enable the browser to parse an XML document, the server must set
15701 * the Content-Type header in the HTTP response to "text/xml".
15703 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15704 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15705 * will be used to make the request.
15707 Roo.data.HttpProxy = function(conn){
15708 Roo.data.HttpProxy.superclass.constructor.call(this);
15709 // is conn a conn config or a real conn?
15711 this.useAjax = !conn || !conn.events;
15715 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15716 // thse are take from connection...
15719 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15722 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15723 * extra parameters to each request made by this object. (defaults to undefined)
15726 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15727 * to each request made by this object. (defaults to undefined)
15730 * @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)
15733 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15736 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15742 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15746 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15747 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15748 * a finer-grained basis than the DataProxy events.
15750 getConnection : function(){
15751 return this.useAjax ? Roo.Ajax : this.conn;
15755 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15756 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15757 * process that block using the passed callback.
15758 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15759 * for the request to the remote server.
15760 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15761 * object into a block of Roo.data.Records.
15762 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15763 * The function must be passed <ul>
15764 * <li>The Record block object</li>
15765 * <li>The "arg" argument from the load function</li>
15766 * <li>A boolean success indicator</li>
15768 * @param {Object} scope The scope in which to call the callback
15769 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15771 load : function(params, reader, callback, scope, arg){
15772 if(this.fireEvent("beforeload", this, params) !== false){
15774 params : params || {},
15776 callback : callback,
15781 callback : this.loadResponse,
15785 Roo.applyIf(o, this.conn);
15786 if(this.activeRequest){
15787 Roo.Ajax.abort(this.activeRequest);
15789 this.activeRequest = Roo.Ajax.request(o);
15791 this.conn.request(o);
15794 callback.call(scope||this, null, arg, false);
15799 loadResponse : function(o, success, response){
15800 delete this.activeRequest;
15802 this.fireEvent("loadexception", this, o, response);
15803 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15808 result = o.reader.read(response);
15810 this.fireEvent("loadexception", this, o, response, e);
15811 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15815 this.fireEvent("load", this, o, o.request.arg);
15816 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15820 update : function(dataSet){
15825 updateResponse : function(dataSet){
15830 * Ext JS Library 1.1.1
15831 * Copyright(c) 2006-2007, Ext JS, LLC.
15833 * Originally Released Under LGPL - original licence link has changed is not relivant.
15836 * <script type="text/javascript">
15840 * @class Roo.data.ScriptTagProxy
15841 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15842 * other than the originating domain of the running page.<br><br>
15844 * <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
15845 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15847 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15848 * source code that is used as the source inside a <script> tag.<br><br>
15850 * In order for the browser to process the returned data, the server must wrap the data object
15851 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15852 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15853 * depending on whether the callback name was passed:
15856 boolean scriptTag = false;
15857 String cb = request.getParameter("callback");
15860 response.setContentType("text/javascript");
15862 response.setContentType("application/x-json");
15864 Writer out = response.getWriter();
15866 out.write(cb + "(");
15868 out.print(dataBlock.toJsonString());
15875 * @param {Object} config A configuration object.
15877 Roo.data.ScriptTagProxy = function(config){
15878 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15879 Roo.apply(this, config);
15880 this.head = document.getElementsByTagName("head")[0];
15883 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15885 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15887 * @cfg {String} url The URL from which to request the data object.
15890 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15894 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15895 * the server the name of the callback function set up by the load call to process the returned data object.
15896 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15897 * javascript output which calls this named function passing the data object as its only parameter.
15899 callbackParam : "callback",
15901 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15902 * name to the request.
15907 * Load data from the configured URL, read the data object into
15908 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15909 * process that block using the passed callback.
15910 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15911 * for the request to the remote server.
15912 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15913 * object into a block of Roo.data.Records.
15914 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15915 * The function must be passed <ul>
15916 * <li>The Record block object</li>
15917 * <li>The "arg" argument from the load function</li>
15918 * <li>A boolean success indicator</li>
15920 * @param {Object} scope The scope in which to call the callback
15921 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15923 load : function(params, reader, callback, scope, arg){
15924 if(this.fireEvent("beforeload", this, params) !== false){
15926 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15928 var url = this.url;
15929 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15931 url += "&_dc=" + (new Date().getTime());
15933 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15936 cb : "stcCallback"+transId,
15937 scriptId : "stcScript"+transId,
15941 callback : callback,
15947 window[trans.cb] = function(o){
15948 conn.handleResponse(o, trans);
15951 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15953 if(this.autoAbort !== false){
15957 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15959 var script = document.createElement("script");
15960 script.setAttribute("src", url);
15961 script.setAttribute("type", "text/javascript");
15962 script.setAttribute("id", trans.scriptId);
15963 this.head.appendChild(script);
15965 this.trans = trans;
15967 callback.call(scope||this, null, arg, false);
15972 isLoading : function(){
15973 return this.trans ? true : false;
15977 * Abort the current server request.
15979 abort : function(){
15980 if(this.isLoading()){
15981 this.destroyTrans(this.trans);
15986 destroyTrans : function(trans, isLoaded){
15987 this.head.removeChild(document.getElementById(trans.scriptId));
15988 clearTimeout(trans.timeoutId);
15990 window[trans.cb] = undefined;
15992 delete window[trans.cb];
15995 // if hasn't been loaded, wait for load to remove it to prevent script error
15996 window[trans.cb] = function(){
15997 window[trans.cb] = undefined;
15999 delete window[trans.cb];
16006 handleResponse : function(o, trans){
16007 this.trans = false;
16008 this.destroyTrans(trans, true);
16011 result = trans.reader.readRecords(o);
16013 this.fireEvent("loadexception", this, o, trans.arg, e);
16014 trans.callback.call(trans.scope||window, null, trans.arg, false);
16017 this.fireEvent("load", this, o, trans.arg);
16018 trans.callback.call(trans.scope||window, result, trans.arg, true);
16022 handleFailure : function(trans){
16023 this.trans = false;
16024 this.destroyTrans(trans, false);
16025 this.fireEvent("loadexception", this, null, trans.arg);
16026 trans.callback.call(trans.scope||window, null, trans.arg, false);
16030 * Ext JS Library 1.1.1
16031 * Copyright(c) 2006-2007, Ext JS, LLC.
16033 * Originally Released Under LGPL - original licence link has changed is not relivant.
16036 * <script type="text/javascript">
16040 * @class Roo.data.JsonReader
16041 * @extends Roo.data.DataReader
16042 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16043 * based on mappings in a provided Roo.data.Record constructor.
16045 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16046 * in the reply previously.
16051 var RecordDef = Roo.data.Record.create([
16052 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16053 {name: 'occupation'} // This field will use "occupation" as the mapping.
16055 var myReader = new Roo.data.JsonReader({
16056 totalProperty: "results", // The property which contains the total dataset size (optional)
16057 root: "rows", // The property which contains an Array of row objects
16058 id: "id" // The property within each row object that provides an ID for the record (optional)
16062 * This would consume a JSON file like this:
16064 { 'results': 2, 'rows': [
16065 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16066 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16069 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16070 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16071 * paged from the remote server.
16072 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16073 * @cfg {String} root name of the property which contains the Array of row objects.
16074 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16075 * @cfg {Array} fields Array of field definition objects
16077 * Create a new JsonReader
16078 * @param {Object} meta Metadata configuration options
16079 * @param {Object} recordType Either an Array of field definition objects,
16080 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16082 Roo.data.JsonReader = function(meta, recordType){
16085 // set some defaults:
16086 Roo.applyIf(meta, {
16087 totalProperty: 'total',
16088 successProperty : 'success',
16093 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16095 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16097 readerType : 'Json',
16100 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16101 * Used by Store query builder to append _requestMeta to params.
16104 metaFromRemote : false,
16106 * This method is only used by a DataProxy which has retrieved data from a remote server.
16107 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16108 * @return {Object} data A data block which is used by an Roo.data.Store object as
16109 * a cache of Roo.data.Records.
16111 read : function(response){
16112 var json = response.responseText;
16114 var o = /* eval:var:o */ eval("("+json+")");
16116 throw {message: "JsonReader.read: Json object not found"};
16122 this.metaFromRemote = true;
16123 this.meta = o.metaData;
16124 this.recordType = Roo.data.Record.create(o.metaData.fields);
16125 this.onMetaChange(this.meta, this.recordType, o);
16127 return this.readRecords(o);
16130 // private function a store will implement
16131 onMetaChange : function(meta, recordType, o){
16138 simpleAccess: function(obj, subsc) {
16145 getJsonAccessor: function(){
16147 return function(expr) {
16149 return(re.test(expr))
16150 ? new Function("obj", "return obj." + expr)
16155 return Roo.emptyFn;
16160 * Create a data block containing Roo.data.Records from an XML document.
16161 * @param {Object} o An object which contains an Array of row objects in the property specified
16162 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16163 * which contains the total size of the dataset.
16164 * @return {Object} data A data block which is used by an Roo.data.Store object as
16165 * a cache of Roo.data.Records.
16167 readRecords : function(o){
16169 * After any data loads, the raw JSON data is available for further custom processing.
16173 var s = this.meta, Record = this.recordType,
16174 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16176 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16178 if(s.totalProperty) {
16179 this.getTotal = this.getJsonAccessor(s.totalProperty);
16181 if(s.successProperty) {
16182 this.getSuccess = this.getJsonAccessor(s.successProperty);
16184 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16186 var g = this.getJsonAccessor(s.id);
16187 this.getId = function(rec) {
16189 return (r === undefined || r === "") ? null : r;
16192 this.getId = function(){return null;};
16195 for(var jj = 0; jj < fl; jj++){
16197 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16198 this.ef[jj] = this.getJsonAccessor(map);
16202 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16203 if(s.totalProperty){
16204 var vt = parseInt(this.getTotal(o), 10);
16209 if(s.successProperty){
16210 var vs = this.getSuccess(o);
16211 if(vs === false || vs === 'false'){
16216 for(var i = 0; i < c; i++){
16219 var id = this.getId(n);
16220 for(var j = 0; j < fl; j++){
16222 var v = this.ef[j](n);
16224 Roo.log('missing convert for ' + f.name);
16228 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16230 var record = new Record(values, id);
16232 records[i] = record;
16238 totalRecords : totalRecords
16241 // used when loading children.. @see loadDataFromChildren
16242 toLoadData: function(rec)
16244 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16245 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16246 return { data : data, total : data.length };
16251 * Ext JS Library 1.1.1
16252 * Copyright(c) 2006-2007, Ext JS, LLC.
16254 * Originally Released Under LGPL - original licence link has changed is not relivant.
16257 * <script type="text/javascript">
16261 * @class Roo.data.ArrayReader
16262 * @extends Roo.data.DataReader
16263 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16264 * Each element of that Array represents a row of data fields. The
16265 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16266 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16270 var RecordDef = Roo.data.Record.create([
16271 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16272 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16274 var myReader = new Roo.data.ArrayReader({
16275 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16279 * This would consume an Array like this:
16281 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16285 * Create a new JsonReader
16286 * @param {Object} meta Metadata configuration options.
16287 * @param {Object|Array} recordType Either an Array of field definition objects
16289 * @cfg {Array} fields Array of field definition objects
16290 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16291 * as specified to {@link Roo.data.Record#create},
16292 * or an {@link Roo.data.Record} object
16295 * created using {@link Roo.data.Record#create}.
16297 Roo.data.ArrayReader = function(meta, recordType)
16299 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16302 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16305 * Create a data block containing Roo.data.Records from an XML document.
16306 * @param {Object} o An Array of row objects which represents the dataset.
16307 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16308 * a cache of Roo.data.Records.
16310 readRecords : function(o)
16312 var sid = this.meta ? this.meta.id : null;
16313 var recordType = this.recordType, fields = recordType.prototype.fields;
16316 for(var i = 0; i < root.length; i++){
16319 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16320 for(var j = 0, jlen = fields.length; j < jlen; j++){
16321 var f = fields.items[j];
16322 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16323 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16325 values[f.name] = v;
16327 var record = new recordType(values, id);
16329 records[records.length] = record;
16333 totalRecords : records.length
16336 // used when loading children.. @see loadDataFromChildren
16337 toLoadData: function(rec)
16339 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16340 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16351 * @class Roo.bootstrap.ComboBox
16352 * @extends Roo.bootstrap.TriggerField
16353 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16354 * @cfg {Boolean} append (true|false) default false
16355 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16356 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16357 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16358 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16359 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16360 * @cfg {Boolean} animate default true
16361 * @cfg {Boolean} emptyResultText only for touch device
16362 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16363 * @cfg {String} emptyTitle default ''
16364 * @cfg {Number} width fixed with? experimental
16366 * Create a new ComboBox.
16367 * @param {Object} config Configuration options
16369 Roo.bootstrap.ComboBox = function(config){
16370 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16374 * Fires when the dropdown list is expanded
16375 * @param {Roo.bootstrap.ComboBox} combo This combo box
16380 * Fires when the dropdown list is collapsed
16381 * @param {Roo.bootstrap.ComboBox} combo This combo box
16385 * @event beforeselect
16386 * Fires before a list item is selected. Return false to cancel the selection.
16387 * @param {Roo.bootstrap.ComboBox} combo This combo box
16388 * @param {Roo.data.Record} record The data record returned from the underlying store
16389 * @param {Number} index The index of the selected item in the dropdown list
16391 'beforeselect' : true,
16394 * Fires when a list item is selected
16395 * @param {Roo.bootstrap.ComboBox} combo This combo box
16396 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16397 * @param {Number} index The index of the selected item in the dropdown list
16401 * @event beforequery
16402 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16403 * The event object passed has these properties:
16404 * @param {Roo.bootstrap.ComboBox} combo This combo box
16405 * @param {String} query The query
16406 * @param {Boolean} forceAll true to force "all" query
16407 * @param {Boolean} cancel true to cancel the query
16408 * @param {Object} e The query event object
16410 'beforequery': true,
16413 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16414 * @param {Roo.bootstrap.ComboBox} combo This combo box
16419 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16420 * @param {Roo.bootstrap.ComboBox} combo This combo box
16421 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16426 * Fires when the remove value from the combobox array
16427 * @param {Roo.bootstrap.ComboBox} combo This combo box
16431 * @event afterremove
16432 * Fires when the remove value from the combobox array
16433 * @param {Roo.bootstrap.ComboBox} combo This combo box
16435 'afterremove' : true,
16437 * @event specialfilter
16438 * Fires when specialfilter
16439 * @param {Roo.bootstrap.ComboBox} combo This combo box
16441 'specialfilter' : true,
16444 * Fires when tick the element
16445 * @param {Roo.bootstrap.ComboBox} combo This combo box
16449 * @event touchviewdisplay
16450 * Fires when touch view require special display (default is using displayField)
16451 * @param {Roo.bootstrap.ComboBox} combo This combo box
16452 * @param {Object} cfg set html .
16454 'touchviewdisplay' : true
16459 this.tickItems = [];
16461 this.selectedIndex = -1;
16462 if(this.mode == 'local'){
16463 if(config.queryDelay === undefined){
16464 this.queryDelay = 10;
16466 if(config.minChars === undefined){
16472 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16475 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16476 * rendering into an Roo.Editor, defaults to false)
16479 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16480 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16483 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16486 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16487 * the dropdown list (defaults to undefined, with no header element)
16491 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16495 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16497 listWidth: undefined,
16499 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16500 * mode = 'remote' or 'text' if mode = 'local')
16502 displayField: undefined,
16505 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16506 * mode = 'remote' or 'value' if mode = 'local').
16507 * Note: use of a valueField requires the user make a selection
16508 * in order for a value to be mapped.
16510 valueField: undefined,
16512 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16517 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16518 * field's data value (defaults to the underlying DOM element's name)
16520 hiddenName: undefined,
16522 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16526 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16528 selectedClass: 'active',
16531 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16535 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16536 * anchor positions (defaults to 'tl-bl')
16538 listAlign: 'tl-bl?',
16540 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16544 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16545 * query specified by the allQuery config option (defaults to 'query')
16547 triggerAction: 'query',
16549 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16550 * (defaults to 4, does not apply if editable = false)
16554 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16555 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16559 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16560 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16564 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16565 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16569 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16570 * when editable = true (defaults to false)
16572 selectOnFocus:false,
16574 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16576 queryParam: 'query',
16578 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16579 * when mode = 'remote' (defaults to 'Loading...')
16581 loadingText: 'Loading...',
16583 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16587 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16591 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16592 * traditional select (defaults to true)
16596 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16600 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16604 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16605 * listWidth has a higher value)
16609 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16610 * allow the user to set arbitrary text into the field (defaults to false)
16612 forceSelection:false,
16614 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16615 * if typeAhead = true (defaults to 250)
16617 typeAheadDelay : 250,
16619 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16620 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16622 valueNotFoundText : undefined,
16624 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16626 blockFocus : false,
16629 * @cfg {Boolean} disableClear Disable showing of clear button.
16631 disableClear : false,
16633 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16635 alwaysQuery : false,
16638 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16643 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16645 invalidClass : "has-warning",
16648 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16650 validClass : "has-success",
16653 * @cfg {Boolean} specialFilter (true|false) special filter default false
16655 specialFilter : false,
16658 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16660 mobileTouchView : true,
16663 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16665 useNativeIOS : false,
16668 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16670 mobile_restrict_height : false,
16672 ios_options : false,
16684 btnPosition : 'right',
16685 triggerList : true,
16686 showToggleBtn : true,
16688 emptyResultText: 'Empty',
16689 triggerText : 'Select',
16693 // element that contains real text value.. (when hidden is used..)
16695 getAutoCreate : function()
16700 * Render classic select for iso
16703 if(Roo.isIOS && this.useNativeIOS){
16704 cfg = this.getAutoCreateNativeIOS();
16712 if(Roo.isTouch && this.mobileTouchView){
16713 cfg = this.getAutoCreateTouchView();
16720 if(!this.tickable){
16721 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16726 * ComboBox with tickable selections
16729 var align = this.labelAlign || this.parentLabelAlign();
16732 cls : 'form-group roo-combobox-tickable' //input-group
16735 var btn_text_select = '';
16736 var btn_text_done = '';
16737 var btn_text_cancel = '';
16739 if (this.btn_text_show) {
16740 btn_text_select = 'Select';
16741 btn_text_done = 'Done';
16742 btn_text_cancel = 'Cancel';
16747 cls : 'tickable-buttons',
16752 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16753 //html : this.triggerText
16754 html: btn_text_select
16760 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16762 html: btn_text_done
16768 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16770 html: btn_text_cancel
16776 buttons.cn.unshift({
16778 cls: 'roo-select2-search-field-input'
16784 Roo.each(buttons.cn, function(c){
16786 c.cls += ' btn-' + _this.size;
16789 if (_this.disabled) {
16796 style : 'display: contents',
16801 cls: 'form-hidden-field'
16805 cls: 'roo-select2-choices',
16809 cls: 'roo-select2-search-field',
16820 cls: 'roo-select2-container input-group roo-select2-container-multi',
16826 // cls: 'typeahead typeahead-long dropdown-menu',
16827 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16832 if(this.hasFeedback && !this.allowBlank){
16836 cls: 'glyphicon form-control-feedback'
16839 combobox.cn.push(feedback);
16846 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16847 tooltip : 'This field is required'
16849 if (Roo.bootstrap.version == 4) {
16852 style : 'display:none'
16855 if (align ==='left' && this.fieldLabel.length) {
16857 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16864 cls : 'control-label col-form-label',
16865 html : this.fieldLabel
16877 var labelCfg = cfg.cn[1];
16878 var contentCfg = cfg.cn[2];
16881 if(this.indicatorpos == 'right'){
16887 cls : 'control-label col-form-label',
16891 html : this.fieldLabel
16907 labelCfg = cfg.cn[0];
16908 contentCfg = cfg.cn[1];
16912 if(this.labelWidth > 12){
16913 labelCfg.style = "width: " + this.labelWidth + 'px';
16915 if(this.width * 1 > 0){
16916 contentCfg.style = "width: " + this.width + 'px';
16918 if(this.labelWidth < 13 && this.labelmd == 0){
16919 this.labelmd = this.labelWidth;
16922 if(this.labellg > 0){
16923 labelCfg.cls += ' col-lg-' + this.labellg;
16924 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16927 if(this.labelmd > 0){
16928 labelCfg.cls += ' col-md-' + this.labelmd;
16929 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16932 if(this.labelsm > 0){
16933 labelCfg.cls += ' col-sm-' + this.labelsm;
16934 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16937 if(this.labelxs > 0){
16938 labelCfg.cls += ' col-xs-' + this.labelxs;
16939 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16943 } else if ( this.fieldLabel.length) {
16944 // Roo.log(" label");
16949 //cls : 'input-group-addon',
16950 html : this.fieldLabel
16955 if(this.indicatorpos == 'right'){
16959 //cls : 'input-group-addon',
16960 html : this.fieldLabel
16970 // Roo.log(" no label && no align");
16977 ['xs','sm','md','lg'].map(function(size){
16978 if (settings[size]) {
16979 cfg.cls += ' col-' + size + '-' + settings[size];
16987 _initEventsCalled : false,
16990 initEvents: function()
16992 if (this._initEventsCalled) { // as we call render... prevent looping...
16995 this._initEventsCalled = true;
16998 throw "can not find store for combo";
17001 this.indicator = this.indicatorEl();
17003 this.store = Roo.factory(this.store, Roo.data);
17004 this.store.parent = this;
17006 // if we are building from html. then this element is so complex, that we can not really
17007 // use the rendered HTML.
17008 // so we have to trash and replace the previous code.
17009 if (Roo.XComponent.build_from_html) {
17010 // remove this element....
17011 var e = this.el.dom, k=0;
17012 while (e ) { e = e.previousSibling; ++k;}
17017 this.rendered = false;
17019 this.render(this.parent().getChildContainer(true), k);
17022 if(Roo.isIOS && this.useNativeIOS){
17023 this.initIOSView();
17031 if(Roo.isTouch && this.mobileTouchView){
17032 this.initTouchView();
17037 this.initTickableEvents();
17041 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17043 if(this.hiddenName){
17045 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17047 this.hiddenField.dom.value =
17048 this.hiddenValue !== undefined ? this.hiddenValue :
17049 this.value !== undefined ? this.value : '';
17051 // prevent input submission
17052 this.el.dom.removeAttribute('name');
17053 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17058 // this.el.dom.setAttribute('autocomplete', 'off');
17061 var cls = 'x-combo-list';
17063 //this.list = new Roo.Layer({
17064 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17070 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17071 _this.list.setWidth(lw);
17074 this.list.on('mouseover', this.onViewOver, this);
17075 this.list.on('mousemove', this.onViewMove, this);
17076 this.list.on('scroll', this.onViewScroll, this);
17079 this.list.swallowEvent('mousewheel');
17080 this.assetHeight = 0;
17083 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17084 this.assetHeight += this.header.getHeight();
17087 this.innerList = this.list.createChild({cls:cls+'-inner'});
17088 this.innerList.on('mouseover', this.onViewOver, this);
17089 this.innerList.on('mousemove', this.onViewMove, this);
17090 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17092 if(this.allowBlank && !this.pageSize && !this.disableClear){
17093 this.footer = this.list.createChild({cls:cls+'-ft'});
17094 this.pageTb = new Roo.Toolbar(this.footer);
17098 this.footer = this.list.createChild({cls:cls+'-ft'});
17099 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17100 {pageSize: this.pageSize});
17104 if (this.pageTb && this.allowBlank && !this.disableClear) {
17106 this.pageTb.add(new Roo.Toolbar.Fill(), {
17107 cls: 'x-btn-icon x-btn-clear',
17109 handler: function()
17112 _this.clearValue();
17113 _this.onSelect(false, -1);
17118 this.assetHeight += this.footer.getHeight();
17123 this.tpl = Roo.bootstrap.version == 4 ?
17124 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17125 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17128 this.view = new Roo.View(this.list, this.tpl, {
17129 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17131 //this.view.wrapEl.setDisplayed(false);
17132 this.view.on('click', this.onViewClick, this);
17135 this.store.on('beforeload', this.onBeforeLoad, this);
17136 this.store.on('load', this.onLoad, this);
17137 this.store.on('loadexception', this.onLoadException, this);
17139 if(this.resizable){
17140 this.resizer = new Roo.Resizable(this.list, {
17141 pinned:true, handles:'se'
17143 this.resizer.on('resize', function(r, w, h){
17144 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17145 this.listWidth = w;
17146 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17147 this.restrictHeight();
17149 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17152 if(!this.editable){
17153 this.editable = true;
17154 this.setEditable(false);
17159 if (typeof(this.events.add.listeners) != 'undefined') {
17161 this.addicon = this.wrap.createChild(
17162 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17164 this.addicon.on('click', function(e) {
17165 this.fireEvent('add', this);
17168 if (typeof(this.events.edit.listeners) != 'undefined') {
17170 this.editicon = this.wrap.createChild(
17171 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17172 if (this.addicon) {
17173 this.editicon.setStyle('margin-left', '40px');
17175 this.editicon.on('click', function(e) {
17177 // we fire even if inothing is selected..
17178 this.fireEvent('edit', this, this.lastData );
17184 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17185 "up" : function(e){
17186 this.inKeyMode = true;
17190 "down" : function(e){
17191 if(!this.isExpanded()){
17192 this.onTriggerClick();
17194 this.inKeyMode = true;
17199 "enter" : function(e){
17200 // this.onViewClick();
17204 if(this.fireEvent("specialkey", this, e)){
17205 this.onViewClick(false);
17211 "esc" : function(e){
17215 "tab" : function(e){
17218 if(this.fireEvent("specialkey", this, e)){
17219 this.onViewClick(false);
17227 doRelay : function(foo, bar, hname){
17228 if(hname == 'down' || this.scope.isExpanded()){
17229 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17238 this.queryDelay = Math.max(this.queryDelay || 10,
17239 this.mode == 'local' ? 10 : 250);
17242 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17244 if(this.typeAhead){
17245 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17247 if(this.editable !== false){
17248 this.inputEl().on("keyup", this.onKeyUp, this);
17250 if(this.forceSelection){
17251 this.inputEl().on('blur', this.doForce, this);
17255 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17256 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17260 initTickableEvents: function()
17264 if(this.hiddenName){
17266 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17268 this.hiddenField.dom.value =
17269 this.hiddenValue !== undefined ? this.hiddenValue :
17270 this.value !== undefined ? this.value : '';
17272 // prevent input submission
17273 this.el.dom.removeAttribute('name');
17274 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17279 // this.list = this.el.select('ul.dropdown-menu',true).first();
17281 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17282 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17283 if(this.triggerList){
17284 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17287 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17288 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17290 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17291 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17293 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17294 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17296 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17297 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17298 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17301 this.cancelBtn.hide();
17306 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17307 _this.list.setWidth(lw);
17310 this.list.on('mouseover', this.onViewOver, this);
17311 this.list.on('mousemove', this.onViewMove, this);
17313 this.list.on('scroll', this.onViewScroll, this);
17316 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17317 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17320 this.view = new Roo.View(this.list, this.tpl, {
17325 selectedClass: this.selectedClass
17328 //this.view.wrapEl.setDisplayed(false);
17329 this.view.on('click', this.onViewClick, this);
17333 this.store.on('beforeload', this.onBeforeLoad, this);
17334 this.store.on('load', this.onLoad, this);
17335 this.store.on('loadexception', this.onLoadException, this);
17338 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17339 "up" : function(e){
17340 this.inKeyMode = true;
17344 "down" : function(e){
17345 this.inKeyMode = true;
17349 "enter" : function(e){
17350 if(this.fireEvent("specialkey", this, e)){
17351 this.onViewClick(false);
17357 "esc" : function(e){
17358 this.onTickableFooterButtonClick(e, false, false);
17361 "tab" : function(e){
17362 this.fireEvent("specialkey", this, e);
17364 this.onTickableFooterButtonClick(e, false, false);
17371 doRelay : function(e, fn, key){
17372 if(this.scope.isExpanded()){
17373 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17382 this.queryDelay = Math.max(this.queryDelay || 10,
17383 this.mode == 'local' ? 10 : 250);
17386 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17388 if(this.typeAhead){
17389 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17392 if(this.editable !== false){
17393 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17396 this.indicator = this.indicatorEl();
17398 if(this.indicator){
17399 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17400 this.indicator.hide();
17405 onDestroy : function(){
17407 this.view.setStore(null);
17408 this.view.el.removeAllListeners();
17409 this.view.el.remove();
17410 this.view.purgeListeners();
17413 this.list.dom.innerHTML = '';
17417 this.store.un('beforeload', this.onBeforeLoad, this);
17418 this.store.un('load', this.onLoad, this);
17419 this.store.un('loadexception', this.onLoadException, this);
17421 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17425 fireKey : function(e){
17426 if(e.isNavKeyPress() && !this.list.isVisible()){
17427 this.fireEvent("specialkey", this, e);
17432 onResize: function(w, h)
17436 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17438 // if(typeof w != 'number'){
17439 // // we do not handle it!?!?
17442 // var tw = this.trigger.getWidth();
17443 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17444 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17446 // this.inputEl().setWidth( this.adjustWidth('input', x));
17448 // //this.trigger.setStyle('left', x+'px');
17450 // if(this.list && this.listWidth === undefined){
17451 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17452 // this.list.setWidth(lw);
17453 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17461 * Allow or prevent the user from directly editing the field text. If false is passed,
17462 * the user will only be able to select from the items defined in the dropdown list. This method
17463 * is the runtime equivalent of setting the 'editable' config option at config time.
17464 * @param {Boolean} value True to allow the user to directly edit the field text
17466 setEditable : function(value){
17467 if(value == this.editable){
17470 this.editable = value;
17472 this.inputEl().dom.setAttribute('readOnly', true);
17473 this.inputEl().on('mousedown', this.onTriggerClick, this);
17474 this.inputEl().addClass('x-combo-noedit');
17476 this.inputEl().dom.removeAttribute('readOnly');
17477 this.inputEl().un('mousedown', this.onTriggerClick, this);
17478 this.inputEl().removeClass('x-combo-noedit');
17484 onBeforeLoad : function(combo,opts){
17485 if(!this.hasFocus){
17489 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17491 this.restrictHeight();
17492 this.selectedIndex = -1;
17496 onLoad : function(){
17498 this.hasQuery = false;
17500 if(!this.hasFocus){
17504 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17505 this.loading.hide();
17508 if(this.store.getCount() > 0){
17511 this.restrictHeight();
17512 if(this.lastQuery == this.allQuery){
17513 if(this.editable && !this.tickable){
17514 this.inputEl().dom.select();
17518 !this.selectByValue(this.value, true) &&
17521 !this.store.lastOptions ||
17522 typeof(this.store.lastOptions.add) == 'undefined' ||
17523 this.store.lastOptions.add != true
17526 this.select(0, true);
17529 if(this.autoFocus){
17532 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17533 this.taTask.delay(this.typeAheadDelay);
17537 this.onEmptyResults();
17543 onLoadException : function()
17545 this.hasQuery = false;
17547 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17548 this.loading.hide();
17551 if(this.tickable && this.editable){
17556 // only causes errors at present
17557 //Roo.log(this.store.reader.jsonData);
17558 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17560 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17566 onTypeAhead : function(){
17567 if(this.store.getCount() > 0){
17568 var r = this.store.getAt(0);
17569 var newValue = r.data[this.displayField];
17570 var len = newValue.length;
17571 var selStart = this.getRawValue().length;
17573 if(selStart != len){
17574 this.setRawValue(newValue);
17575 this.selectText(selStart, newValue.length);
17581 onSelect : function(record, index){
17583 if(this.fireEvent('beforeselect', this, record, index) !== false){
17585 this.setFromData(index > -1 ? record.data : false);
17588 this.fireEvent('select', this, record, index);
17593 * Returns the currently selected field value or empty string if no value is set.
17594 * @return {String} value The selected value
17596 getValue : function()
17598 if(Roo.isIOS && this.useNativeIOS){
17599 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17603 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17606 if(this.valueField){
17607 return typeof this.value != 'undefined' ? this.value : '';
17609 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17613 getRawValue : function()
17615 if(Roo.isIOS && this.useNativeIOS){
17616 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17619 var v = this.inputEl().getValue();
17625 * Clears any text/value currently set in the field
17627 clearValue : function(){
17629 if(this.hiddenField){
17630 this.hiddenField.dom.value = '';
17633 this.setRawValue('');
17634 this.lastSelectionText = '';
17635 this.lastData = false;
17637 var close = this.closeTriggerEl();
17648 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17649 * will be displayed in the field. If the value does not match the data value of an existing item,
17650 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17651 * Otherwise the field will be blank (although the value will still be set).
17652 * @param {String} value The value to match
17654 setValue : function(v)
17656 if(Roo.isIOS && this.useNativeIOS){
17657 this.setIOSValue(v);
17667 if(this.valueField){
17668 var r = this.findRecord(this.valueField, v);
17670 text = r.data[this.displayField];
17671 }else if(this.valueNotFoundText !== undefined){
17672 text = this.valueNotFoundText;
17675 this.lastSelectionText = text;
17676 if(this.hiddenField){
17677 this.hiddenField.dom.value = v;
17679 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17682 var close = this.closeTriggerEl();
17685 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17691 * @property {Object} the last set data for the element
17696 * Sets the value of the field based on a object which is related to the record format for the store.
17697 * @param {Object} value the value to set as. or false on reset?
17699 setFromData : function(o){
17706 var dv = ''; // display value
17707 var vv = ''; // value value..
17709 if (this.displayField) {
17710 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17712 // this is an error condition!!!
17713 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17716 if(this.valueField){
17717 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17720 var close = this.closeTriggerEl();
17723 if(dv.length || vv * 1 > 0){
17725 this.blockFocus=true;
17731 if(this.hiddenField){
17732 this.hiddenField.dom.value = vv;
17734 this.lastSelectionText = dv;
17735 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17739 // no hidden field.. - we store the value in 'value', but still display
17740 // display field!!!!
17741 this.lastSelectionText = dv;
17742 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17749 reset : function(){
17750 // overridden so that last data is reset..
17757 this.setValue(this.originalValue);
17758 //this.clearInvalid();
17759 this.lastData = false;
17761 this.view.clearSelections();
17767 findRecord : function(prop, value){
17769 if(this.store.getCount() > 0){
17770 this.store.each(function(r){
17771 if(r.data[prop] == value){
17781 getName: function()
17783 // returns hidden if it's set..
17784 if (!this.rendered) {return ''};
17785 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17789 onViewMove : function(e, t){
17790 this.inKeyMode = false;
17794 onViewOver : function(e, t){
17795 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17798 var item = this.view.findItemFromChild(t);
17801 var index = this.view.indexOf(item);
17802 this.select(index, false);
17807 onViewClick : function(view, doFocus, el, e)
17809 var index = this.view.getSelectedIndexes()[0];
17811 var r = this.store.getAt(index);
17815 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17822 Roo.each(this.tickItems, function(v,k){
17824 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17826 _this.tickItems.splice(k, 1);
17828 if(typeof(e) == 'undefined' && view == false){
17829 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17841 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17842 this.tickItems.push(r.data);
17845 if(typeof(e) == 'undefined' && view == false){
17846 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17853 this.onSelect(r, index);
17855 if(doFocus !== false && !this.blockFocus){
17856 this.inputEl().focus();
17861 restrictHeight : function(){
17862 //this.innerList.dom.style.height = '';
17863 //var inner = this.innerList.dom;
17864 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17865 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17866 //this.list.beginUpdate();
17867 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17868 this.list.alignTo(this.inputEl(), this.listAlign);
17869 this.list.alignTo(this.inputEl(), this.listAlign);
17870 //this.list.endUpdate();
17874 onEmptyResults : function(){
17876 if(this.tickable && this.editable){
17877 this.hasFocus = false;
17878 this.restrictHeight();
17886 * Returns true if the dropdown list is expanded, else false.
17888 isExpanded : function(){
17889 return this.list.isVisible();
17893 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17894 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17895 * @param {String} value The data value of the item to select
17896 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17897 * selected item if it is not currently in view (defaults to true)
17898 * @return {Boolean} True if the value matched an item in the list, else false
17900 selectByValue : function(v, scrollIntoView){
17901 if(v !== undefined && v !== null){
17902 var r = this.findRecord(this.valueField || this.displayField, v);
17904 this.select(this.store.indexOf(r), scrollIntoView);
17912 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17913 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17914 * @param {Number} index The zero-based index of the list item to select
17915 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17916 * selected item if it is not currently in view (defaults to true)
17918 select : function(index, scrollIntoView){
17919 this.selectedIndex = index;
17920 this.view.select(index);
17921 if(scrollIntoView !== false){
17922 var el = this.view.getNode(index);
17924 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17927 this.list.scrollChildIntoView(el, false);
17933 selectNext : function(){
17934 var ct = this.store.getCount();
17936 if(this.selectedIndex == -1){
17938 }else if(this.selectedIndex < ct-1){
17939 this.select(this.selectedIndex+1);
17945 selectPrev : function(){
17946 var ct = this.store.getCount();
17948 if(this.selectedIndex == -1){
17950 }else if(this.selectedIndex != 0){
17951 this.select(this.selectedIndex-1);
17957 onKeyUp : function(e){
17958 if(this.editable !== false && !e.isSpecialKey()){
17959 this.lastKey = e.getKey();
17960 this.dqTask.delay(this.queryDelay);
17965 validateBlur : function(){
17966 return !this.list || !this.list.isVisible();
17970 initQuery : function(){
17972 var v = this.getRawValue();
17974 if(this.tickable && this.editable){
17975 v = this.tickableInputEl().getValue();
17982 doForce : function(){
17983 if(this.inputEl().dom.value.length > 0){
17984 this.inputEl().dom.value =
17985 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17991 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17992 * query allowing the query action to be canceled if needed.
17993 * @param {String} query The SQL query to execute
17994 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17995 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17996 * saved in the current store (defaults to false)
17998 doQuery : function(q, forceAll){
18000 if(q === undefined || q === null){
18005 forceAll: forceAll,
18009 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18014 forceAll = qe.forceAll;
18015 if(forceAll === true || (q.length >= this.minChars)){
18017 this.hasQuery = true;
18019 if(this.lastQuery != q || this.alwaysQuery){
18020 this.lastQuery = q;
18021 if(this.mode == 'local'){
18022 this.selectedIndex = -1;
18024 this.store.clearFilter();
18027 if(this.specialFilter){
18028 this.fireEvent('specialfilter', this);
18033 this.store.filter(this.displayField, q);
18036 this.store.fireEvent("datachanged", this.store);
18043 this.store.baseParams[this.queryParam] = q;
18045 var options = {params : this.getParams(q)};
18048 options.add = true;
18049 options.params.start = this.page * this.pageSize;
18052 this.store.load(options);
18055 * this code will make the page width larger, at the beginning, the list not align correctly,
18056 * we should expand the list on onLoad
18057 * so command out it
18062 this.selectedIndex = -1;
18067 this.loadNext = false;
18071 getParams : function(q){
18073 //p[this.queryParam] = q;
18077 p.limit = this.pageSize;
18083 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18085 collapse : function(){
18086 if(!this.isExpanded()){
18092 this.hasFocus = false;
18096 this.cancelBtn.hide();
18097 this.trigger.show();
18100 this.tickableInputEl().dom.value = '';
18101 this.tickableInputEl().blur();
18106 Roo.get(document).un('mousedown', this.collapseIf, this);
18107 Roo.get(document).un('mousewheel', this.collapseIf, this);
18108 if (!this.editable) {
18109 Roo.get(document).un('keydown', this.listKeyPress, this);
18111 this.fireEvent('collapse', this);
18117 collapseIf : function(e){
18118 var in_combo = e.within(this.el);
18119 var in_list = e.within(this.list);
18120 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18122 if (in_combo || in_list || is_list) {
18123 //e.stopPropagation();
18128 this.onTickableFooterButtonClick(e, false, false);
18136 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18138 expand : function(){
18140 if(this.isExpanded() || !this.hasFocus){
18144 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18145 this.list.setWidth(lw);
18151 this.restrictHeight();
18155 this.tickItems = Roo.apply([], this.item);
18158 this.cancelBtn.show();
18159 this.trigger.hide();
18162 this.tickableInputEl().focus();
18167 Roo.get(document).on('mousedown', this.collapseIf, this);
18168 Roo.get(document).on('mousewheel', this.collapseIf, this);
18169 if (!this.editable) {
18170 Roo.get(document).on('keydown', this.listKeyPress, this);
18173 this.fireEvent('expand', this);
18177 // Implements the default empty TriggerField.onTriggerClick function
18178 onTriggerClick : function(e)
18180 Roo.log('trigger click');
18182 if(this.disabled || !this.triggerList){
18187 this.loadNext = false;
18189 if(this.isExpanded()){
18191 if (!this.blockFocus) {
18192 this.inputEl().focus();
18196 this.hasFocus = true;
18197 if(this.triggerAction == 'all') {
18198 this.doQuery(this.allQuery, true);
18200 this.doQuery(this.getRawValue());
18202 if (!this.blockFocus) {
18203 this.inputEl().focus();
18208 onTickableTriggerClick : function(e)
18215 this.loadNext = false;
18216 this.hasFocus = true;
18218 if(this.triggerAction == 'all') {
18219 this.doQuery(this.allQuery, true);
18221 this.doQuery(this.getRawValue());
18225 onSearchFieldClick : function(e)
18227 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18228 this.onTickableFooterButtonClick(e, false, false);
18232 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18237 this.loadNext = false;
18238 this.hasFocus = true;
18240 if(this.triggerAction == 'all') {
18241 this.doQuery(this.allQuery, true);
18243 this.doQuery(this.getRawValue());
18247 listKeyPress : function(e)
18249 //Roo.log('listkeypress');
18250 // scroll to first matching element based on key pres..
18251 if (e.isSpecialKey()) {
18254 var k = String.fromCharCode(e.getKey()).toUpperCase();
18257 var csel = this.view.getSelectedNodes();
18258 var cselitem = false;
18260 var ix = this.view.indexOf(csel[0]);
18261 cselitem = this.store.getAt(ix);
18262 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18268 this.store.each(function(v) {
18270 // start at existing selection.
18271 if (cselitem.id == v.id) {
18277 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18278 match = this.store.indexOf(v);
18284 if (match === false) {
18285 return true; // no more action?
18288 this.view.select(match);
18289 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18290 sn.scrollIntoView(sn.dom.parentNode, false);
18293 onViewScroll : function(e, t){
18295 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){
18299 this.hasQuery = true;
18301 this.loading = this.list.select('.loading', true).first();
18303 if(this.loading === null){
18304 this.list.createChild({
18306 cls: 'loading roo-select2-more-results roo-select2-active',
18307 html: 'Loading more results...'
18310 this.loading = this.list.select('.loading', true).first();
18312 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18314 this.loading.hide();
18317 this.loading.show();
18322 this.loadNext = true;
18324 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18329 addItem : function(o)
18331 var dv = ''; // display value
18333 if (this.displayField) {
18334 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18336 // this is an error condition!!!
18337 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18344 var choice = this.choices.createChild({
18346 cls: 'roo-select2-search-choice',
18355 cls: 'roo-select2-search-choice-close fa fa-times',
18360 }, this.searchField);
18362 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18364 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18372 this.inputEl().dom.value = '';
18377 onRemoveItem : function(e, _self, o)
18379 e.preventDefault();
18381 this.lastItem = Roo.apply([], this.item);
18383 var index = this.item.indexOf(o.data) * 1;
18386 Roo.log('not this item?!');
18390 this.item.splice(index, 1);
18395 this.fireEvent('remove', this, e);
18401 syncValue : function()
18403 if(!this.item.length){
18410 Roo.each(this.item, function(i){
18411 if(_this.valueField){
18412 value.push(i[_this.valueField]);
18419 this.value = value.join(',');
18421 if(this.hiddenField){
18422 this.hiddenField.dom.value = this.value;
18425 this.store.fireEvent("datachanged", this.store);
18430 clearItem : function()
18432 if(!this.multiple){
18438 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18446 if(this.tickable && !Roo.isTouch){
18447 this.view.refresh();
18451 inputEl: function ()
18453 if(Roo.isIOS && this.useNativeIOS){
18454 return this.el.select('select.roo-ios-select', true).first();
18457 if(Roo.isTouch && this.mobileTouchView){
18458 return this.el.select('input.form-control',true).first();
18462 return this.searchField;
18465 return this.el.select('input.form-control',true).first();
18468 onTickableFooterButtonClick : function(e, btn, el)
18470 e.preventDefault();
18472 this.lastItem = Roo.apply([], this.item);
18474 if(btn && btn.name == 'cancel'){
18475 this.tickItems = Roo.apply([], this.item);
18484 Roo.each(this.tickItems, function(o){
18492 validate : function()
18494 if(this.getVisibilityEl().hasClass('hidden')){
18498 var v = this.getRawValue();
18501 v = this.getValue();
18504 if(this.disabled || this.allowBlank || v.length){
18509 this.markInvalid();
18513 tickableInputEl : function()
18515 if(!this.tickable || !this.editable){
18516 return this.inputEl();
18519 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18523 getAutoCreateTouchView : function()
18528 cls: 'form-group' //input-group
18534 type : this.inputType,
18535 cls : 'form-control x-combo-noedit',
18536 autocomplete: 'new-password',
18537 placeholder : this.placeholder || '',
18542 input.name = this.name;
18546 input.cls += ' input-' + this.size;
18549 if (this.disabled) {
18550 input.disabled = true;
18554 cls : 'roo-combobox-wrap',
18561 inputblock.cls += ' input-group';
18563 inputblock.cn.unshift({
18565 cls : 'input-group-addon input-group-prepend input-group-text',
18570 if(this.removable && !this.multiple){
18571 inputblock.cls += ' roo-removable';
18573 inputblock.cn.push({
18576 cls : 'roo-combo-removable-btn close'
18580 if(this.hasFeedback && !this.allowBlank){
18582 inputblock.cls += ' has-feedback';
18584 inputblock.cn.push({
18586 cls: 'glyphicon form-control-feedback'
18593 inputblock.cls += (this.before) ? '' : ' input-group';
18595 inputblock.cn.push({
18597 cls : 'input-group-addon input-group-append input-group-text',
18603 var ibwrap = inputblock;
18608 cls: 'roo-select2-choices',
18612 cls: 'roo-select2-search-field',
18625 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18630 cls: 'form-hidden-field'
18636 if(!this.multiple && this.showToggleBtn){
18642 if (this.caret != false) {
18645 cls: 'fa fa-' + this.caret
18652 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18654 Roo.bootstrap.version == 3 ? caret : '',
18657 cls: 'combobox-clear',
18671 combobox.cls += ' roo-select2-container-multi';
18674 var required = this.allowBlank ? {
18676 style: 'display: none'
18679 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18680 tooltip : 'This field is required'
18683 var align = this.labelAlign || this.parentLabelAlign();
18685 if (align ==='left' && this.fieldLabel.length) {
18691 cls : 'control-label col-form-label',
18692 html : this.fieldLabel
18696 cls : 'roo-combobox-wrap ',
18703 var labelCfg = cfg.cn[1];
18704 var contentCfg = cfg.cn[2];
18707 if(this.indicatorpos == 'right'){
18712 cls : 'control-label col-form-label',
18716 html : this.fieldLabel
18722 cls : "roo-combobox-wrap ",
18730 labelCfg = cfg.cn[0];
18731 contentCfg = cfg.cn[1];
18736 if(this.labelWidth > 12){
18737 labelCfg.style = "width: " + this.labelWidth + 'px';
18740 if(this.labelWidth < 13 && this.labelmd == 0){
18741 this.labelmd = this.labelWidth;
18744 if(this.labellg > 0){
18745 labelCfg.cls += ' col-lg-' + this.labellg;
18746 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18749 if(this.labelmd > 0){
18750 labelCfg.cls += ' col-md-' + this.labelmd;
18751 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18754 if(this.labelsm > 0){
18755 labelCfg.cls += ' col-sm-' + this.labelsm;
18756 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18759 if(this.labelxs > 0){
18760 labelCfg.cls += ' col-xs-' + this.labelxs;
18761 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18765 } else if ( this.fieldLabel.length) {
18770 cls : 'control-label',
18771 html : this.fieldLabel
18782 if(this.indicatorpos == 'right'){
18786 cls : 'control-label',
18787 html : this.fieldLabel,
18805 var settings = this;
18807 ['xs','sm','md','lg'].map(function(size){
18808 if (settings[size]) {
18809 cfg.cls += ' col-' + size + '-' + settings[size];
18816 initTouchView : function()
18818 this.renderTouchView();
18820 this.touchViewEl.on('scroll', function(){
18821 this.el.dom.scrollTop = 0;
18824 this.originalValue = this.getValue();
18826 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18828 this.inputEl().on("click", this.showTouchView, this);
18829 if (this.triggerEl) {
18830 this.triggerEl.on("click", this.showTouchView, this);
18834 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18835 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18837 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18839 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18840 this.store.on('load', this.onTouchViewLoad, this);
18841 this.store.on('loadexception', this.onTouchViewLoadException, this);
18843 if(this.hiddenName){
18845 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18847 this.hiddenField.dom.value =
18848 this.hiddenValue !== undefined ? this.hiddenValue :
18849 this.value !== undefined ? this.value : '';
18851 this.el.dom.removeAttribute('name');
18852 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18856 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18857 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18860 if(this.removable && !this.multiple){
18861 var close = this.closeTriggerEl();
18863 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18864 close.on('click', this.removeBtnClick, this, close);
18868 * fix the bug in Safari iOS8
18870 this.inputEl().on("focus", function(e){
18871 document.activeElement.blur();
18874 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18881 renderTouchView : function()
18883 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18884 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18886 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18887 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18889 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18890 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18891 this.touchViewBodyEl.setStyle('overflow', 'auto');
18893 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18894 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18896 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18897 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18901 showTouchView : function()
18907 this.touchViewHeaderEl.hide();
18909 if(this.modalTitle.length){
18910 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18911 this.touchViewHeaderEl.show();
18914 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18915 this.touchViewEl.show();
18917 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18919 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18920 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18922 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18924 if(this.modalTitle.length){
18925 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18928 this.touchViewBodyEl.setHeight(bodyHeight);
18932 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18934 this.touchViewEl.addClass(['in','show']);
18937 if(this._touchViewMask){
18938 Roo.get(document.body).addClass("x-body-masked");
18939 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18940 this._touchViewMask.setStyle('z-index', 10000);
18941 this._touchViewMask.addClass('show');
18944 this.doTouchViewQuery();
18948 hideTouchView : function()
18950 this.touchViewEl.removeClass(['in','show']);
18954 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18956 this.touchViewEl.setStyle('display', 'none');
18959 if(this._touchViewMask){
18960 this._touchViewMask.removeClass('show');
18961 Roo.get(document.body).removeClass("x-body-masked");
18965 setTouchViewValue : function()
18972 Roo.each(this.tickItems, function(o){
18977 this.hideTouchView();
18980 doTouchViewQuery : function()
18989 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18993 if(!this.alwaysQuery || this.mode == 'local'){
18994 this.onTouchViewLoad();
19001 onTouchViewBeforeLoad : function(combo,opts)
19007 onTouchViewLoad : function()
19009 if(this.store.getCount() < 1){
19010 this.onTouchViewEmptyResults();
19014 this.clearTouchView();
19016 var rawValue = this.getRawValue();
19018 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19020 this.tickItems = [];
19022 this.store.data.each(function(d, rowIndex){
19023 var row = this.touchViewListGroup.createChild(template);
19025 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19026 row.addClass(d.data.cls);
19029 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19032 html : d.data[this.displayField]
19035 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19036 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19039 row.removeClass('selected');
19040 if(!this.multiple && this.valueField &&
19041 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19044 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19045 row.addClass('selected');
19048 if(this.multiple && this.valueField &&
19049 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19053 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19054 this.tickItems.push(d.data);
19057 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19061 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19063 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19065 if(this.modalTitle.length){
19066 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19069 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19071 if(this.mobile_restrict_height && listHeight < bodyHeight){
19072 this.touchViewBodyEl.setHeight(listHeight);
19077 if(firstChecked && listHeight > bodyHeight){
19078 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19083 onTouchViewLoadException : function()
19085 this.hideTouchView();
19088 onTouchViewEmptyResults : function()
19090 this.clearTouchView();
19092 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19094 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19098 clearTouchView : function()
19100 this.touchViewListGroup.dom.innerHTML = '';
19103 onTouchViewClick : function(e, el, o)
19105 e.preventDefault();
19108 var rowIndex = o.rowIndex;
19110 var r = this.store.getAt(rowIndex);
19112 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19114 if(!this.multiple){
19115 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19116 c.dom.removeAttribute('checked');
19119 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19121 this.setFromData(r.data);
19123 var close = this.closeTriggerEl();
19129 this.hideTouchView();
19131 this.fireEvent('select', this, r, rowIndex);
19136 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19137 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19138 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19142 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19143 this.addItem(r.data);
19144 this.tickItems.push(r.data);
19148 getAutoCreateNativeIOS : function()
19151 cls: 'form-group' //input-group,
19156 cls : 'roo-ios-select'
19160 combobox.name = this.name;
19163 if (this.disabled) {
19164 combobox.disabled = true;
19167 var settings = this;
19169 ['xs','sm','md','lg'].map(function(size){
19170 if (settings[size]) {
19171 cfg.cls += ' col-' + size + '-' + settings[size];
19181 initIOSView : function()
19183 this.store.on('load', this.onIOSViewLoad, this);
19188 onIOSViewLoad : function()
19190 if(this.store.getCount() < 1){
19194 this.clearIOSView();
19196 if(this.allowBlank) {
19198 var default_text = '-- SELECT --';
19200 if(this.placeholder.length){
19201 default_text = this.placeholder;
19204 if(this.emptyTitle.length){
19205 default_text += ' - ' + this.emptyTitle + ' -';
19208 var opt = this.inputEl().createChild({
19211 html : default_text
19215 o[this.valueField] = 0;
19216 o[this.displayField] = default_text;
19218 this.ios_options.push({
19225 this.store.data.each(function(d, rowIndex){
19229 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19230 html = d.data[this.displayField];
19235 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19236 value = d.data[this.valueField];
19245 if(this.value == d.data[this.valueField]){
19246 option['selected'] = true;
19249 var opt = this.inputEl().createChild(option);
19251 this.ios_options.push({
19258 this.inputEl().on('change', function(){
19259 this.fireEvent('select', this);
19264 clearIOSView: function()
19266 this.inputEl().dom.innerHTML = '';
19268 this.ios_options = [];
19271 setIOSValue: function(v)
19275 if(!this.ios_options){
19279 Roo.each(this.ios_options, function(opts){
19281 opts.el.dom.removeAttribute('selected');
19283 if(opts.data[this.valueField] != v){
19287 opts.el.dom.setAttribute('selected', true);
19293 * @cfg {Boolean} grow
19297 * @cfg {Number} growMin
19301 * @cfg {Number} growMax
19310 Roo.apply(Roo.bootstrap.ComboBox, {
19314 cls: 'modal-header',
19336 cls: 'list-group-item',
19340 cls: 'roo-combobox-list-group-item-value'
19344 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19358 listItemCheckbox : {
19360 cls: 'list-group-item',
19364 cls: 'roo-combobox-list-group-item-value'
19368 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19384 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19389 cls: 'modal-footer',
19397 cls: 'col-xs-6 text-left',
19400 cls: 'btn btn-danger roo-touch-view-cancel',
19406 cls: 'col-xs-6 text-right',
19409 cls: 'btn btn-success roo-touch-view-ok',
19420 Roo.apply(Roo.bootstrap.ComboBox, {
19422 touchViewTemplate : {
19424 cls: 'modal fade roo-combobox-touch-view',
19428 cls: 'modal-dialog',
19429 style : 'position:fixed', // we have to fix position....
19433 cls: 'modal-content',
19435 Roo.bootstrap.ComboBox.header,
19436 Roo.bootstrap.ComboBox.body,
19437 Roo.bootstrap.ComboBox.footer
19446 * Ext JS Library 1.1.1
19447 * Copyright(c) 2006-2007, Ext JS, LLC.
19449 * Originally Released Under LGPL - original licence link has changed is not relivant.
19452 * <script type="text/javascript">
19457 * @extends Roo.util.Observable
19458 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19459 * This class also supports single and multi selection modes. <br>
19460 * Create a data model bound view:
19462 var store = new Roo.data.Store(...);
19464 var view = new Roo.View({
19466 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19468 singleSelect: true,
19469 selectedClass: "ydataview-selected",
19473 // listen for node click?
19474 view.on("click", function(vw, index, node, e){
19475 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19479 dataModel.load("foobar.xml");
19481 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19483 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19484 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19486 * Note: old style constructor is still suported (container, template, config)
19489 * Create a new View
19490 * @param {Object} config The config object
19493 Roo.View = function(config, depreciated_tpl, depreciated_config){
19495 this.parent = false;
19497 if (typeof(depreciated_tpl) == 'undefined') {
19498 // new way.. - universal constructor.
19499 Roo.apply(this, config);
19500 this.el = Roo.get(this.el);
19503 this.el = Roo.get(config);
19504 this.tpl = depreciated_tpl;
19505 Roo.apply(this, depreciated_config);
19507 this.wrapEl = this.el.wrap().wrap();
19508 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19511 if(typeof(this.tpl) == "string"){
19512 this.tpl = new Roo.Template(this.tpl);
19514 // support xtype ctors..
19515 this.tpl = new Roo.factory(this.tpl, Roo);
19519 this.tpl.compile();
19524 * @event beforeclick
19525 * Fires before a click is processed. Returns false to cancel the default action.
19526 * @param {Roo.View} this
19527 * @param {Number} index The index of the target node
19528 * @param {HTMLElement} node The target node
19529 * @param {Roo.EventObject} e The raw event object
19531 "beforeclick" : true,
19534 * Fires when a template node is clicked.
19535 * @param {Roo.View} this
19536 * @param {Number} index The index of the target node
19537 * @param {HTMLElement} node The target node
19538 * @param {Roo.EventObject} e The raw event object
19543 * Fires when a template node is double clicked.
19544 * @param {Roo.View} this
19545 * @param {Number} index The index of the target node
19546 * @param {HTMLElement} node The target node
19547 * @param {Roo.EventObject} e The raw event object
19551 * @event contextmenu
19552 * Fires when a template node is right clicked.
19553 * @param {Roo.View} this
19554 * @param {Number} index The index of the target node
19555 * @param {HTMLElement} node The target node
19556 * @param {Roo.EventObject} e The raw event object
19558 "contextmenu" : true,
19560 * @event selectionchange
19561 * Fires when the selected nodes change.
19562 * @param {Roo.View} this
19563 * @param {Array} selections Array of the selected nodes
19565 "selectionchange" : true,
19568 * @event beforeselect
19569 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19570 * @param {Roo.View} this
19571 * @param {HTMLElement} node The node to be selected
19572 * @param {Array} selections Array of currently selected nodes
19574 "beforeselect" : true,
19576 * @event preparedata
19577 * Fires on every row to render, to allow you to change the data.
19578 * @param {Roo.View} this
19579 * @param {Object} data to be rendered (change this)
19581 "preparedata" : true
19589 "click": this.onClick,
19590 "dblclick": this.onDblClick,
19591 "contextmenu": this.onContextMenu,
19595 this.selections = [];
19597 this.cmp = new Roo.CompositeElementLite([]);
19599 this.store = Roo.factory(this.store, Roo.data);
19600 this.setStore(this.store, true);
19603 if ( this.footer && this.footer.xtype) {
19605 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19607 this.footer.dataSource = this.store;
19608 this.footer.container = fctr;
19609 this.footer = Roo.factory(this.footer, Roo);
19610 fctr.insertFirst(this.el);
19612 // this is a bit insane - as the paging toolbar seems to detach the el..
19613 // dom.parentNode.parentNode.parentNode
19614 // they get detached?
19618 Roo.View.superclass.constructor.call(this);
19623 Roo.extend(Roo.View, Roo.util.Observable, {
19626 * @cfg {Roo.data.Store} store Data store to load data from.
19631 * @cfg {String|Roo.Element} el The container element.
19636 * @cfg {String|Roo.Template} tpl The template used by this View
19640 * @cfg {String} dataName the named area of the template to use as the data area
19641 * Works with domtemplates roo-name="name"
19645 * @cfg {String} selectedClass The css class to add to selected nodes
19647 selectedClass : "x-view-selected",
19649 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19654 * @cfg {String} text to display on mask (default Loading)
19658 * @cfg {Boolean} multiSelect Allow multiple selection
19660 multiSelect : false,
19662 * @cfg {Boolean} singleSelect Allow single selection
19664 singleSelect: false,
19667 * @cfg {Boolean} toggleSelect - selecting
19669 toggleSelect : false,
19672 * @cfg {Boolean} tickable - selecting
19677 * Returns the element this view is bound to.
19678 * @return {Roo.Element}
19680 getEl : function(){
19681 return this.wrapEl;
19687 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19689 refresh : function(){
19690 //Roo.log('refresh');
19693 // if we are using something like 'domtemplate', then
19694 // the what gets used is:
19695 // t.applySubtemplate(NAME, data, wrapping data..)
19696 // the outer template then get' applied with
19697 // the store 'extra data'
19698 // and the body get's added to the
19699 // roo-name="data" node?
19700 // <span class='roo-tpl-{name}'></span> ?????
19704 this.clearSelections();
19705 this.el.update("");
19707 var records = this.store.getRange();
19708 if(records.length < 1) {
19710 // is this valid?? = should it render a template??
19712 this.el.update(this.emptyText);
19716 if (this.dataName) {
19717 this.el.update(t.apply(this.store.meta)); //????
19718 el = this.el.child('.roo-tpl-' + this.dataName);
19721 for(var i = 0, len = records.length; i < len; i++){
19722 var data = this.prepareData(records[i].data, i, records[i]);
19723 this.fireEvent("preparedata", this, data, i, records[i]);
19725 var d = Roo.apply({}, data);
19728 Roo.apply(d, {'roo-id' : Roo.id()});
19732 Roo.each(this.parent.item, function(item){
19733 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19736 Roo.apply(d, {'roo-data-checked' : 'checked'});
19740 html[html.length] = Roo.util.Format.trim(
19742 t.applySubtemplate(this.dataName, d, this.store.meta) :
19749 el.update(html.join(""));
19750 this.nodes = el.dom.childNodes;
19751 this.updateIndexes(0);
19756 * Function to override to reformat the data that is sent to
19757 * the template for each node.
19758 * DEPRICATED - use the preparedata event handler.
19759 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19760 * a JSON object for an UpdateManager bound view).
19762 prepareData : function(data, index, record)
19764 this.fireEvent("preparedata", this, data, index, record);
19768 onUpdate : function(ds, record){
19769 // Roo.log('on update');
19770 this.clearSelections();
19771 var index = this.store.indexOf(record);
19772 var n = this.nodes[index];
19773 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19774 n.parentNode.removeChild(n);
19775 this.updateIndexes(index, index);
19781 onAdd : function(ds, records, index)
19783 //Roo.log(['on Add', ds, records, index] );
19784 this.clearSelections();
19785 if(this.nodes.length == 0){
19789 var n = this.nodes[index];
19790 for(var i = 0, len = records.length; i < len; i++){
19791 var d = this.prepareData(records[i].data, i, records[i]);
19793 this.tpl.insertBefore(n, d);
19796 this.tpl.append(this.el, d);
19799 this.updateIndexes(index);
19802 onRemove : function(ds, record, index){
19803 // Roo.log('onRemove');
19804 this.clearSelections();
19805 var el = this.dataName ?
19806 this.el.child('.roo-tpl-' + this.dataName) :
19809 el.dom.removeChild(this.nodes[index]);
19810 this.updateIndexes(index);
19814 * Refresh an individual node.
19815 * @param {Number} index
19817 refreshNode : function(index){
19818 this.onUpdate(this.store, this.store.getAt(index));
19821 updateIndexes : function(startIndex, endIndex){
19822 var ns = this.nodes;
19823 startIndex = startIndex || 0;
19824 endIndex = endIndex || ns.length - 1;
19825 for(var i = startIndex; i <= endIndex; i++){
19826 ns[i].nodeIndex = i;
19831 * Changes the data store this view uses and refresh the view.
19832 * @param {Store} store
19834 setStore : function(store, initial){
19835 if(!initial && this.store){
19836 this.store.un("datachanged", this.refresh);
19837 this.store.un("add", this.onAdd);
19838 this.store.un("remove", this.onRemove);
19839 this.store.un("update", this.onUpdate);
19840 this.store.un("clear", this.refresh);
19841 this.store.un("beforeload", this.onBeforeLoad);
19842 this.store.un("load", this.onLoad);
19843 this.store.un("loadexception", this.onLoad);
19847 store.on("datachanged", this.refresh, this);
19848 store.on("add", this.onAdd, this);
19849 store.on("remove", this.onRemove, this);
19850 store.on("update", this.onUpdate, this);
19851 store.on("clear", this.refresh, this);
19852 store.on("beforeload", this.onBeforeLoad, this);
19853 store.on("load", this.onLoad, this);
19854 store.on("loadexception", this.onLoad, this);
19862 * onbeforeLoad - masks the loading area.
19865 onBeforeLoad : function(store,opts)
19867 //Roo.log('onBeforeLoad');
19869 this.el.update("");
19871 this.el.mask(this.mask ? this.mask : "Loading" );
19873 onLoad : function ()
19880 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19881 * @param {HTMLElement} node
19882 * @return {HTMLElement} The template node
19884 findItemFromChild : function(node){
19885 var el = this.dataName ?
19886 this.el.child('.roo-tpl-' + this.dataName,true) :
19889 if(!node || node.parentNode == el){
19892 var p = node.parentNode;
19893 while(p && p != el){
19894 if(p.parentNode == el){
19903 onClick : function(e){
19904 var item = this.findItemFromChild(e.getTarget());
19906 var index = this.indexOf(item);
19907 if(this.onItemClick(item, index, e) !== false){
19908 this.fireEvent("click", this, index, item, e);
19911 this.clearSelections();
19916 onContextMenu : function(e){
19917 var item = this.findItemFromChild(e.getTarget());
19919 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19924 onDblClick : function(e){
19925 var item = this.findItemFromChild(e.getTarget());
19927 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19931 onItemClick : function(item, index, e)
19933 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19936 if (this.toggleSelect) {
19937 var m = this.isSelected(item) ? 'unselect' : 'select';
19940 _t[m](item, true, false);
19943 if(this.multiSelect || this.singleSelect){
19944 if(this.multiSelect && e.shiftKey && this.lastSelection){
19945 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19947 this.select(item, this.multiSelect && e.ctrlKey);
19948 this.lastSelection = item;
19951 if(!this.tickable){
19952 e.preventDefault();
19960 * Get the number of selected nodes.
19963 getSelectionCount : function(){
19964 return this.selections.length;
19968 * Get the currently selected nodes.
19969 * @return {Array} An array of HTMLElements
19971 getSelectedNodes : function(){
19972 return this.selections;
19976 * Get the indexes of the selected nodes.
19979 getSelectedIndexes : function(){
19980 var indexes = [], s = this.selections;
19981 for(var i = 0, len = s.length; i < len; i++){
19982 indexes.push(s[i].nodeIndex);
19988 * Clear all selections
19989 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19991 clearSelections : function(suppressEvent){
19992 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19993 this.cmp.elements = this.selections;
19994 this.cmp.removeClass(this.selectedClass);
19995 this.selections = [];
19996 if(!suppressEvent){
19997 this.fireEvent("selectionchange", this, this.selections);
20003 * Returns true if the passed node is selected
20004 * @param {HTMLElement/Number} node The node or node index
20005 * @return {Boolean}
20007 isSelected : function(node){
20008 var s = this.selections;
20012 node = this.getNode(node);
20013 return s.indexOf(node) !== -1;
20018 * @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
20019 * @param {Boolean} keepExisting (optional) true to keep existing selections
20020 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20022 select : function(nodeInfo, keepExisting, suppressEvent){
20023 if(nodeInfo instanceof Array){
20025 this.clearSelections(true);
20027 for(var i = 0, len = nodeInfo.length; i < len; i++){
20028 this.select(nodeInfo[i], true, true);
20032 var node = this.getNode(nodeInfo);
20033 if(!node || this.isSelected(node)){
20034 return; // already selected.
20037 this.clearSelections(true);
20040 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20041 Roo.fly(node).addClass(this.selectedClass);
20042 this.selections.push(node);
20043 if(!suppressEvent){
20044 this.fireEvent("selectionchange", this, this.selections);
20052 * @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
20053 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20054 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20056 unselect : function(nodeInfo, keepExisting, suppressEvent)
20058 if(nodeInfo instanceof Array){
20059 Roo.each(this.selections, function(s) {
20060 this.unselect(s, nodeInfo);
20064 var node = this.getNode(nodeInfo);
20065 if(!node || !this.isSelected(node)){
20066 //Roo.log("not selected");
20067 return; // not selected.
20071 Roo.each(this.selections, function(s) {
20073 Roo.fly(node).removeClass(this.selectedClass);
20080 this.selections= ns;
20081 this.fireEvent("selectionchange", this, this.selections);
20085 * Gets a template node.
20086 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20087 * @return {HTMLElement} The node or null if it wasn't found
20089 getNode : function(nodeInfo){
20090 if(typeof nodeInfo == "string"){
20091 return document.getElementById(nodeInfo);
20092 }else if(typeof nodeInfo == "number"){
20093 return this.nodes[nodeInfo];
20099 * Gets a range template nodes.
20100 * @param {Number} startIndex
20101 * @param {Number} endIndex
20102 * @return {Array} An array of nodes
20104 getNodes : function(start, end){
20105 var ns = this.nodes;
20106 start = start || 0;
20107 end = typeof end == "undefined" ? ns.length - 1 : end;
20110 for(var i = start; i <= end; i++){
20114 for(var i = start; i >= end; i--){
20122 * Finds the index of the passed node
20123 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20124 * @return {Number} The index of the node or -1
20126 indexOf : function(node){
20127 node = this.getNode(node);
20128 if(typeof node.nodeIndex == "number"){
20129 return node.nodeIndex;
20131 var ns = this.nodes;
20132 for(var i = 0, len = ns.length; i < len; i++){
20143 * based on jquery fullcalendar
20147 Roo.bootstrap = Roo.bootstrap || {};
20149 * @class Roo.bootstrap.Calendar
20150 * @extends Roo.bootstrap.Component
20151 * Bootstrap Calendar class
20152 * @cfg {Boolean} loadMask (true|false) default false
20153 * @cfg {Object} header generate the user specific header of the calendar, default false
20156 * Create a new Container
20157 * @param {Object} config The config object
20162 Roo.bootstrap.Calendar = function(config){
20163 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20167 * Fires when a date is selected
20168 * @param {DatePicker} this
20169 * @param {Date} date The selected date
20173 * @event monthchange
20174 * Fires when the displayed month changes
20175 * @param {DatePicker} this
20176 * @param {Date} date The selected month
20178 'monthchange': true,
20180 * @event evententer
20181 * Fires when mouse over an event
20182 * @param {Calendar} this
20183 * @param {event} Event
20185 'evententer': true,
20187 * @event eventleave
20188 * Fires when the mouse leaves an
20189 * @param {Calendar} this
20192 'eventleave': true,
20194 * @event eventclick
20195 * Fires when the mouse click an
20196 * @param {Calendar} this
20205 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20208 * @cfg {Roo.data.Store} store
20209 * The data source for the calendar
20213 * @cfg {Number} startDay
20214 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20222 getAutoCreate : function(){
20225 var fc_button = function(name, corner, style, content ) {
20226 return Roo.apply({},{
20228 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20230 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20233 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20244 style : 'width:100%',
20251 cls : 'fc-header-left',
20253 fc_button('prev', 'left', 'arrow', '‹' ),
20254 fc_button('next', 'right', 'arrow', '›' ),
20255 { tag: 'span', cls: 'fc-header-space' },
20256 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20264 cls : 'fc-header-center',
20268 cls: 'fc-header-title',
20271 html : 'month / year'
20279 cls : 'fc-header-right',
20281 /* fc_button('month', 'left', '', 'month' ),
20282 fc_button('week', '', '', 'week' ),
20283 fc_button('day', 'right', '', 'day' )
20295 header = this.header;
20298 var cal_heads = function() {
20300 // fixme - handle this.
20302 for (var i =0; i < Date.dayNames.length; i++) {
20303 var d = Date.dayNames[i];
20306 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20307 html : d.substring(0,3)
20311 ret[0].cls += ' fc-first';
20312 ret[6].cls += ' fc-last';
20315 var cal_cell = function(n) {
20318 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20323 cls: 'fc-day-number',
20327 cls: 'fc-day-content',
20331 style: 'position: relative;' // height: 17px;
20343 var cal_rows = function() {
20346 for (var r = 0; r < 6; r++) {
20353 for (var i =0; i < Date.dayNames.length; i++) {
20354 var d = Date.dayNames[i];
20355 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20358 row.cn[0].cls+=' fc-first';
20359 row.cn[0].cn[0].style = 'min-height:90px';
20360 row.cn[6].cls+=' fc-last';
20364 ret[0].cls += ' fc-first';
20365 ret[4].cls += ' fc-prev-last';
20366 ret[5].cls += ' fc-last';
20373 cls: 'fc-border-separate',
20374 style : 'width:100%',
20382 cls : 'fc-first fc-last',
20400 cls : 'fc-content',
20401 style : "position: relative;",
20404 cls : 'fc-view fc-view-month fc-grid',
20405 style : 'position: relative',
20406 unselectable : 'on',
20409 cls : 'fc-event-container',
20410 style : 'position:absolute;z-index:8;top:0;left:0;'
20428 initEvents : function()
20431 throw "can not find store for calendar";
20437 style: "text-align:center",
20441 style: "background-color:white;width:50%;margin:250 auto",
20445 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20456 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20458 var size = this.el.select('.fc-content', true).first().getSize();
20459 this.maskEl.setSize(size.width, size.height);
20460 this.maskEl.enableDisplayMode("block");
20461 if(!this.loadMask){
20462 this.maskEl.hide();
20465 this.store = Roo.factory(this.store, Roo.data);
20466 this.store.on('load', this.onLoad, this);
20467 this.store.on('beforeload', this.onBeforeLoad, this);
20471 this.cells = this.el.select('.fc-day',true);
20472 //Roo.log(this.cells);
20473 this.textNodes = this.el.query('.fc-day-number');
20474 this.cells.addClassOnOver('fc-state-hover');
20476 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20477 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20478 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20479 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20481 this.on('monthchange', this.onMonthChange, this);
20483 this.update(new Date().clearTime());
20486 resize : function() {
20487 var sz = this.el.getSize();
20489 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20490 this.el.select('.fc-day-content div',true).setHeight(34);
20495 showPrevMonth : function(e){
20496 this.update(this.activeDate.add("mo", -1));
20498 showToday : function(e){
20499 this.update(new Date().clearTime());
20502 showNextMonth : function(e){
20503 this.update(this.activeDate.add("mo", 1));
20507 showPrevYear : function(){
20508 this.update(this.activeDate.add("y", -1));
20512 showNextYear : function(){
20513 this.update(this.activeDate.add("y", 1));
20518 update : function(date)
20520 var vd = this.activeDate;
20521 this.activeDate = date;
20522 // if(vd && this.el){
20523 // var t = date.getTime();
20524 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20525 // Roo.log('using add remove');
20527 // this.fireEvent('monthchange', this, date);
20529 // this.cells.removeClass("fc-state-highlight");
20530 // this.cells.each(function(c){
20531 // if(c.dateValue == t){
20532 // c.addClass("fc-state-highlight");
20533 // setTimeout(function(){
20534 // try{c.dom.firstChild.focus();}catch(e){}
20544 var days = date.getDaysInMonth();
20546 var firstOfMonth = date.getFirstDateOfMonth();
20547 var startingPos = firstOfMonth.getDay()-this.startDay;
20549 if(startingPos < this.startDay){
20553 var pm = date.add(Date.MONTH, -1);
20554 var prevStart = pm.getDaysInMonth()-startingPos;
20556 this.cells = this.el.select('.fc-day',true);
20557 this.textNodes = this.el.query('.fc-day-number');
20558 this.cells.addClassOnOver('fc-state-hover');
20560 var cells = this.cells.elements;
20561 var textEls = this.textNodes;
20563 Roo.each(cells, function(cell){
20564 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20567 days += startingPos;
20569 // convert everything to numbers so it's fast
20570 var day = 86400000;
20571 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20574 //Roo.log(prevStart);
20576 var today = new Date().clearTime().getTime();
20577 var sel = date.clearTime().getTime();
20578 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20579 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20580 var ddMatch = this.disabledDatesRE;
20581 var ddText = this.disabledDatesText;
20582 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20583 var ddaysText = this.disabledDaysText;
20584 var format = this.format;
20586 var setCellClass = function(cal, cell){
20590 //Roo.log('set Cell Class');
20592 var t = d.getTime();
20596 cell.dateValue = t;
20598 cell.className += " fc-today";
20599 cell.className += " fc-state-highlight";
20600 cell.title = cal.todayText;
20603 // disable highlight in other month..
20604 //cell.className += " fc-state-highlight";
20609 cell.className = " fc-state-disabled";
20610 cell.title = cal.minText;
20614 cell.className = " fc-state-disabled";
20615 cell.title = cal.maxText;
20619 if(ddays.indexOf(d.getDay()) != -1){
20620 cell.title = ddaysText;
20621 cell.className = " fc-state-disabled";
20624 if(ddMatch && format){
20625 var fvalue = d.dateFormat(format);
20626 if(ddMatch.test(fvalue)){
20627 cell.title = ddText.replace("%0", fvalue);
20628 cell.className = " fc-state-disabled";
20632 if (!cell.initialClassName) {
20633 cell.initialClassName = cell.dom.className;
20636 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20641 for(; i < startingPos; i++) {
20642 textEls[i].innerHTML = (++prevStart);
20643 d.setDate(d.getDate()+1);
20645 cells[i].className = "fc-past fc-other-month";
20646 setCellClass(this, cells[i]);
20651 for(; i < days; i++){
20652 intDay = i - startingPos + 1;
20653 textEls[i].innerHTML = (intDay);
20654 d.setDate(d.getDate()+1);
20656 cells[i].className = ''; // "x-date-active";
20657 setCellClass(this, cells[i]);
20661 for(; i < 42; i++) {
20662 textEls[i].innerHTML = (++extraDays);
20663 d.setDate(d.getDate()+1);
20665 cells[i].className = "fc-future fc-other-month";
20666 setCellClass(this, cells[i]);
20669 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20671 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20673 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20674 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20676 if(totalRows != 6){
20677 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20678 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20681 this.fireEvent('monthchange', this, date);
20685 if(!this.internalRender){
20686 var main = this.el.dom.firstChild;
20687 var w = main.offsetWidth;
20688 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20689 Roo.fly(main).setWidth(w);
20690 this.internalRender = true;
20691 // opera does not respect the auto grow header center column
20692 // then, after it gets a width opera refuses to recalculate
20693 // without a second pass
20694 if(Roo.isOpera && !this.secondPass){
20695 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20696 this.secondPass = true;
20697 this.update.defer(10, this, [date]);
20704 findCell : function(dt) {
20705 dt = dt.clearTime().getTime();
20707 this.cells.each(function(c){
20708 //Roo.log("check " +c.dateValue + '?=' + dt);
20709 if(c.dateValue == dt){
20719 findCells : function(ev) {
20720 var s = ev.start.clone().clearTime().getTime();
20722 var e= ev.end.clone().clearTime().getTime();
20725 this.cells.each(function(c){
20726 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20728 if(c.dateValue > e){
20731 if(c.dateValue < s){
20740 // findBestRow: function(cells)
20744 // for (var i =0 ; i < cells.length;i++) {
20745 // ret = Math.max(cells[i].rows || 0,ret);
20752 addItem : function(ev)
20754 // look for vertical location slot in
20755 var cells = this.findCells(ev);
20757 // ev.row = this.findBestRow(cells);
20759 // work out the location.
20763 for(var i =0; i < cells.length; i++) {
20765 cells[i].row = cells[0].row;
20768 cells[i].row = cells[i].row + 1;
20778 if (crow.start.getY() == cells[i].getY()) {
20780 crow.end = cells[i];
20797 cells[0].events.push(ev);
20799 this.calevents.push(ev);
20802 clearEvents: function() {
20804 if(!this.calevents){
20808 Roo.each(this.cells.elements, function(c){
20814 Roo.each(this.calevents, function(e) {
20815 Roo.each(e.els, function(el) {
20816 el.un('mouseenter' ,this.onEventEnter, this);
20817 el.un('mouseleave' ,this.onEventLeave, this);
20822 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20828 renderEvents: function()
20832 this.cells.each(function(c) {
20841 if(c.row != c.events.length){
20842 r = 4 - (4 - (c.row - c.events.length));
20845 c.events = ev.slice(0, r);
20846 c.more = ev.slice(r);
20848 if(c.more.length && c.more.length == 1){
20849 c.events.push(c.more.pop());
20852 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20856 this.cells.each(function(c) {
20858 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20861 for (var e = 0; e < c.events.length; e++){
20862 var ev = c.events[e];
20863 var rows = ev.rows;
20865 for(var i = 0; i < rows.length; i++) {
20867 // how many rows should it span..
20870 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20871 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20873 unselectable : "on",
20876 cls: 'fc-event-inner',
20880 // cls: 'fc-event-time',
20881 // html : cells.length > 1 ? '' : ev.time
20885 cls: 'fc-event-title',
20886 html : String.format('{0}', ev.title)
20893 cls: 'ui-resizable-handle ui-resizable-e',
20894 html : '  '
20901 cfg.cls += ' fc-event-start';
20903 if ((i+1) == rows.length) {
20904 cfg.cls += ' fc-event-end';
20907 var ctr = _this.el.select('.fc-event-container',true).first();
20908 var cg = ctr.createChild(cfg);
20910 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20911 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20913 var r = (c.more.length) ? 1 : 0;
20914 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20915 cg.setWidth(ebox.right - sbox.x -2);
20917 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20918 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20919 cg.on('click', _this.onEventClick, _this, ev);
20930 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20931 style : 'position: absolute',
20932 unselectable : "on",
20935 cls: 'fc-event-inner',
20939 cls: 'fc-event-title',
20947 cls: 'ui-resizable-handle ui-resizable-e',
20948 html : '  '
20954 var ctr = _this.el.select('.fc-event-container',true).first();
20955 var cg = ctr.createChild(cfg);
20957 var sbox = c.select('.fc-day-content',true).first().getBox();
20958 var ebox = c.select('.fc-day-content',true).first().getBox();
20960 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20961 cg.setWidth(ebox.right - sbox.x -2);
20963 cg.on('click', _this.onMoreEventClick, _this, c.more);
20973 onEventEnter: function (e, el,event,d) {
20974 this.fireEvent('evententer', this, el, event);
20977 onEventLeave: function (e, el,event,d) {
20978 this.fireEvent('eventleave', this, el, event);
20981 onEventClick: function (e, el,event,d) {
20982 this.fireEvent('eventclick', this, el, event);
20985 onMonthChange: function () {
20989 onMoreEventClick: function(e, el, more)
20993 this.calpopover.placement = 'right';
20994 this.calpopover.setTitle('More');
20996 this.calpopover.setContent('');
20998 var ctr = this.calpopover.el.select('.popover-content', true).first();
21000 Roo.each(more, function(m){
21002 cls : 'fc-event-hori fc-event-draggable',
21005 var cg = ctr.createChild(cfg);
21007 cg.on('click', _this.onEventClick, _this, m);
21010 this.calpopover.show(el);
21015 onLoad: function ()
21017 this.calevents = [];
21020 if(this.store.getCount() > 0){
21021 this.store.data.each(function(d){
21024 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21025 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21026 time : d.data.start_time,
21027 title : d.data.title,
21028 description : d.data.description,
21029 venue : d.data.venue
21034 this.renderEvents();
21036 if(this.calevents.length && this.loadMask){
21037 this.maskEl.hide();
21041 onBeforeLoad: function()
21043 this.clearEvents();
21045 this.maskEl.show();
21059 * @class Roo.bootstrap.Popover
21060 * @extends Roo.bootstrap.Component
21061 * Bootstrap Popover class
21062 * @cfg {String} html contents of the popover (or false to use children..)
21063 * @cfg {String} title of popover (or false to hide)
21064 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21065 * @cfg {String} trigger click || hover (or false to trigger manually)
21066 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21067 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21068 * - if false and it has a 'parent' then it will be automatically added to that element
21069 * - if string - Roo.get will be called
21070 * @cfg {Number} delay - delay before showing
21073 * Create a new Popover
21074 * @param {Object} config The config object
21077 Roo.bootstrap.Popover = function(config){
21078 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21084 * After the popover show
21086 * @param {Roo.bootstrap.Popover} this
21091 * After the popover hide
21093 * @param {Roo.bootstrap.Popover} this
21099 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21104 placement : 'right',
21105 trigger : 'hover', // hover
21111 can_build_overlaid : false,
21113 maskEl : false, // the mask element
21116 alignEl : false, // when show is called with an element - this get's stored.
21118 getChildContainer : function()
21120 return this.contentEl;
21123 getPopoverHeader : function()
21125 this.title = true; // flag not to hide it..
21126 this.headerEl.addClass('p-0');
21127 return this.headerEl
21131 getAutoCreate : function(){
21134 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21135 style: 'display:block',
21141 cls : 'popover-inner ',
21145 cls: 'popover-title popover-header',
21146 html : this.title === false ? '' : this.title
21149 cls : 'popover-content popover-body ' + (this.cls || ''),
21150 html : this.html || ''
21161 * @param {string} the title
21163 setTitle: function(str)
21167 this.headerEl.dom.innerHTML = str;
21172 * @param {string} the body content
21174 setContent: function(str)
21177 if (this.contentEl) {
21178 this.contentEl.dom.innerHTML = str;
21182 // as it get's added to the bottom of the page.
21183 onRender : function(ct, position)
21185 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21190 var cfg = Roo.apply({}, this.getAutoCreate());
21194 cfg.cls += ' ' + this.cls;
21197 cfg.style = this.style;
21199 //Roo.log("adding to ");
21200 this.el = Roo.get(document.body).createChild(cfg, position);
21201 // Roo.log(this.el);
21204 this.contentEl = this.el.select('.popover-content',true).first();
21205 this.headerEl = this.el.select('.popover-title',true).first();
21208 if(typeof(this.items) != 'undefined'){
21209 var items = this.items;
21212 for(var i =0;i < items.length;i++) {
21213 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21217 this.items = nitems;
21219 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21220 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21227 resizeMask : function()
21229 this.maskEl.setSize(
21230 Roo.lib.Dom.getViewWidth(true),
21231 Roo.lib.Dom.getViewHeight(true)
21235 initEvents : function()
21239 Roo.bootstrap.Popover.register(this);
21242 this.arrowEl = this.el.select('.arrow',true).first();
21243 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21244 this.el.enableDisplayMode('block');
21248 if (this.over === false && !this.parent()) {
21251 if (this.triggers === false) {
21256 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21257 var triggers = this.trigger ? this.trigger.split(' ') : [];
21258 Roo.each(triggers, function(trigger) {
21260 if (trigger == 'click') {
21261 on_el.on('click', this.toggle, this);
21262 } else if (trigger != 'manual') {
21263 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21264 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21266 on_el.on(eventIn ,this.enter, this);
21267 on_el.on(eventOut, this.leave, this);
21277 toggle : function () {
21278 this.hoverState == 'in' ? this.leave() : this.enter();
21281 enter : function () {
21283 clearTimeout(this.timeout);
21285 this.hoverState = 'in';
21287 if (!this.delay || !this.delay.show) {
21292 this.timeout = setTimeout(function () {
21293 if (_t.hoverState == 'in') {
21296 }, this.delay.show)
21299 leave : function() {
21300 clearTimeout(this.timeout);
21302 this.hoverState = 'out';
21304 if (!this.delay || !this.delay.hide) {
21309 this.timeout = setTimeout(function () {
21310 if (_t.hoverState == 'out') {
21313 }, this.delay.hide)
21317 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21318 * @param {string} (left|right|top|bottom) position
21320 show : function (on_el, placement)
21322 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21323 on_el = on_el || false; // default to false
21326 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21327 on_el = this.parent().el;
21328 } else if (this.over) {
21329 on_el = Roo.get(this.over);
21334 this.alignEl = Roo.get( on_el );
21337 this.render(document.body);
21343 if (this.title === false) {
21344 this.headerEl.hide();
21349 this.el.dom.style.display = 'block';
21352 if (this.alignEl) {
21353 this.updatePosition(this.placement, true);
21356 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21357 var es = this.el.getSize();
21358 var x = Roo.lib.Dom.getViewWidth()/2;
21359 var y = Roo.lib.Dom.getViewHeight()/2;
21360 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21365 //var arrow = this.el.select('.arrow',true).first();
21366 //arrow.set(align[2],
21368 this.el.addClass('in');
21372 this.hoverState = 'in';
21375 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21376 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21377 this.maskEl.dom.style.display = 'block';
21378 this.maskEl.addClass('show');
21380 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21382 this.fireEvent('show', this);
21386 * fire this manually after loading a grid in the table for example
21387 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21388 * @param {Boolean} try and move it if we cant get right position.
21390 updatePosition : function(placement, try_move)
21392 // allow for calling with no parameters
21393 placement = placement ? placement : this.placement;
21394 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21396 this.el.removeClass([
21397 'fade','top','bottom', 'left', 'right','in',
21398 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21400 this.el.addClass(placement + ' bs-popover-' + placement);
21402 if (!this.alignEl ) {
21406 switch (placement) {
21408 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21409 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21410 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21411 //normal display... or moved up/down.
21412 this.el.setXY(offset);
21413 var xy = this.alignEl.getAnchorXY('tr', false);
21415 this.arrowEl.setXY(xy);
21418 // continue through...
21419 return this.updatePosition('left', false);
21423 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21424 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21425 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21426 //normal display... or moved up/down.
21427 this.el.setXY(offset);
21428 var xy = this.alignEl.getAnchorXY('tl', false);
21429 xy[0]-=10;xy[1]+=5; // << fix me
21430 this.arrowEl.setXY(xy);
21434 return this.updatePosition('right', false);
21437 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21438 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21439 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21440 //normal display... or moved up/down.
21441 this.el.setXY(offset);
21442 var xy = this.alignEl.getAnchorXY('t', false);
21443 xy[1]-=10; // << fix me
21444 this.arrowEl.setXY(xy);
21448 return this.updatePosition('bottom', false);
21451 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21452 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21453 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21454 //normal display... or moved up/down.
21455 this.el.setXY(offset);
21456 var xy = this.alignEl.getAnchorXY('b', false);
21457 xy[1]+=2; // << fix me
21458 this.arrowEl.setXY(xy);
21462 return this.updatePosition('top', false);
21473 this.el.setXY([0,0]);
21474 this.el.removeClass('in');
21476 this.hoverState = null;
21477 this.maskEl.hide(); // always..
21478 this.fireEvent('hide', this);
21484 Roo.apply(Roo.bootstrap.Popover, {
21487 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21488 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21489 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21490 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21495 clickHander : false,
21499 onMouseDown : function(e)
21501 if (this.popups.length && !e.getTarget(".roo-popover")) {
21502 /// what is nothing is showing..
21511 register : function(popup)
21513 if (!Roo.bootstrap.Popover.clickHandler) {
21514 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21516 // hide other popups.
21517 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21518 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21519 this.hideAll(); //<< why?
21520 //this.popups.push(popup);
21522 hideAll : function()
21524 this.popups.forEach(function(p) {
21528 onShow : function() {
21529 Roo.bootstrap.Popover.popups.push(this);
21531 onHide : function() {
21532 Roo.bootstrap.Popover.popups.remove(this);
21538 * Card header - holder for the card header elements.
21543 * @class Roo.bootstrap.PopoverNav
21544 * @extends Roo.bootstrap.NavGroup
21545 * Bootstrap Popover header navigation class
21547 * Create a new Popover Header Navigation
21548 * @param {Object} config The config object
21551 Roo.bootstrap.PopoverNav = function(config){
21552 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21555 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21558 container_method : 'getPopoverHeader'
21576 * @class Roo.bootstrap.Progress
21577 * @extends Roo.bootstrap.Component
21578 * Bootstrap Progress class
21579 * @cfg {Boolean} striped striped of the progress bar
21580 * @cfg {Boolean} active animated of the progress bar
21584 * Create a new Progress
21585 * @param {Object} config The config object
21588 Roo.bootstrap.Progress = function(config){
21589 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21592 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21597 getAutoCreate : function(){
21605 cfg.cls += ' progress-striped';
21609 cfg.cls += ' active';
21628 * @class Roo.bootstrap.ProgressBar
21629 * @extends Roo.bootstrap.Component
21630 * Bootstrap ProgressBar class
21631 * @cfg {Number} aria_valuenow aria-value now
21632 * @cfg {Number} aria_valuemin aria-value min
21633 * @cfg {Number} aria_valuemax aria-value max
21634 * @cfg {String} label label for the progress bar
21635 * @cfg {String} panel (success | info | warning | danger )
21636 * @cfg {String} role role of the progress bar
21637 * @cfg {String} sr_only text
21641 * Create a new ProgressBar
21642 * @param {Object} config The config object
21645 Roo.bootstrap.ProgressBar = function(config){
21646 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21649 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21653 aria_valuemax : 100,
21659 getAutoCreate : function()
21664 cls: 'progress-bar',
21665 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21677 cfg.role = this.role;
21680 if(this.aria_valuenow){
21681 cfg['aria-valuenow'] = this.aria_valuenow;
21684 if(this.aria_valuemin){
21685 cfg['aria-valuemin'] = this.aria_valuemin;
21688 if(this.aria_valuemax){
21689 cfg['aria-valuemax'] = this.aria_valuemax;
21692 if(this.label && !this.sr_only){
21693 cfg.html = this.label;
21697 cfg.cls += ' progress-bar-' + this.panel;
21703 update : function(aria_valuenow)
21705 this.aria_valuenow = aria_valuenow;
21707 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21722 * @class Roo.bootstrap.TabGroup
21723 * @extends Roo.bootstrap.Column
21724 * Bootstrap Column class
21725 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21726 * @cfg {Boolean} carousel true to make the group behave like a carousel
21727 * @cfg {Boolean} bullets show bullets for the panels
21728 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21729 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21730 * @cfg {Boolean} showarrow (true|false) show arrow default true
21733 * Create a new TabGroup
21734 * @param {Object} config The config object
21737 Roo.bootstrap.TabGroup = function(config){
21738 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21740 this.navId = Roo.id();
21743 Roo.bootstrap.TabGroup.register(this);
21747 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21750 transition : false,
21755 slideOnTouch : false,
21758 getAutoCreate : function()
21760 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21762 cfg.cls += ' tab-content';
21764 if (this.carousel) {
21765 cfg.cls += ' carousel slide';
21768 cls : 'carousel-inner',
21772 if(this.bullets && !Roo.isTouch){
21775 cls : 'carousel-bullets',
21779 if(this.bullets_cls){
21780 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21787 cfg.cn[0].cn.push(bullets);
21790 if(this.showarrow){
21791 cfg.cn[0].cn.push({
21793 class : 'carousel-arrow',
21797 class : 'carousel-prev',
21801 class : 'fa fa-chevron-left'
21807 class : 'carousel-next',
21811 class : 'fa fa-chevron-right'
21824 initEvents: function()
21826 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21827 // this.el.on("touchstart", this.onTouchStart, this);
21830 if(this.autoslide){
21833 this.slideFn = window.setInterval(function() {
21834 _this.showPanelNext();
21838 if(this.showarrow){
21839 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21840 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21846 // onTouchStart : function(e, el, o)
21848 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21852 // this.showPanelNext();
21856 getChildContainer : function()
21858 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21862 * register a Navigation item
21863 * @param {Roo.bootstrap.NavItem} the navitem to add
21865 register : function(item)
21867 this.tabs.push( item);
21868 item.navId = this.navId; // not really needed..
21873 getActivePanel : function()
21876 Roo.each(this.tabs, function(t) {
21886 getPanelByName : function(n)
21889 Roo.each(this.tabs, function(t) {
21890 if (t.tabId == n) {
21898 indexOfPanel : function(p)
21901 Roo.each(this.tabs, function(t,i) {
21902 if (t.tabId == p.tabId) {
21911 * show a specific panel
21912 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21913 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21915 showPanel : function (pan)
21917 if(this.transition || typeof(pan) == 'undefined'){
21918 Roo.log("waiting for the transitionend");
21922 if (typeof(pan) == 'number') {
21923 pan = this.tabs[pan];
21926 if (typeof(pan) == 'string') {
21927 pan = this.getPanelByName(pan);
21930 var cur = this.getActivePanel();
21933 Roo.log('pan or acitve pan is undefined');
21937 if (pan.tabId == this.getActivePanel().tabId) {
21941 if (false === cur.fireEvent('beforedeactivate')) {
21945 if(this.bullets > 0 && !Roo.isTouch){
21946 this.setActiveBullet(this.indexOfPanel(pan));
21949 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21951 //class="carousel-item carousel-item-next carousel-item-left"
21953 this.transition = true;
21954 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21955 var lr = dir == 'next' ? 'left' : 'right';
21956 pan.el.addClass(dir); // or prev
21957 pan.el.addClass('carousel-item-' + dir); // or prev
21958 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21959 cur.el.addClass(lr); // or right
21960 pan.el.addClass(lr);
21961 cur.el.addClass('carousel-item-' +lr); // or right
21962 pan.el.addClass('carousel-item-' +lr);
21966 cur.el.on('transitionend', function() {
21967 Roo.log("trans end?");
21969 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21970 pan.setActive(true);
21972 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21973 cur.setActive(false);
21975 _this.transition = false;
21977 }, this, { single: true } );
21982 cur.setActive(false);
21983 pan.setActive(true);
21988 showPanelNext : function()
21990 var i = this.indexOfPanel(this.getActivePanel());
21992 if (i >= this.tabs.length - 1 && !this.autoslide) {
21996 if (i >= this.tabs.length - 1 && this.autoslide) {
22000 this.showPanel(this.tabs[i+1]);
22003 showPanelPrev : function()
22005 var i = this.indexOfPanel(this.getActivePanel());
22007 if (i < 1 && !this.autoslide) {
22011 if (i < 1 && this.autoslide) {
22012 i = this.tabs.length;
22015 this.showPanel(this.tabs[i-1]);
22019 addBullet: function()
22021 if(!this.bullets || Roo.isTouch){
22024 var ctr = this.el.select('.carousel-bullets',true).first();
22025 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22026 var bullet = ctr.createChild({
22027 cls : 'bullet bullet-' + i
22028 },ctr.dom.lastChild);
22033 bullet.on('click', (function(e, el, o, ii, t){
22035 e.preventDefault();
22037 this.showPanel(ii);
22039 if(this.autoslide && this.slideFn){
22040 clearInterval(this.slideFn);
22041 this.slideFn = window.setInterval(function() {
22042 _this.showPanelNext();
22046 }).createDelegate(this, [i, bullet], true));
22051 setActiveBullet : function(i)
22057 Roo.each(this.el.select('.bullet', true).elements, function(el){
22058 el.removeClass('selected');
22061 var bullet = this.el.select('.bullet-' + i, true).first();
22067 bullet.addClass('selected');
22078 Roo.apply(Roo.bootstrap.TabGroup, {
22082 * register a Navigation Group
22083 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22085 register : function(navgrp)
22087 this.groups[navgrp.navId] = navgrp;
22091 * fetch a Navigation Group based on the navigation ID
22092 * if one does not exist , it will get created.
22093 * @param {string} the navgroup to add
22094 * @returns {Roo.bootstrap.NavGroup} the navgroup
22096 get: function(navId) {
22097 if (typeof(this.groups[navId]) == 'undefined') {
22098 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22100 return this.groups[navId] ;
22115 * @class Roo.bootstrap.TabPanel
22116 * @extends Roo.bootstrap.Component
22117 * Bootstrap TabPanel class
22118 * @cfg {Boolean} active panel active
22119 * @cfg {String} html panel content
22120 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22121 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22122 * @cfg {String} href click to link..
22123 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22127 * Create a new TabPanel
22128 * @param {Object} config The config object
22131 Roo.bootstrap.TabPanel = function(config){
22132 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22136 * Fires when the active status changes
22137 * @param {Roo.bootstrap.TabPanel} this
22138 * @param {Boolean} state the new state
22143 * @event beforedeactivate
22144 * Fires before a tab is de-activated - can be used to do validation on a form.
22145 * @param {Roo.bootstrap.TabPanel} this
22146 * @return {Boolean} false if there is an error
22149 'beforedeactivate': true
22152 this.tabId = this.tabId || Roo.id();
22156 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22163 touchSlide : false,
22164 getAutoCreate : function(){
22169 // item is needed for carousel - not sure if it has any effect otherwise
22170 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22171 html: this.html || ''
22175 cfg.cls += ' active';
22179 cfg.tabId = this.tabId;
22187 initEvents: function()
22189 var p = this.parent();
22191 this.navId = this.navId || p.navId;
22193 if (typeof(this.navId) != 'undefined') {
22194 // not really needed.. but just in case.. parent should be a NavGroup.
22195 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22199 var i = tg.tabs.length - 1;
22201 if(this.active && tg.bullets > 0 && i < tg.bullets){
22202 tg.setActiveBullet(i);
22206 this.el.on('click', this.onClick, this);
22208 if(Roo.isTouch && this.touchSlide){
22209 this.el.on("touchstart", this.onTouchStart, this);
22210 this.el.on("touchmove", this.onTouchMove, this);
22211 this.el.on("touchend", this.onTouchEnd, this);
22216 onRender : function(ct, position)
22218 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22221 setActive : function(state)
22223 Roo.log("panel - set active " + this.tabId + "=" + state);
22225 this.active = state;
22227 this.el.removeClass('active');
22229 } else if (!this.el.hasClass('active')) {
22230 this.el.addClass('active');
22233 this.fireEvent('changed', this, state);
22236 onClick : function(e)
22238 e.preventDefault();
22240 if(!this.href.length){
22244 window.location.href = this.href;
22253 onTouchStart : function(e)
22255 this.swiping = false;
22257 this.startX = e.browserEvent.touches[0].clientX;
22258 this.startY = e.browserEvent.touches[0].clientY;
22261 onTouchMove : function(e)
22263 this.swiping = true;
22265 this.endX = e.browserEvent.touches[0].clientX;
22266 this.endY = e.browserEvent.touches[0].clientY;
22269 onTouchEnd : function(e)
22276 var tabGroup = this.parent();
22278 if(this.endX > this.startX){ // swiping right
22279 tabGroup.showPanelPrev();
22283 if(this.startX > this.endX){ // swiping left
22284 tabGroup.showPanelNext();
22303 * @class Roo.bootstrap.DateField
22304 * @extends Roo.bootstrap.Input
22305 * Bootstrap DateField class
22306 * @cfg {Number} weekStart default 0
22307 * @cfg {String} viewMode default empty, (months|years)
22308 * @cfg {String} minViewMode default empty, (months|years)
22309 * @cfg {Number} startDate default -Infinity
22310 * @cfg {Number} endDate default Infinity
22311 * @cfg {Boolean} todayHighlight default false
22312 * @cfg {Boolean} todayBtn default false
22313 * @cfg {Boolean} calendarWeeks default false
22314 * @cfg {Object} daysOfWeekDisabled default empty
22315 * @cfg {Boolean} singleMode default false (true | false)
22317 * @cfg {Boolean} keyboardNavigation default true
22318 * @cfg {String} language default en
22321 * Create a new DateField
22322 * @param {Object} config The config object
22325 Roo.bootstrap.DateField = function(config){
22326 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22330 * Fires when this field show.
22331 * @param {Roo.bootstrap.DateField} this
22332 * @param {Mixed} date The date value
22337 * Fires when this field hide.
22338 * @param {Roo.bootstrap.DateField} this
22339 * @param {Mixed} date The date value
22344 * Fires when select a date.
22345 * @param {Roo.bootstrap.DateField} this
22346 * @param {Mixed} date The date value
22350 * @event beforeselect
22351 * Fires when before select a date.
22352 * @param {Roo.bootstrap.DateField} this
22353 * @param {Mixed} date The date value
22355 beforeselect : true
22359 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22362 * @cfg {String} format
22363 * The default date format string which can be overriden for localization support. The format must be
22364 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22368 * @cfg {String} altFormats
22369 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22370 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22372 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22380 todayHighlight : false,
22386 keyboardNavigation: true,
22388 calendarWeeks: false,
22390 startDate: -Infinity,
22394 daysOfWeekDisabled: [],
22398 singleMode : false,
22400 UTCDate: function()
22402 return new Date(Date.UTC.apply(Date, arguments));
22405 UTCToday: function()
22407 var today = new Date();
22408 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22411 getDate: function() {
22412 var d = this.getUTCDate();
22413 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22416 getUTCDate: function() {
22420 setDate: function(d) {
22421 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22424 setUTCDate: function(d) {
22426 this.setValue(this.formatDate(this.date));
22429 onRender: function(ct, position)
22432 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22434 this.language = this.language || 'en';
22435 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22436 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22438 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22439 this.format = this.format || 'm/d/y';
22440 this.isInline = false;
22441 this.isInput = true;
22442 this.component = this.el.select('.add-on', true).first() || false;
22443 this.component = (this.component && this.component.length === 0) ? false : this.component;
22444 this.hasInput = this.component && this.inputEl().length;
22446 if (typeof(this.minViewMode === 'string')) {
22447 switch (this.minViewMode) {
22449 this.minViewMode = 1;
22452 this.minViewMode = 2;
22455 this.minViewMode = 0;
22460 if (typeof(this.viewMode === 'string')) {
22461 switch (this.viewMode) {
22474 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22476 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22478 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22480 this.picker().on('mousedown', this.onMousedown, this);
22481 this.picker().on('click', this.onClick, this);
22483 this.picker().addClass('datepicker-dropdown');
22485 this.startViewMode = this.viewMode;
22487 if(this.singleMode){
22488 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22489 v.setVisibilityMode(Roo.Element.DISPLAY);
22493 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22494 v.setStyle('width', '189px');
22498 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22499 if(!this.calendarWeeks){
22504 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22505 v.attr('colspan', function(i, val){
22506 return parseInt(val) + 1;
22511 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22513 this.setStartDate(this.startDate);
22514 this.setEndDate(this.endDate);
22516 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22523 if(this.isInline) {
22528 picker : function()
22530 return this.pickerEl;
22531 // return this.el.select('.datepicker', true).first();
22534 fillDow: function()
22536 var dowCnt = this.weekStart;
22545 if(this.calendarWeeks){
22553 while (dowCnt < this.weekStart + 7) {
22557 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22561 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22564 fillMonths: function()
22567 var months = this.picker().select('>.datepicker-months td', true).first();
22569 months.dom.innerHTML = '';
22575 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22578 months.createChild(month);
22585 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;
22587 if (this.date < this.startDate) {
22588 this.viewDate = new Date(this.startDate);
22589 } else if (this.date > this.endDate) {
22590 this.viewDate = new Date(this.endDate);
22592 this.viewDate = new Date(this.date);
22600 var d = new Date(this.viewDate),
22601 year = d.getUTCFullYear(),
22602 month = d.getUTCMonth(),
22603 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22604 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22605 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22606 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22607 currentDate = this.date && this.date.valueOf(),
22608 today = this.UTCToday();
22610 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22612 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22614 // this.picker.select('>tfoot th.today').
22615 // .text(dates[this.language].today)
22616 // .toggle(this.todayBtn !== false);
22618 this.updateNavArrows();
22621 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22623 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22625 prevMonth.setUTCDate(day);
22627 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22629 var nextMonth = new Date(prevMonth);
22631 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22633 nextMonth = nextMonth.valueOf();
22635 var fillMonths = false;
22637 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22639 while(prevMonth.valueOf() <= nextMonth) {
22642 if (prevMonth.getUTCDay() === this.weekStart) {
22644 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22652 if(this.calendarWeeks){
22653 // ISO 8601: First week contains first thursday.
22654 // ISO also states week starts on Monday, but we can be more abstract here.
22656 // Start of current week: based on weekstart/current date
22657 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22658 // Thursday of this week
22659 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22660 // First Thursday of year, year from thursday
22661 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22662 // Calendar week: ms between thursdays, div ms per day, div 7 days
22663 calWeek = (th - yth) / 864e5 / 7 + 1;
22665 fillMonths.cn.push({
22673 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22675 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22678 if (this.todayHighlight &&
22679 prevMonth.getUTCFullYear() == today.getFullYear() &&
22680 prevMonth.getUTCMonth() == today.getMonth() &&
22681 prevMonth.getUTCDate() == today.getDate()) {
22682 clsName += ' today';
22685 if (currentDate && prevMonth.valueOf() === currentDate) {
22686 clsName += ' active';
22689 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22690 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22691 clsName += ' disabled';
22694 fillMonths.cn.push({
22696 cls: 'day ' + clsName,
22697 html: prevMonth.getDate()
22700 prevMonth.setDate(prevMonth.getDate()+1);
22703 var currentYear = this.date && this.date.getUTCFullYear();
22704 var currentMonth = this.date && this.date.getUTCMonth();
22706 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22708 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22709 v.removeClass('active');
22711 if(currentYear === year && k === currentMonth){
22712 v.addClass('active');
22715 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22716 v.addClass('disabled');
22722 year = parseInt(year/10, 10) * 10;
22724 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22726 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22729 for (var i = -1; i < 11; i++) {
22730 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22732 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22740 showMode: function(dir)
22743 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22746 Roo.each(this.picker().select('>div',true).elements, function(v){
22747 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22750 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22755 if(this.isInline) {
22759 this.picker().removeClass(['bottom', 'top']);
22761 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22763 * place to the top of element!
22767 this.picker().addClass('top');
22768 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22773 this.picker().addClass('bottom');
22775 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22778 parseDate : function(value)
22780 if(!value || value instanceof Date){
22783 var v = Date.parseDate(value, this.format);
22784 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22785 v = Date.parseDate(value, 'Y-m-d');
22787 if(!v && this.altFormats){
22788 if(!this.altFormatsArray){
22789 this.altFormatsArray = this.altFormats.split("|");
22791 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22792 v = Date.parseDate(value, this.altFormatsArray[i]);
22798 formatDate : function(date, fmt)
22800 return (!date || !(date instanceof Date)) ?
22801 date : date.dateFormat(fmt || this.format);
22804 onFocus : function()
22806 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22810 onBlur : function()
22812 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22814 var d = this.inputEl().getValue();
22821 showPopup : function()
22823 this.picker().show();
22827 this.fireEvent('showpopup', this, this.date);
22830 hidePopup : function()
22832 if(this.isInline) {
22835 this.picker().hide();
22836 this.viewMode = this.startViewMode;
22839 this.fireEvent('hidepopup', this, this.date);
22843 onMousedown: function(e)
22845 e.stopPropagation();
22846 e.preventDefault();
22851 Roo.bootstrap.DateField.superclass.keyup.call(this);
22855 setValue: function(v)
22857 if(this.fireEvent('beforeselect', this, v) !== false){
22858 var d = new Date(this.parseDate(v) ).clearTime();
22860 if(isNaN(d.getTime())){
22861 this.date = this.viewDate = '';
22862 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22866 v = this.formatDate(d);
22868 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22870 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22874 this.fireEvent('select', this, this.date);
22878 getValue: function()
22880 return this.formatDate(this.date);
22883 fireKey: function(e)
22885 if (!this.picker().isVisible()){
22886 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22892 var dateChanged = false,
22894 newDate, newViewDate;
22899 e.preventDefault();
22903 if (!this.keyboardNavigation) {
22906 dir = e.keyCode == 37 ? -1 : 1;
22909 newDate = this.moveYear(this.date, dir);
22910 newViewDate = this.moveYear(this.viewDate, dir);
22911 } else if (e.shiftKey){
22912 newDate = this.moveMonth(this.date, dir);
22913 newViewDate = this.moveMonth(this.viewDate, dir);
22915 newDate = new Date(this.date);
22916 newDate.setUTCDate(this.date.getUTCDate() + dir);
22917 newViewDate = new Date(this.viewDate);
22918 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22920 if (this.dateWithinRange(newDate)){
22921 this.date = newDate;
22922 this.viewDate = newViewDate;
22923 this.setValue(this.formatDate(this.date));
22925 e.preventDefault();
22926 dateChanged = true;
22931 if (!this.keyboardNavigation) {
22934 dir = e.keyCode == 38 ? -1 : 1;
22936 newDate = this.moveYear(this.date, dir);
22937 newViewDate = this.moveYear(this.viewDate, dir);
22938 } else if (e.shiftKey){
22939 newDate = this.moveMonth(this.date, dir);
22940 newViewDate = this.moveMonth(this.viewDate, dir);
22942 newDate = new Date(this.date);
22943 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22944 newViewDate = new Date(this.viewDate);
22945 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22947 if (this.dateWithinRange(newDate)){
22948 this.date = newDate;
22949 this.viewDate = newViewDate;
22950 this.setValue(this.formatDate(this.date));
22952 e.preventDefault();
22953 dateChanged = true;
22957 this.setValue(this.formatDate(this.date));
22959 e.preventDefault();
22962 this.setValue(this.formatDate(this.date));
22976 onClick: function(e)
22978 e.stopPropagation();
22979 e.preventDefault();
22981 var target = e.getTarget();
22983 if(target.nodeName.toLowerCase() === 'i'){
22984 target = Roo.get(target).dom.parentNode;
22987 var nodeName = target.nodeName;
22988 var className = target.className;
22989 var html = target.innerHTML;
22990 //Roo.log(nodeName);
22992 switch(nodeName.toLowerCase()) {
22994 switch(className) {
23000 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23001 switch(this.viewMode){
23003 this.viewDate = this.moveMonth(this.viewDate, dir);
23007 this.viewDate = this.moveYear(this.viewDate, dir);
23013 var date = new Date();
23014 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23016 this.setValue(this.formatDate(this.date));
23023 if (className.indexOf('disabled') < 0) {
23024 if (!this.viewDate) {
23025 this.viewDate = new Date();
23027 this.viewDate.setUTCDate(1);
23028 if (className.indexOf('month') > -1) {
23029 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23031 var year = parseInt(html, 10) || 0;
23032 this.viewDate.setUTCFullYear(year);
23036 if(this.singleMode){
23037 this.setValue(this.formatDate(this.viewDate));
23048 //Roo.log(className);
23049 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23050 var day = parseInt(html, 10) || 1;
23051 var year = (this.viewDate || new Date()).getUTCFullYear(),
23052 month = (this.viewDate || new Date()).getUTCMonth();
23054 if (className.indexOf('old') > -1) {
23061 } else if (className.indexOf('new') > -1) {
23069 //Roo.log([year,month,day]);
23070 this.date = this.UTCDate(year, month, day,0,0,0,0);
23071 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23073 //Roo.log(this.formatDate(this.date));
23074 this.setValue(this.formatDate(this.date));
23081 setStartDate: function(startDate)
23083 this.startDate = startDate || -Infinity;
23084 if (this.startDate !== -Infinity) {
23085 this.startDate = this.parseDate(this.startDate);
23088 this.updateNavArrows();
23091 setEndDate: function(endDate)
23093 this.endDate = endDate || Infinity;
23094 if (this.endDate !== Infinity) {
23095 this.endDate = this.parseDate(this.endDate);
23098 this.updateNavArrows();
23101 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23103 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23104 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23105 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23107 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23108 return parseInt(d, 10);
23111 this.updateNavArrows();
23114 updateNavArrows: function()
23116 if(this.singleMode){
23120 var d = new Date(this.viewDate),
23121 year = d.getUTCFullYear(),
23122 month = d.getUTCMonth();
23124 Roo.each(this.picker().select('.prev', true).elements, function(v){
23126 switch (this.viewMode) {
23129 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23135 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23142 Roo.each(this.picker().select('.next', true).elements, function(v){
23144 switch (this.viewMode) {
23147 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23153 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23161 moveMonth: function(date, dir)
23166 var new_date = new Date(date.valueOf()),
23167 day = new_date.getUTCDate(),
23168 month = new_date.getUTCMonth(),
23169 mag = Math.abs(dir),
23171 dir = dir > 0 ? 1 : -1;
23174 // If going back one month, make sure month is not current month
23175 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23177 return new_date.getUTCMonth() == month;
23179 // If going forward one month, make sure month is as expected
23180 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23182 return new_date.getUTCMonth() != new_month;
23184 new_month = month + dir;
23185 new_date.setUTCMonth(new_month);
23186 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23187 if (new_month < 0 || new_month > 11) {
23188 new_month = (new_month + 12) % 12;
23191 // For magnitudes >1, move one month at a time...
23192 for (var i=0; i<mag; i++) {
23193 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23194 new_date = this.moveMonth(new_date, dir);
23196 // ...then reset the day, keeping it in the new month
23197 new_month = new_date.getUTCMonth();
23198 new_date.setUTCDate(day);
23200 return new_month != new_date.getUTCMonth();
23203 // Common date-resetting loop -- if date is beyond end of month, make it
23206 new_date.setUTCDate(--day);
23207 new_date.setUTCMonth(new_month);
23212 moveYear: function(date, dir)
23214 return this.moveMonth(date, dir*12);
23217 dateWithinRange: function(date)
23219 return date >= this.startDate && date <= this.endDate;
23225 this.picker().remove();
23228 validateValue : function(value)
23230 if(this.getVisibilityEl().hasClass('hidden')){
23234 if(value.length < 1) {
23235 if(this.allowBlank){
23241 if(value.length < this.minLength){
23244 if(value.length > this.maxLength){
23248 var vt = Roo.form.VTypes;
23249 if(!vt[this.vtype](value, this)){
23253 if(typeof this.validator == "function"){
23254 var msg = this.validator(value);
23260 if(this.regex && !this.regex.test(value)){
23264 if(typeof(this.parseDate(value)) == 'undefined'){
23268 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23272 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23282 this.date = this.viewDate = '';
23284 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23289 Roo.apply(Roo.bootstrap.DateField, {
23300 html: '<i class="fa fa-arrow-left"/>'
23310 html: '<i class="fa fa-arrow-right"/>'
23352 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23353 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23354 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23355 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23356 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23369 navFnc: 'FullYear',
23374 navFnc: 'FullYear',
23379 Roo.apply(Roo.bootstrap.DateField, {
23383 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23387 cls: 'datepicker-days',
23391 cls: 'table-condensed',
23393 Roo.bootstrap.DateField.head,
23397 Roo.bootstrap.DateField.footer
23404 cls: 'datepicker-months',
23408 cls: 'table-condensed',
23410 Roo.bootstrap.DateField.head,
23411 Roo.bootstrap.DateField.content,
23412 Roo.bootstrap.DateField.footer
23419 cls: 'datepicker-years',
23423 cls: 'table-condensed',
23425 Roo.bootstrap.DateField.head,
23426 Roo.bootstrap.DateField.content,
23427 Roo.bootstrap.DateField.footer
23446 * @class Roo.bootstrap.TimeField
23447 * @extends Roo.bootstrap.Input
23448 * Bootstrap DateField class
23452 * Create a new TimeField
23453 * @param {Object} config The config object
23456 Roo.bootstrap.TimeField = function(config){
23457 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23461 * Fires when this field show.
23462 * @param {Roo.bootstrap.DateField} thisthis
23463 * @param {Mixed} date The date value
23468 * Fires when this field hide.
23469 * @param {Roo.bootstrap.DateField} this
23470 * @param {Mixed} date The date value
23475 * Fires when select a date.
23476 * @param {Roo.bootstrap.DateField} this
23477 * @param {Mixed} date The date value
23483 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23486 * @cfg {String} format
23487 * The default time format string which can be overriden for localization support. The format must be
23488 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23492 getAutoCreate : function()
23494 this.after = '<i class="fa far fa-clock"></i>';
23495 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23499 onRender: function(ct, position)
23502 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23504 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23506 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23508 this.pop = this.picker().select('>.datepicker-time',true).first();
23509 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23511 this.picker().on('mousedown', this.onMousedown, this);
23512 this.picker().on('click', this.onClick, this);
23514 this.picker().addClass('datepicker-dropdown');
23519 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23520 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23521 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23522 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23523 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23524 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23528 fireKey: function(e){
23529 if (!this.picker().isVisible()){
23530 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23536 e.preventDefault();
23544 this.onTogglePeriod();
23547 this.onIncrementMinutes();
23550 this.onDecrementMinutes();
23559 onClick: function(e) {
23560 e.stopPropagation();
23561 e.preventDefault();
23564 picker : function()
23566 return this.pickerEl;
23569 fillTime: function()
23571 var time = this.pop.select('tbody', true).first();
23573 time.dom.innerHTML = '';
23588 cls: 'hours-up fa fas fa-chevron-up'
23608 cls: 'minutes-up fa fas fa-chevron-up'
23629 cls: 'timepicker-hour',
23644 cls: 'timepicker-minute',
23659 cls: 'btn btn-primary period',
23681 cls: 'hours-down fa fas fa-chevron-down'
23701 cls: 'minutes-down fa fas fa-chevron-down'
23719 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23726 var hours = this.time.getHours();
23727 var minutes = this.time.getMinutes();
23740 hours = hours - 12;
23744 hours = '0' + hours;
23748 minutes = '0' + minutes;
23751 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23752 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23753 this.pop.select('button', true).first().dom.innerHTML = period;
23759 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23761 var cls = ['bottom'];
23763 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23770 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23774 //this.picker().setXY(20000,20000);
23775 this.picker().addClass(cls.join('-'));
23779 Roo.each(cls, function(c){
23784 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23785 //_this.picker().setTop(_this.inputEl().getHeight());
23789 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23791 //_this.picker().setTop(0 - _this.picker().getHeight());
23796 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23800 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23808 onFocus : function()
23810 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23814 onBlur : function()
23816 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23822 this.picker().show();
23827 this.fireEvent('show', this, this.date);
23832 this.picker().hide();
23835 this.fireEvent('hide', this, this.date);
23838 setTime : function()
23841 this.setValue(this.time.format(this.format));
23843 this.fireEvent('select', this, this.date);
23848 onMousedown: function(e){
23849 e.stopPropagation();
23850 e.preventDefault();
23853 onIncrementHours: function()
23855 Roo.log('onIncrementHours');
23856 this.time = this.time.add(Date.HOUR, 1);
23861 onDecrementHours: function()
23863 Roo.log('onDecrementHours');
23864 this.time = this.time.add(Date.HOUR, -1);
23868 onIncrementMinutes: function()
23870 Roo.log('onIncrementMinutes');
23871 this.time = this.time.add(Date.MINUTE, 1);
23875 onDecrementMinutes: function()
23877 Roo.log('onDecrementMinutes');
23878 this.time = this.time.add(Date.MINUTE, -1);
23882 onTogglePeriod: function()
23884 Roo.log('onTogglePeriod');
23885 this.time = this.time.add(Date.HOUR, 12);
23893 Roo.apply(Roo.bootstrap.TimeField, {
23897 cls: 'datepicker dropdown-menu',
23901 cls: 'datepicker-time',
23905 cls: 'table-condensed',
23934 cls: 'btn btn-info ok',
23962 * @class Roo.bootstrap.MonthField
23963 * @extends Roo.bootstrap.Input
23964 * Bootstrap MonthField class
23966 * @cfg {String} language default en
23969 * Create a new MonthField
23970 * @param {Object} config The config object
23973 Roo.bootstrap.MonthField = function(config){
23974 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23979 * Fires when this field show.
23980 * @param {Roo.bootstrap.MonthField} this
23981 * @param {Mixed} date The date value
23986 * Fires when this field hide.
23987 * @param {Roo.bootstrap.MonthField} this
23988 * @param {Mixed} date The date value
23993 * Fires when select a date.
23994 * @param {Roo.bootstrap.MonthField} this
23995 * @param {String} oldvalue The old value
23996 * @param {String} newvalue The new value
24002 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
24004 onRender: function(ct, position)
24007 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24009 this.language = this.language || 'en';
24010 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24011 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24013 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24014 this.isInline = false;
24015 this.isInput = true;
24016 this.component = this.el.select('.add-on', true).first() || false;
24017 this.component = (this.component && this.component.length === 0) ? false : this.component;
24018 this.hasInput = this.component && this.inputEL().length;
24020 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24022 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24024 this.picker().on('mousedown', this.onMousedown, this);
24025 this.picker().on('click', this.onClick, this);
24027 this.picker().addClass('datepicker-dropdown');
24029 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24030 v.setStyle('width', '189px');
24037 if(this.isInline) {
24043 setValue: function(v, suppressEvent)
24045 var o = this.getValue();
24047 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24051 if(suppressEvent !== true){
24052 this.fireEvent('select', this, o, v);
24057 getValue: function()
24062 onClick: function(e)
24064 e.stopPropagation();
24065 e.preventDefault();
24067 var target = e.getTarget();
24069 if(target.nodeName.toLowerCase() === 'i'){
24070 target = Roo.get(target).dom.parentNode;
24073 var nodeName = target.nodeName;
24074 var className = target.className;
24075 var html = target.innerHTML;
24077 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24081 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24083 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24089 picker : function()
24091 return this.pickerEl;
24094 fillMonths: function()
24097 var months = this.picker().select('>.datepicker-months td', true).first();
24099 months.dom.innerHTML = '';
24105 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24108 months.createChild(month);
24117 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24118 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24121 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24122 e.removeClass('active');
24124 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24125 e.addClass('active');
24132 if(this.isInline) {
24136 this.picker().removeClass(['bottom', 'top']);
24138 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24140 * place to the top of element!
24144 this.picker().addClass('top');
24145 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24150 this.picker().addClass('bottom');
24152 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24155 onFocus : function()
24157 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24161 onBlur : function()
24163 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24165 var d = this.inputEl().getValue();
24174 this.picker().show();
24175 this.picker().select('>.datepicker-months', true).first().show();
24179 this.fireEvent('show', this, this.date);
24184 if(this.isInline) {
24187 this.picker().hide();
24188 this.fireEvent('hide', this, this.date);
24192 onMousedown: function(e)
24194 e.stopPropagation();
24195 e.preventDefault();
24200 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24204 fireKey: function(e)
24206 if (!this.picker().isVisible()){
24207 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24218 e.preventDefault();
24222 dir = e.keyCode == 37 ? -1 : 1;
24224 this.vIndex = this.vIndex + dir;
24226 if(this.vIndex < 0){
24230 if(this.vIndex > 11){
24234 if(isNaN(this.vIndex)){
24238 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24244 dir = e.keyCode == 38 ? -1 : 1;
24246 this.vIndex = this.vIndex + dir * 4;
24248 if(this.vIndex < 0){
24252 if(this.vIndex > 11){
24256 if(isNaN(this.vIndex)){
24260 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24265 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24266 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24270 e.preventDefault();
24273 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24274 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24290 this.picker().remove();
24295 Roo.apply(Roo.bootstrap.MonthField, {
24314 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24315 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24320 Roo.apply(Roo.bootstrap.MonthField, {
24324 cls: 'datepicker dropdown-menu roo-dynamic',
24328 cls: 'datepicker-months',
24332 cls: 'table-condensed',
24334 Roo.bootstrap.DateField.content
24354 * @class Roo.bootstrap.CheckBox
24355 * @extends Roo.bootstrap.Input
24356 * Bootstrap CheckBox class
24358 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24359 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24360 * @cfg {String} boxLabel The text that appears beside the checkbox
24361 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24362 * @cfg {Boolean} checked initnal the element
24363 * @cfg {Boolean} inline inline the element (default false)
24364 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24365 * @cfg {String} tooltip label tooltip
24368 * Create a new CheckBox
24369 * @param {Object} config The config object
24372 Roo.bootstrap.CheckBox = function(config){
24373 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24378 * Fires when the element is checked or unchecked.
24379 * @param {Roo.bootstrap.CheckBox} this This input
24380 * @param {Boolean} checked The new checked value
24385 * Fires when the element is click.
24386 * @param {Roo.bootstrap.CheckBox} this This input
24393 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24395 inputType: 'checkbox',
24404 // checkbox success does not make any sense really..
24409 getAutoCreate : function()
24411 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24417 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24420 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24426 type : this.inputType,
24427 value : this.inputValue,
24428 cls : 'roo-' + this.inputType, //'form-box',
24429 placeholder : this.placeholder || ''
24433 if(this.inputType != 'radio'){
24437 cls : 'roo-hidden-value',
24438 value : this.checked ? this.inputValue : this.valueOff
24443 if (this.weight) { // Validity check?
24444 cfg.cls += " " + this.inputType + "-" + this.weight;
24447 if (this.disabled) {
24448 input.disabled=true;
24452 input.checked = this.checked;
24457 input.name = this.name;
24459 if(this.inputType != 'radio'){
24460 hidden.name = this.name;
24461 input.name = '_hidden_' + this.name;
24466 input.cls += ' input-' + this.size;
24471 ['xs','sm','md','lg'].map(function(size){
24472 if (settings[size]) {
24473 cfg.cls += ' col-' + size + '-' + settings[size];
24477 var inputblock = input;
24479 if (this.before || this.after) {
24482 cls : 'input-group',
24487 inputblock.cn.push({
24489 cls : 'input-group-addon',
24494 inputblock.cn.push(input);
24496 if(this.inputType != 'radio'){
24497 inputblock.cn.push(hidden);
24501 inputblock.cn.push({
24503 cls : 'input-group-addon',
24509 var boxLabelCfg = false;
24515 //'for': id, // box label is handled by onclick - so no for...
24517 html: this.boxLabel
24520 boxLabelCfg.tooltip = this.tooltip;
24526 if (align ==='left' && this.fieldLabel.length) {
24527 // Roo.log("left and has label");
24532 cls : 'control-label',
24533 html : this.fieldLabel
24544 cfg.cn[1].cn.push(boxLabelCfg);
24547 if(this.labelWidth > 12){
24548 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24551 if(this.labelWidth < 13 && this.labelmd == 0){
24552 this.labelmd = this.labelWidth;
24555 if(this.labellg > 0){
24556 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24557 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24560 if(this.labelmd > 0){
24561 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24562 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24565 if(this.labelsm > 0){
24566 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24567 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24570 if(this.labelxs > 0){
24571 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24572 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24575 } else if ( this.fieldLabel.length) {
24576 // Roo.log(" label");
24580 tag: this.boxLabel ? 'span' : 'label',
24582 cls: 'control-label box-input-label',
24583 //cls : 'input-group-addon',
24584 html : this.fieldLabel
24591 cfg.cn.push(boxLabelCfg);
24596 // Roo.log(" no label && no align");
24597 cfg.cn = [ inputblock ] ;
24599 cfg.cn.push(boxLabelCfg);
24607 if(this.inputType != 'radio'){
24608 cfg.cn.push(hidden);
24616 * return the real input element.
24618 inputEl: function ()
24620 return this.el.select('input.roo-' + this.inputType,true).first();
24622 hiddenEl: function ()
24624 return this.el.select('input.roo-hidden-value',true).first();
24627 labelEl: function()
24629 return this.el.select('label.control-label',true).first();
24631 /* depricated... */
24635 return this.labelEl();
24638 boxLabelEl: function()
24640 return this.el.select('label.box-label',true).first();
24643 initEvents : function()
24645 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24647 this.inputEl().on('click', this.onClick, this);
24649 if (this.boxLabel) {
24650 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24653 this.startValue = this.getValue();
24656 Roo.bootstrap.CheckBox.register(this);
24660 onClick : function(e)
24662 if(this.fireEvent('click', this, e) !== false){
24663 this.setChecked(!this.checked);
24668 setChecked : function(state,suppressEvent)
24670 this.startValue = this.getValue();
24672 if(this.inputType == 'radio'){
24674 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24675 e.dom.checked = false;
24678 this.inputEl().dom.checked = true;
24680 this.inputEl().dom.value = this.inputValue;
24682 if(suppressEvent !== true){
24683 this.fireEvent('check', this, true);
24691 this.checked = state;
24693 this.inputEl().dom.checked = state;
24696 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24698 if(suppressEvent !== true){
24699 this.fireEvent('check', this, state);
24705 getValue : function()
24707 if(this.inputType == 'radio'){
24708 return this.getGroupValue();
24711 return this.hiddenEl().dom.value;
24715 getGroupValue : function()
24717 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24721 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24724 setValue : function(v,suppressEvent)
24726 if(this.inputType == 'radio'){
24727 this.setGroupValue(v, suppressEvent);
24731 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24736 setGroupValue : function(v, suppressEvent)
24738 this.startValue = this.getValue();
24740 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24741 e.dom.checked = false;
24743 if(e.dom.value == v){
24744 e.dom.checked = true;
24748 if(suppressEvent !== true){
24749 this.fireEvent('check', this, true);
24757 validate : function()
24759 if(this.getVisibilityEl().hasClass('hidden')){
24765 (this.inputType == 'radio' && this.validateRadio()) ||
24766 (this.inputType == 'checkbox' && this.validateCheckbox())
24772 this.markInvalid();
24776 validateRadio : function()
24778 if(this.getVisibilityEl().hasClass('hidden')){
24782 if(this.allowBlank){
24788 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24789 if(!e.dom.checked){
24801 validateCheckbox : function()
24804 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24805 //return (this.getValue() == this.inputValue) ? true : false;
24808 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24816 for(var i in group){
24817 if(group[i].el.isVisible(true)){
24825 for(var i in group){
24830 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24837 * Mark this field as valid
24839 markValid : function()
24843 this.fireEvent('valid', this);
24845 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24848 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24855 if(this.inputType == 'radio'){
24856 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24857 var fg = e.findParent('.form-group', false, true);
24858 if (Roo.bootstrap.version == 3) {
24859 fg.removeClass([_this.invalidClass, _this.validClass]);
24860 fg.addClass(_this.validClass);
24862 fg.removeClass(['is-valid', 'is-invalid']);
24863 fg.addClass('is-valid');
24871 var fg = this.el.findParent('.form-group', false, true);
24872 if (Roo.bootstrap.version == 3) {
24873 fg.removeClass([this.invalidClass, this.validClass]);
24874 fg.addClass(this.validClass);
24876 fg.removeClass(['is-valid', 'is-invalid']);
24877 fg.addClass('is-valid');
24882 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24888 for(var i in group){
24889 var fg = group[i].el.findParent('.form-group', false, true);
24890 if (Roo.bootstrap.version == 3) {
24891 fg.removeClass([this.invalidClass, this.validClass]);
24892 fg.addClass(this.validClass);
24894 fg.removeClass(['is-valid', 'is-invalid']);
24895 fg.addClass('is-valid');
24901 * Mark this field as invalid
24902 * @param {String} msg The validation message
24904 markInvalid : function(msg)
24906 if(this.allowBlank){
24912 this.fireEvent('invalid', this, msg);
24914 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24917 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24921 label.markInvalid();
24924 if(this.inputType == 'radio'){
24926 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24927 var fg = e.findParent('.form-group', false, true);
24928 if (Roo.bootstrap.version == 3) {
24929 fg.removeClass([_this.invalidClass, _this.validClass]);
24930 fg.addClass(_this.invalidClass);
24932 fg.removeClass(['is-invalid', 'is-valid']);
24933 fg.addClass('is-invalid');
24941 var fg = this.el.findParent('.form-group', false, true);
24942 if (Roo.bootstrap.version == 3) {
24943 fg.removeClass([_this.invalidClass, _this.validClass]);
24944 fg.addClass(_this.invalidClass);
24946 fg.removeClass(['is-invalid', 'is-valid']);
24947 fg.addClass('is-invalid');
24952 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24958 for(var i in group){
24959 var fg = group[i].el.findParent('.form-group', false, true);
24960 if (Roo.bootstrap.version == 3) {
24961 fg.removeClass([_this.invalidClass, _this.validClass]);
24962 fg.addClass(_this.invalidClass);
24964 fg.removeClass(['is-invalid', 'is-valid']);
24965 fg.addClass('is-invalid');
24971 clearInvalid : function()
24973 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24975 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24977 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24979 if (label && label.iconEl) {
24980 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24981 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24985 disable : function()
24987 if(this.inputType != 'radio'){
24988 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24995 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24996 _this.getActionEl().addClass(this.disabledClass);
24997 e.dom.disabled = true;
25001 this.disabled = true;
25002 this.fireEvent("disable", this);
25006 enable : function()
25008 if(this.inputType != 'radio'){
25009 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25016 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25017 _this.getActionEl().removeClass(this.disabledClass);
25018 e.dom.disabled = false;
25022 this.disabled = false;
25023 this.fireEvent("enable", this);
25027 setBoxLabel : function(v)
25032 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25038 Roo.apply(Roo.bootstrap.CheckBox, {
25043 * register a CheckBox Group
25044 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25046 register : function(checkbox)
25048 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25049 this.groups[checkbox.groupId] = {};
25052 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25056 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25060 * fetch a CheckBox Group based on the group ID
25061 * @param {string} the group ID
25062 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25064 get: function(groupId) {
25065 if (typeof(this.groups[groupId]) == 'undefined') {
25069 return this.groups[groupId] ;
25082 * @class Roo.bootstrap.Radio
25083 * @extends Roo.bootstrap.Component
25084 * Bootstrap Radio class
25085 * @cfg {String} boxLabel - the label associated
25086 * @cfg {String} value - the value of radio
25089 * Create a new Radio
25090 * @param {Object} config The config object
25092 Roo.bootstrap.Radio = function(config){
25093 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25097 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25103 getAutoCreate : function()
25107 cls : 'form-group radio',
25112 html : this.boxLabel
25120 initEvents : function()
25122 this.parent().register(this);
25124 this.el.on('click', this.onClick, this);
25128 onClick : function(e)
25130 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25131 this.setChecked(true);
25135 setChecked : function(state, suppressEvent)
25137 this.parent().setValue(this.value, suppressEvent);
25141 setBoxLabel : function(v)
25146 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25161 * @class Roo.bootstrap.SecurePass
25162 * @extends Roo.bootstrap.Input
25163 * Bootstrap SecurePass class
25167 * Create a new SecurePass
25168 * @param {Object} config The config object
25171 Roo.bootstrap.SecurePass = function (config) {
25172 // these go here, so the translation tool can replace them..
25174 PwdEmpty: "Please type a password, and then retype it to confirm.",
25175 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25176 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25177 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25178 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25179 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25180 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25181 TooWeak: "Your password is Too Weak."
25183 this.meterLabel = "Password strength:";
25184 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25185 this.meterClass = [
25186 "roo-password-meter-tooweak",
25187 "roo-password-meter-weak",
25188 "roo-password-meter-medium",
25189 "roo-password-meter-strong",
25190 "roo-password-meter-grey"
25195 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25198 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25200 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25202 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25203 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25204 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25205 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25206 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25207 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25208 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25218 * @cfg {String/Object} Label for the strength meter (defaults to
25219 * 'Password strength:')
25224 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25225 * ['Weak', 'Medium', 'Strong'])
25228 pwdStrengths: false,
25241 initEvents: function ()
25243 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25245 if (this.el.is('input[type=password]') && Roo.isSafari) {
25246 this.el.on('keydown', this.SafariOnKeyDown, this);
25249 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25252 onRender: function (ct, position)
25254 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25255 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25256 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25258 this.trigger.createChild({
25263 cls: 'roo-password-meter-grey col-xs-12',
25266 //width: this.meterWidth + 'px'
25270 cls: 'roo-password-meter-text'
25276 if (this.hideTrigger) {
25277 this.trigger.setDisplayed(false);
25279 this.setSize(this.width || '', this.height || '');
25282 onDestroy: function ()
25284 if (this.trigger) {
25285 this.trigger.removeAllListeners();
25286 this.trigger.remove();
25289 this.wrap.remove();
25291 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25294 checkStrength: function ()
25296 var pwd = this.inputEl().getValue();
25297 if (pwd == this._lastPwd) {
25302 if (this.ClientSideStrongPassword(pwd)) {
25304 } else if (this.ClientSideMediumPassword(pwd)) {
25306 } else if (this.ClientSideWeakPassword(pwd)) {
25312 Roo.log('strength1: ' + strength);
25314 //var pm = this.trigger.child('div/div/div').dom;
25315 var pm = this.trigger.child('div/div');
25316 pm.removeClass(this.meterClass);
25317 pm.addClass(this.meterClass[strength]);
25320 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25322 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25324 this._lastPwd = pwd;
25328 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25330 this._lastPwd = '';
25332 var pm = this.trigger.child('div/div');
25333 pm.removeClass(this.meterClass);
25334 pm.addClass('roo-password-meter-grey');
25337 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25340 this.inputEl().dom.type='password';
25343 validateValue: function (value)
25345 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25348 if (value.length == 0) {
25349 if (this.allowBlank) {
25350 this.clearInvalid();
25354 this.markInvalid(this.errors.PwdEmpty);
25355 this.errorMsg = this.errors.PwdEmpty;
25363 if (!value.match(/[\x21-\x7e]+/)) {
25364 this.markInvalid(this.errors.PwdBadChar);
25365 this.errorMsg = this.errors.PwdBadChar;
25368 if (value.length < 6) {
25369 this.markInvalid(this.errors.PwdShort);
25370 this.errorMsg = this.errors.PwdShort;
25373 if (value.length > 16) {
25374 this.markInvalid(this.errors.PwdLong);
25375 this.errorMsg = this.errors.PwdLong;
25379 if (this.ClientSideStrongPassword(value)) {
25381 } else if (this.ClientSideMediumPassword(value)) {
25383 } else if (this.ClientSideWeakPassword(value)) {
25390 if (strength < 2) {
25391 //this.markInvalid(this.errors.TooWeak);
25392 this.errorMsg = this.errors.TooWeak;
25397 console.log('strength2: ' + strength);
25399 //var pm = this.trigger.child('div/div/div').dom;
25401 var pm = this.trigger.child('div/div');
25402 pm.removeClass(this.meterClass);
25403 pm.addClass(this.meterClass[strength]);
25405 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25407 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25409 this.errorMsg = '';
25413 CharacterSetChecks: function (type)
25416 this.fResult = false;
25419 isctype: function (character, type)
25422 case this.kCapitalLetter:
25423 if (character >= 'A' && character <= 'Z') {
25428 case this.kSmallLetter:
25429 if (character >= 'a' && character <= 'z') {
25435 if (character >= '0' && character <= '9') {
25440 case this.kPunctuation:
25441 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25452 IsLongEnough: function (pwd, size)
25454 return !(pwd == null || isNaN(size) || pwd.length < size);
25457 SpansEnoughCharacterSets: function (word, nb)
25459 if (!this.IsLongEnough(word, nb))
25464 var characterSetChecks = new Array(
25465 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25466 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25469 for (var index = 0; index < word.length; ++index) {
25470 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25471 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25472 characterSetChecks[nCharSet].fResult = true;
25479 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25480 if (characterSetChecks[nCharSet].fResult) {
25485 if (nCharSets < nb) {
25491 ClientSideStrongPassword: function (pwd)
25493 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25496 ClientSideMediumPassword: function (pwd)
25498 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25501 ClientSideWeakPassword: function (pwd)
25503 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25506 })//<script type="text/javascript">
25509 * Based Ext JS Library 1.1.1
25510 * Copyright(c) 2006-2007, Ext JS, LLC.
25516 * @class Roo.HtmlEditorCore
25517 * @extends Roo.Component
25518 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25520 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25523 Roo.HtmlEditorCore = function(config){
25526 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25531 * @event initialize
25532 * Fires when the editor is fully initialized (including the iframe)
25533 * @param {Roo.HtmlEditorCore} this
25538 * Fires when the editor is first receives the focus. Any insertion must wait
25539 * until after this event.
25540 * @param {Roo.HtmlEditorCore} this
25544 * @event beforesync
25545 * Fires before the textarea is updated with content from the editor iframe. Return false
25546 * to cancel the sync.
25547 * @param {Roo.HtmlEditorCore} this
25548 * @param {String} html
25552 * @event beforepush
25553 * Fires before the iframe editor is updated with content from the textarea. Return false
25554 * to cancel the push.
25555 * @param {Roo.HtmlEditorCore} this
25556 * @param {String} html
25561 * Fires when the textarea is updated with content from the editor iframe.
25562 * @param {Roo.HtmlEditorCore} this
25563 * @param {String} html
25568 * Fires when the iframe editor is updated with content from the textarea.
25569 * @param {Roo.HtmlEditorCore} this
25570 * @param {String} html
25575 * @event editorevent
25576 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25577 * @param {Roo.HtmlEditorCore} this
25583 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25585 // defaults : white / black...
25586 this.applyBlacklists();
25593 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25597 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25603 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25608 * @cfg {Number} height (in pixels)
25612 * @cfg {Number} width (in pixels)
25617 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25620 stylesheets: false,
25623 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25625 allowComments: false,
25629 // private properties
25630 validationEvent : false,
25632 initialized : false,
25634 sourceEditMode : false,
25635 onFocus : Roo.emptyFn,
25637 hideMode:'offsets',
25641 // blacklist + whitelisted elements..
25648 * Protected method that will not generally be called directly. It
25649 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25650 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25652 getDocMarkup : function(){
25656 // inherit styels from page...??
25657 if (this.stylesheets === false) {
25659 Roo.get(document.head).select('style').each(function(node) {
25660 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25663 Roo.get(document.head).select('link').each(function(node) {
25664 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25667 } else if (!this.stylesheets.length) {
25669 st = '<style type="text/css">' +
25670 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25673 for (var i in this.stylesheets) {
25674 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25679 st += '<style type="text/css">' +
25680 'IMG { cursor: pointer } ' +
25683 var cls = 'roo-htmleditor-body';
25685 if(this.bodyCls.length){
25686 cls += ' ' + this.bodyCls;
25689 return '<html><head>' + st +
25690 //<style type="text/css">' +
25691 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25693 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25697 onRender : function(ct, position)
25700 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25701 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25704 this.el.dom.style.border = '0 none';
25705 this.el.dom.setAttribute('tabIndex', -1);
25706 this.el.addClass('x-hidden hide');
25710 if(Roo.isIE){ // fix IE 1px bogus margin
25711 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25715 this.frameId = Roo.id();
25719 var iframe = this.owner.wrap.createChild({
25721 cls: 'form-control', // bootstrap..
25723 name: this.frameId,
25724 frameBorder : 'no',
25725 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25730 this.iframe = iframe.dom;
25732 this.assignDocWin();
25734 this.doc.designMode = 'on';
25737 this.doc.write(this.getDocMarkup());
25741 var task = { // must defer to wait for browser to be ready
25743 //console.log("run task?" + this.doc.readyState);
25744 this.assignDocWin();
25745 if(this.doc.body || this.doc.readyState == 'complete'){
25747 this.doc.designMode="on";
25751 Roo.TaskMgr.stop(task);
25752 this.initEditor.defer(10, this);
25759 Roo.TaskMgr.start(task);
25764 onResize : function(w, h)
25766 Roo.log('resize: ' +w + ',' + h );
25767 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25771 if(typeof w == 'number'){
25773 this.iframe.style.width = w + 'px';
25775 if(typeof h == 'number'){
25777 this.iframe.style.height = h + 'px';
25779 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25786 * Toggles the editor between standard and source edit mode.
25787 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25789 toggleSourceEdit : function(sourceEditMode){
25791 this.sourceEditMode = sourceEditMode === true;
25793 if(this.sourceEditMode){
25795 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25798 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25799 //this.iframe.className = '';
25802 //this.setSize(this.owner.wrap.getSize());
25803 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25810 * Protected method that will not generally be called directly. If you need/want
25811 * custom HTML cleanup, this is the method you should override.
25812 * @param {String} html The HTML to be cleaned
25813 * return {String} The cleaned HTML
25815 cleanHtml : function(html){
25816 html = String(html);
25817 if(html.length > 5){
25818 if(Roo.isSafari){ // strip safari nonsense
25819 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25822 if(html == ' '){
25829 * HTML Editor -> Textarea
25830 * Protected method that will not generally be called directly. Syncs the contents
25831 * of the editor iframe with the textarea.
25833 syncValue : function(){
25834 if(this.initialized){
25835 var bd = (this.doc.body || this.doc.documentElement);
25836 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25837 var html = bd.innerHTML;
25839 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25840 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25842 html = '<div style="'+m[0]+'">' + html + '</div>';
25845 html = this.cleanHtml(html);
25846 // fix up the special chars.. normaly like back quotes in word...
25847 // however we do not want to do this with chinese..
25848 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25850 var cc = match.charCodeAt();
25852 // Get the character value, handling surrogate pairs
25853 if (match.length == 2) {
25854 // It's a surrogate pair, calculate the Unicode code point
25855 var high = match.charCodeAt(0) - 0xD800;
25856 var low = match.charCodeAt(1) - 0xDC00;
25857 cc = (high * 0x400) + low + 0x10000;
25859 (cc >= 0x4E00 && cc < 0xA000 ) ||
25860 (cc >= 0x3400 && cc < 0x4E00 ) ||
25861 (cc >= 0xf900 && cc < 0xfb00 )
25866 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25867 return "&#" + cc + ";";
25874 if(this.owner.fireEvent('beforesync', this, html) !== false){
25875 this.el.dom.value = html;
25876 this.owner.fireEvent('sync', this, html);
25882 * Protected method that will not generally be called directly. Pushes the value of the textarea
25883 * into the iframe editor.
25885 pushValue : function(){
25886 if(this.initialized){
25887 var v = this.el.dom.value.trim();
25889 // if(v.length < 1){
25893 if(this.owner.fireEvent('beforepush', this, v) !== false){
25894 var d = (this.doc.body || this.doc.documentElement);
25896 this.cleanUpPaste();
25897 this.el.dom.value = d.innerHTML;
25898 this.owner.fireEvent('push', this, v);
25904 deferFocus : function(){
25905 this.focus.defer(10, this);
25909 focus : function(){
25910 if(this.win && !this.sourceEditMode){
25917 assignDocWin: function()
25919 var iframe = this.iframe;
25922 this.doc = iframe.contentWindow.document;
25923 this.win = iframe.contentWindow;
25925 // if (!Roo.get(this.frameId)) {
25928 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25929 // this.win = Roo.get(this.frameId).dom.contentWindow;
25931 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25935 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25936 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25941 initEditor : function(){
25942 //console.log("INIT EDITOR");
25943 this.assignDocWin();
25947 this.doc.designMode="on";
25949 this.doc.write(this.getDocMarkup());
25952 var dbody = (this.doc.body || this.doc.documentElement);
25953 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25954 // this copies styles from the containing element into thsi one..
25955 // not sure why we need all of this..
25956 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25958 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25959 //ss['background-attachment'] = 'fixed'; // w3c
25960 dbody.bgProperties = 'fixed'; // ie
25961 //Roo.DomHelper.applyStyles(dbody, ss);
25962 Roo.EventManager.on(this.doc, {
25963 //'mousedown': this.onEditorEvent,
25964 'mouseup': this.onEditorEvent,
25965 'dblclick': this.onEditorEvent,
25966 'click': this.onEditorEvent,
25967 'keyup': this.onEditorEvent,
25972 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25974 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25975 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25977 this.initialized = true;
25979 this.owner.fireEvent('initialize', this);
25984 onDestroy : function(){
25990 //for (var i =0; i < this.toolbars.length;i++) {
25991 // // fixme - ask toolbars for heights?
25992 // this.toolbars[i].onDestroy();
25995 //this.wrap.dom.innerHTML = '';
25996 //this.wrap.remove();
26001 onFirstFocus : function(){
26003 this.assignDocWin();
26006 this.activated = true;
26009 if(Roo.isGecko){ // prevent silly gecko errors
26011 var s = this.win.getSelection();
26012 if(!s.focusNode || s.focusNode.nodeType != 3){
26013 var r = s.getRangeAt(0);
26014 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26019 this.execCmd('useCSS', true);
26020 this.execCmd('styleWithCSS', false);
26023 this.owner.fireEvent('activate', this);
26027 adjustFont: function(btn){
26028 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26029 //if(Roo.isSafari){ // safari
26032 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26033 if(Roo.isSafari){ // safari
26034 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26035 v = (v < 10) ? 10 : v;
26036 v = (v > 48) ? 48 : v;
26037 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26042 v = Math.max(1, v+adjust);
26044 this.execCmd('FontSize', v );
26047 onEditorEvent : function(e)
26049 this.owner.fireEvent('editorevent', this, e);
26050 // this.updateToolbar();
26051 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26054 insertTag : function(tg)
26056 // could be a bit smarter... -> wrap the current selected tRoo..
26057 if (tg.toLowerCase() == 'span' ||
26058 tg.toLowerCase() == 'code' ||
26059 tg.toLowerCase() == 'sup' ||
26060 tg.toLowerCase() == 'sub'
26063 range = this.createRange(this.getSelection());
26064 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26065 wrappingNode.appendChild(range.extractContents());
26066 range.insertNode(wrappingNode);
26073 this.execCmd("formatblock", tg);
26077 insertText : function(txt)
26081 var range = this.createRange();
26082 range.deleteContents();
26083 //alert(Sender.getAttribute('label'));
26085 range.insertNode(this.doc.createTextNode(txt));
26091 * Executes a Midas editor command on the editor document and performs necessary focus and
26092 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26093 * @param {String} cmd The Midas command
26094 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26096 relayCmd : function(cmd, value){
26098 this.execCmd(cmd, value);
26099 this.owner.fireEvent('editorevent', this);
26100 //this.updateToolbar();
26101 this.owner.deferFocus();
26105 * Executes a Midas editor command directly on the editor document.
26106 * For visual commands, you should use {@link #relayCmd} instead.
26107 * <b>This should only be called after the editor is initialized.</b>
26108 * @param {String} cmd The Midas command
26109 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26111 execCmd : function(cmd, value){
26112 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26119 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26121 * @param {String} text | dom node..
26123 insertAtCursor : function(text)
26126 if(!this.activated){
26132 var r = this.doc.selection.createRange();
26143 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26147 // from jquery ui (MIT licenced)
26149 var win = this.win;
26151 if (win.getSelection && win.getSelection().getRangeAt) {
26152 range = win.getSelection().getRangeAt(0);
26153 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26154 range.insertNode(node);
26155 } else if (win.document.selection && win.document.selection.createRange) {
26156 // no firefox support
26157 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26158 win.document.selection.createRange().pasteHTML(txt);
26160 // no firefox support
26161 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26162 this.execCmd('InsertHTML', txt);
26171 mozKeyPress : function(e){
26173 var c = e.getCharCode(), cmd;
26176 c = String.fromCharCode(c).toLowerCase();
26190 this.cleanUpPaste.defer(100, this);
26198 e.preventDefault();
26206 fixKeys : function(){ // load time branching for fastest keydown performance
26208 return function(e){
26209 var k = e.getKey(), r;
26212 r = this.doc.selection.createRange();
26215 r.pasteHTML('    ');
26222 r = this.doc.selection.createRange();
26224 var target = r.parentElement();
26225 if(!target || target.tagName.toLowerCase() != 'li'){
26227 r.pasteHTML('<br />');
26233 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26234 this.cleanUpPaste.defer(100, this);
26240 }else if(Roo.isOpera){
26241 return function(e){
26242 var k = e.getKey();
26246 this.execCmd('InsertHTML','    ');
26249 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26250 this.cleanUpPaste.defer(100, this);
26255 }else if(Roo.isSafari){
26256 return function(e){
26257 var k = e.getKey();
26261 this.execCmd('InsertText','\t');
26265 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26266 this.cleanUpPaste.defer(100, this);
26274 getAllAncestors: function()
26276 var p = this.getSelectedNode();
26279 a.push(p); // push blank onto stack..
26280 p = this.getParentElement();
26284 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26288 a.push(this.doc.body);
26292 lastSelNode : false,
26295 getSelection : function()
26297 this.assignDocWin();
26298 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26301 getSelectedNode: function()
26303 // this may only work on Gecko!!!
26305 // should we cache this!!!!
26310 var range = this.createRange(this.getSelection()).cloneRange();
26313 var parent = range.parentElement();
26315 var testRange = range.duplicate();
26316 testRange.moveToElementText(parent);
26317 if (testRange.inRange(range)) {
26320 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26323 parent = parent.parentElement;
26328 // is ancestor a text element.
26329 var ac = range.commonAncestorContainer;
26330 if (ac.nodeType == 3) {
26331 ac = ac.parentNode;
26334 var ar = ac.childNodes;
26337 var other_nodes = [];
26338 var has_other_nodes = false;
26339 for (var i=0;i<ar.length;i++) {
26340 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26343 // fullly contained node.
26345 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26350 // probably selected..
26351 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26352 other_nodes.push(ar[i]);
26356 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26361 has_other_nodes = true;
26363 if (!nodes.length && other_nodes.length) {
26364 nodes= other_nodes;
26366 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26372 createRange: function(sel)
26374 // this has strange effects when using with
26375 // top toolbar - not sure if it's a great idea.
26376 //this.editor.contentWindow.focus();
26377 if (typeof sel != "undefined") {
26379 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26381 return this.doc.createRange();
26384 return this.doc.createRange();
26387 getParentElement: function()
26390 this.assignDocWin();
26391 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26393 var range = this.createRange(sel);
26396 var p = range.commonAncestorContainer;
26397 while (p.nodeType == 3) { // text node
26408 * Range intersection.. the hard stuff...
26412 * [ -- selected range --- ]
26416 * if end is before start or hits it. fail.
26417 * if start is after end or hits it fail.
26419 * if either hits (but other is outside. - then it's not
26425 // @see http://www.thismuchiknow.co.uk/?p=64.
26426 rangeIntersectsNode : function(range, node)
26428 var nodeRange = node.ownerDocument.createRange();
26430 nodeRange.selectNode(node);
26432 nodeRange.selectNodeContents(node);
26435 var rangeStartRange = range.cloneRange();
26436 rangeStartRange.collapse(true);
26438 var rangeEndRange = range.cloneRange();
26439 rangeEndRange.collapse(false);
26441 var nodeStartRange = nodeRange.cloneRange();
26442 nodeStartRange.collapse(true);
26444 var nodeEndRange = nodeRange.cloneRange();
26445 nodeEndRange.collapse(false);
26447 return rangeStartRange.compareBoundaryPoints(
26448 Range.START_TO_START, nodeEndRange) == -1 &&
26449 rangeEndRange.compareBoundaryPoints(
26450 Range.START_TO_START, nodeStartRange) == 1;
26454 rangeCompareNode : function(range, node)
26456 var nodeRange = node.ownerDocument.createRange();
26458 nodeRange.selectNode(node);
26460 nodeRange.selectNodeContents(node);
26464 range.collapse(true);
26466 nodeRange.collapse(true);
26468 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26469 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26471 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26473 var nodeIsBefore = ss == 1;
26474 var nodeIsAfter = ee == -1;
26476 if (nodeIsBefore && nodeIsAfter) {
26479 if (!nodeIsBefore && nodeIsAfter) {
26480 return 1; //right trailed.
26483 if (nodeIsBefore && !nodeIsAfter) {
26484 return 2; // left trailed.
26490 // private? - in a new class?
26491 cleanUpPaste : function()
26493 // cleans up the whole document..
26494 Roo.log('cleanuppaste');
26496 this.cleanUpChildren(this.doc.body);
26497 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26498 if (clean != this.doc.body.innerHTML) {
26499 this.doc.body.innerHTML = clean;
26504 cleanWordChars : function(input) {// change the chars to hex code
26505 var he = Roo.HtmlEditorCore;
26507 var output = input;
26508 Roo.each(he.swapCodes, function(sw) {
26509 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26511 output = output.replace(swapper, sw[1]);
26518 cleanUpChildren : function (n)
26520 if (!n.childNodes.length) {
26523 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26524 this.cleanUpChild(n.childNodes[i]);
26531 cleanUpChild : function (node)
26534 //console.log(node);
26535 if (node.nodeName == "#text") {
26536 // clean up silly Windows -- stuff?
26539 if (node.nodeName == "#comment") {
26540 if (!this.allowComments) {
26541 node.parentNode.removeChild(node);
26543 // clean up silly Windows -- stuff?
26546 var lcname = node.tagName.toLowerCase();
26547 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26548 // whitelist of tags..
26550 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26552 node.parentNode.removeChild(node);
26557 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26559 // spans with no attributes - just remove them..
26560 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26561 remove_keep_children = true;
26564 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26565 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26567 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26568 // remove_keep_children = true;
26571 if (remove_keep_children) {
26572 this.cleanUpChildren(node);
26573 // inserts everything just before this node...
26574 while (node.childNodes.length) {
26575 var cn = node.childNodes[0];
26576 node.removeChild(cn);
26577 node.parentNode.insertBefore(cn, node);
26579 node.parentNode.removeChild(node);
26583 if (!node.attributes || !node.attributes.length) {
26588 this.cleanUpChildren(node);
26592 function cleanAttr(n,v)
26595 if (v.match(/^\./) || v.match(/^\//)) {
26598 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26601 if (v.match(/^#/)) {
26604 if (v.match(/^\{/)) { // allow template editing.
26607 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26608 node.removeAttribute(n);
26612 var cwhite = this.cwhite;
26613 var cblack = this.cblack;
26615 function cleanStyle(n,v)
26617 if (v.match(/expression/)) { //XSS?? should we even bother..
26618 node.removeAttribute(n);
26622 var parts = v.split(/;/);
26625 Roo.each(parts, function(p) {
26626 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26630 var l = p.split(':').shift().replace(/\s+/g,'');
26631 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26633 if ( cwhite.length && cblack.indexOf(l) > -1) {
26634 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26635 //node.removeAttribute(n);
26639 // only allow 'c whitelisted system attributes'
26640 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26641 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26642 //node.removeAttribute(n);
26652 if (clean.length) {
26653 node.setAttribute(n, clean.join(';'));
26655 node.removeAttribute(n);
26661 for (var i = node.attributes.length-1; i > -1 ; i--) {
26662 var a = node.attributes[i];
26665 if (a.name.toLowerCase().substr(0,2)=='on') {
26666 node.removeAttribute(a.name);
26669 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26670 node.removeAttribute(a.name);
26673 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26674 cleanAttr(a.name,a.value); // fixme..
26677 if (a.name == 'style') {
26678 cleanStyle(a.name,a.value);
26681 /// clean up MS crap..
26682 // tecnically this should be a list of valid class'es..
26685 if (a.name == 'class') {
26686 if (a.value.match(/^Mso/)) {
26687 node.removeAttribute('class');
26690 if (a.value.match(/^body$/)) {
26691 node.removeAttribute('class');
26702 this.cleanUpChildren(node);
26708 * Clean up MS wordisms...
26710 cleanWord : function(node)
26713 this.cleanWord(this.doc.body);
26718 node.nodeName == 'SPAN' &&
26719 !node.hasAttributes() &&
26720 node.childNodes.length == 1 &&
26721 node.firstChild.nodeName == "#text"
26723 var textNode = node.firstChild;
26724 node.removeChild(textNode);
26725 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26726 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26728 node.parentNode.insertBefore(textNode, node);
26729 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26730 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26732 node.parentNode.removeChild(node);
26735 if (node.nodeName == "#text") {
26736 // clean up silly Windows -- stuff?
26739 if (node.nodeName == "#comment") {
26740 node.parentNode.removeChild(node);
26741 // clean up silly Windows -- stuff?
26745 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26746 node.parentNode.removeChild(node);
26749 //Roo.log(node.tagName);
26750 // remove - but keep children..
26751 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26752 //Roo.log('-- removed');
26753 while (node.childNodes.length) {
26754 var cn = node.childNodes[0];
26755 node.removeChild(cn);
26756 node.parentNode.insertBefore(cn, node);
26757 // move node to parent - and clean it..
26758 this.cleanWord(cn);
26760 node.parentNode.removeChild(node);
26761 /// no need to iterate chidlren = it's got none..
26762 //this.iterateChildren(node, this.cleanWord);
26766 if (node.className.length) {
26768 var cn = node.className.split(/\W+/);
26770 Roo.each(cn, function(cls) {
26771 if (cls.match(/Mso[a-zA-Z]+/)) {
26776 node.className = cna.length ? cna.join(' ') : '';
26778 node.removeAttribute("class");
26782 if (node.hasAttribute("lang")) {
26783 node.removeAttribute("lang");
26786 if (node.hasAttribute("style")) {
26788 var styles = node.getAttribute("style").split(";");
26790 Roo.each(styles, function(s) {
26791 if (!s.match(/:/)) {
26794 var kv = s.split(":");
26795 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26798 // what ever is left... we allow.
26801 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26802 if (!nstyle.length) {
26803 node.removeAttribute('style');
26806 this.iterateChildren(node, this.cleanWord);
26812 * iterateChildren of a Node, calling fn each time, using this as the scole..
26813 * @param {DomNode} node node to iterate children of.
26814 * @param {Function} fn method of this class to call on each item.
26816 iterateChildren : function(node, fn)
26818 if (!node.childNodes.length) {
26821 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26822 fn.call(this, node.childNodes[i])
26828 * cleanTableWidths.
26830 * Quite often pasting from word etc.. results in tables with column and widths.
26831 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26834 cleanTableWidths : function(node)
26839 this.cleanTableWidths(this.doc.body);
26844 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26847 Roo.log(node.tagName);
26848 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26849 this.iterateChildren(node, this.cleanTableWidths);
26852 if (node.hasAttribute('width')) {
26853 node.removeAttribute('width');
26857 if (node.hasAttribute("style")) {
26860 var styles = node.getAttribute("style").split(";");
26862 Roo.each(styles, function(s) {
26863 if (!s.match(/:/)) {
26866 var kv = s.split(":");
26867 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26870 // what ever is left... we allow.
26873 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26874 if (!nstyle.length) {
26875 node.removeAttribute('style');
26879 this.iterateChildren(node, this.cleanTableWidths);
26887 domToHTML : function(currentElement, depth, nopadtext) {
26889 depth = depth || 0;
26890 nopadtext = nopadtext || false;
26892 if (!currentElement) {
26893 return this.domToHTML(this.doc.body);
26896 //Roo.log(currentElement);
26898 var allText = false;
26899 var nodeName = currentElement.nodeName;
26900 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26902 if (nodeName == '#text') {
26904 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26909 if (nodeName != 'BODY') {
26912 // Prints the node tagName, such as <A>, <IMG>, etc
26915 for(i = 0; i < currentElement.attributes.length;i++) {
26917 var aname = currentElement.attributes.item(i).name;
26918 if (!currentElement.attributes.item(i).value.length) {
26921 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26924 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26933 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26936 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26941 // Traverse the tree
26943 var currentElementChild = currentElement.childNodes.item(i);
26944 var allText = true;
26945 var innerHTML = '';
26947 while (currentElementChild) {
26948 // Formatting code (indent the tree so it looks nice on the screen)
26949 var nopad = nopadtext;
26950 if (lastnode == 'SPAN') {
26954 if (currentElementChild.nodeName == '#text') {
26955 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26956 toadd = nopadtext ? toadd : toadd.trim();
26957 if (!nopad && toadd.length > 80) {
26958 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26960 innerHTML += toadd;
26963 currentElementChild = currentElement.childNodes.item(i);
26969 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26971 // Recursively traverse the tree structure of the child node
26972 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26973 lastnode = currentElementChild.nodeName;
26975 currentElementChild=currentElement.childNodes.item(i);
26981 // The remaining code is mostly for formatting the tree
26982 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26987 ret+= "</"+tagName+">";
26993 applyBlacklists : function()
26995 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26996 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27000 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27001 if (b.indexOf(tag) > -1) {
27004 this.white.push(tag);
27008 Roo.each(w, function(tag) {
27009 if (b.indexOf(tag) > -1) {
27012 if (this.white.indexOf(tag) > -1) {
27015 this.white.push(tag);
27020 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27021 if (w.indexOf(tag) > -1) {
27024 this.black.push(tag);
27028 Roo.each(b, function(tag) {
27029 if (w.indexOf(tag) > -1) {
27032 if (this.black.indexOf(tag) > -1) {
27035 this.black.push(tag);
27040 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27041 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27045 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27046 if (b.indexOf(tag) > -1) {
27049 this.cwhite.push(tag);
27053 Roo.each(w, function(tag) {
27054 if (b.indexOf(tag) > -1) {
27057 if (this.cwhite.indexOf(tag) > -1) {
27060 this.cwhite.push(tag);
27065 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27066 if (w.indexOf(tag) > -1) {
27069 this.cblack.push(tag);
27073 Roo.each(b, function(tag) {
27074 if (w.indexOf(tag) > -1) {
27077 if (this.cblack.indexOf(tag) > -1) {
27080 this.cblack.push(tag);
27085 setStylesheets : function(stylesheets)
27087 if(typeof(stylesheets) == 'string'){
27088 Roo.get(this.iframe.contentDocument.head).createChild({
27090 rel : 'stylesheet',
27099 Roo.each(stylesheets, function(s) {
27104 Roo.get(_this.iframe.contentDocument.head).createChild({
27106 rel : 'stylesheet',
27115 removeStylesheets : function()
27119 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27124 setStyle : function(style)
27126 Roo.get(this.iframe.contentDocument.head).createChild({
27135 // hide stuff that is not compatible
27149 * @event specialkey
27153 * @cfg {String} fieldClass @hide
27156 * @cfg {String} focusClass @hide
27159 * @cfg {String} autoCreate @hide
27162 * @cfg {String} inputType @hide
27165 * @cfg {String} invalidClass @hide
27168 * @cfg {String} invalidText @hide
27171 * @cfg {String} msgFx @hide
27174 * @cfg {String} validateOnBlur @hide
27178 Roo.HtmlEditorCore.white = [
27179 'area', 'br', 'img', 'input', 'hr', 'wbr',
27181 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27182 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27183 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27184 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27185 'table', 'ul', 'xmp',
27187 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27190 'dir', 'menu', 'ol', 'ul', 'dl',
27196 Roo.HtmlEditorCore.black = [
27197 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27199 'base', 'basefont', 'bgsound', 'blink', 'body',
27200 'frame', 'frameset', 'head', 'html', 'ilayer',
27201 'iframe', 'layer', 'link', 'meta', 'object',
27202 'script', 'style' ,'title', 'xml' // clean later..
27204 Roo.HtmlEditorCore.clean = [
27205 'script', 'style', 'title', 'xml'
27207 Roo.HtmlEditorCore.remove = [
27212 Roo.HtmlEditorCore.ablack = [
27216 Roo.HtmlEditorCore.aclean = [
27217 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27221 Roo.HtmlEditorCore.pwhite= [
27222 'http', 'https', 'mailto'
27225 // white listed style attributes.
27226 Roo.HtmlEditorCore.cwhite= [
27227 // 'text-align', /// default is to allow most things..
27233 // black listed style attributes.
27234 Roo.HtmlEditorCore.cblack= [
27235 // 'font-size' -- this can be set by the project
27239 Roo.HtmlEditorCore.swapCodes =[
27240 [ 8211, "–" ],
27241 [ 8212, "—" ],
27258 * @class Roo.bootstrap.HtmlEditor
27259 * @extends Roo.bootstrap.TextArea
27260 * Bootstrap HtmlEditor class
27263 * Create a new HtmlEditor
27264 * @param {Object} config The config object
27267 Roo.bootstrap.HtmlEditor = function(config){
27268 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27269 if (!this.toolbars) {
27270 this.toolbars = [];
27273 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27276 * @event initialize
27277 * Fires when the editor is fully initialized (including the iframe)
27278 * @param {HtmlEditor} this
27283 * Fires when the editor is first receives the focus. Any insertion must wait
27284 * until after this event.
27285 * @param {HtmlEditor} this
27289 * @event beforesync
27290 * Fires before the textarea is updated with content from the editor iframe. Return false
27291 * to cancel the sync.
27292 * @param {HtmlEditor} this
27293 * @param {String} html
27297 * @event beforepush
27298 * Fires before the iframe editor is updated with content from the textarea. Return false
27299 * to cancel the push.
27300 * @param {HtmlEditor} this
27301 * @param {String} html
27306 * Fires when the textarea is updated with content from the editor iframe.
27307 * @param {HtmlEditor} this
27308 * @param {String} html
27313 * Fires when the iframe editor is updated with content from the textarea.
27314 * @param {HtmlEditor} this
27315 * @param {String} html
27319 * @event editmodechange
27320 * Fires when the editor switches edit modes
27321 * @param {HtmlEditor} this
27322 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27324 editmodechange: true,
27326 * @event editorevent
27327 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27328 * @param {HtmlEditor} this
27332 * @event firstfocus
27333 * Fires when on first focus - needed by toolbars..
27334 * @param {HtmlEditor} this
27339 * Auto save the htmlEditor value as a file into Events
27340 * @param {HtmlEditor} this
27344 * @event savedpreview
27345 * preview the saved version of htmlEditor
27346 * @param {HtmlEditor} this
27353 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27357 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27362 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27367 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27372 * @cfg {Number} height (in pixels)
27376 * @cfg {Number} width (in pixels)
27381 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27384 stylesheets: false,
27389 // private properties
27390 validationEvent : false,
27392 initialized : false,
27395 onFocus : Roo.emptyFn,
27397 hideMode:'offsets',
27399 tbContainer : false,
27403 toolbarContainer :function() {
27404 return this.wrap.select('.x-html-editor-tb',true).first();
27408 * Protected method that will not generally be called directly. It
27409 * is called when the editor creates its toolbar. Override this method if you need to
27410 * add custom toolbar buttons.
27411 * @param {HtmlEditor} editor
27413 createToolbar : function(){
27414 Roo.log('renewing');
27415 Roo.log("create toolbars");
27417 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27418 this.toolbars[0].render(this.toolbarContainer());
27422 // if (!editor.toolbars || !editor.toolbars.length) {
27423 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27426 // for (var i =0 ; i < editor.toolbars.length;i++) {
27427 // editor.toolbars[i] = Roo.factory(
27428 // typeof(editor.toolbars[i]) == 'string' ?
27429 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27430 // Roo.bootstrap.HtmlEditor);
27431 // editor.toolbars[i].init(editor);
27437 onRender : function(ct, position)
27439 // Roo.log("Call onRender: " + this.xtype);
27441 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27443 this.wrap = this.inputEl().wrap({
27444 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27447 this.editorcore.onRender(ct, position);
27449 if (this.resizable) {
27450 this.resizeEl = new Roo.Resizable(this.wrap, {
27454 minHeight : this.height,
27455 height: this.height,
27456 handles : this.resizable,
27459 resize : function(r, w, h) {
27460 _t.onResize(w,h); // -something
27466 this.createToolbar(this);
27469 if(!this.width && this.resizable){
27470 this.setSize(this.wrap.getSize());
27472 if (this.resizeEl) {
27473 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27474 // should trigger onReize..
27480 onResize : function(w, h)
27482 Roo.log('resize: ' +w + ',' + h );
27483 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27487 if(this.inputEl() ){
27488 if(typeof w == 'number'){
27489 var aw = w - this.wrap.getFrameWidth('lr');
27490 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27493 if(typeof h == 'number'){
27494 var tbh = -11; // fixme it needs to tool bar size!
27495 for (var i =0; i < this.toolbars.length;i++) {
27496 // fixme - ask toolbars for heights?
27497 tbh += this.toolbars[i].el.getHeight();
27498 //if (this.toolbars[i].footer) {
27499 // tbh += this.toolbars[i].footer.el.getHeight();
27507 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27508 ah -= 5; // knock a few pixes off for look..
27509 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27513 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27514 this.editorcore.onResize(ew,eh);
27519 * Toggles the editor between standard and source edit mode.
27520 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27522 toggleSourceEdit : function(sourceEditMode)
27524 this.editorcore.toggleSourceEdit(sourceEditMode);
27526 if(this.editorcore.sourceEditMode){
27527 Roo.log('editor - showing textarea');
27530 // Roo.log(this.syncValue());
27532 this.inputEl().removeClass(['hide', 'x-hidden']);
27533 this.inputEl().dom.removeAttribute('tabIndex');
27534 this.inputEl().focus();
27536 Roo.log('editor - hiding textarea');
27538 // Roo.log(this.pushValue());
27541 this.inputEl().addClass(['hide', 'x-hidden']);
27542 this.inputEl().dom.setAttribute('tabIndex', -1);
27543 //this.deferFocus();
27546 if(this.resizable){
27547 this.setSize(this.wrap.getSize());
27550 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27553 // private (for BoxComponent)
27554 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27556 // private (for BoxComponent)
27557 getResizeEl : function(){
27561 // private (for BoxComponent)
27562 getPositionEl : function(){
27567 initEvents : function(){
27568 this.originalValue = this.getValue();
27572 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27575 // markInvalid : Roo.emptyFn,
27577 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27580 // clearInvalid : Roo.emptyFn,
27582 setValue : function(v){
27583 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27584 this.editorcore.pushValue();
27589 deferFocus : function(){
27590 this.focus.defer(10, this);
27594 focus : function(){
27595 this.editorcore.focus();
27601 onDestroy : function(){
27607 for (var i =0; i < this.toolbars.length;i++) {
27608 // fixme - ask toolbars for heights?
27609 this.toolbars[i].onDestroy();
27612 this.wrap.dom.innerHTML = '';
27613 this.wrap.remove();
27618 onFirstFocus : function(){
27619 //Roo.log("onFirstFocus");
27620 this.editorcore.onFirstFocus();
27621 for (var i =0; i < this.toolbars.length;i++) {
27622 this.toolbars[i].onFirstFocus();
27628 syncValue : function()
27630 this.editorcore.syncValue();
27633 pushValue : function()
27635 this.editorcore.pushValue();
27639 // hide stuff that is not compatible
27653 * @event specialkey
27657 * @cfg {String} fieldClass @hide
27660 * @cfg {String} focusClass @hide
27663 * @cfg {String} autoCreate @hide
27666 * @cfg {String} inputType @hide
27670 * @cfg {String} invalidText @hide
27673 * @cfg {String} msgFx @hide
27676 * @cfg {String} validateOnBlur @hide
27685 Roo.namespace('Roo.bootstrap.htmleditor');
27687 * @class Roo.bootstrap.HtmlEditorToolbar1
27693 new Roo.bootstrap.HtmlEditor({
27696 new Roo.bootstrap.HtmlEditorToolbar1({
27697 disable : { fonts: 1 , format: 1, ..., ... , ...],
27703 * @cfg {Object} disable List of elements to disable..
27704 * @cfg {Array} btns List of additional buttons.
27708 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27711 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27714 Roo.apply(this, config);
27716 // default disabled, based on 'good practice'..
27717 this.disable = this.disable || {};
27718 Roo.applyIf(this.disable, {
27721 specialElements : true
27723 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27725 this.editor = config.editor;
27726 this.editorcore = config.editor.editorcore;
27728 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27730 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27731 // dont call parent... till later.
27733 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27738 editorcore : false,
27743 "h1","h2","h3","h4","h5","h6",
27745 "abbr", "acronym", "address", "cite", "samp", "var",
27749 onRender : function(ct, position)
27751 // Roo.log("Call onRender: " + this.xtype);
27753 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27755 this.el.dom.style.marginBottom = '0';
27757 var editorcore = this.editorcore;
27758 var editor= this.editor;
27761 var btn = function(id,cmd , toggle, handler, html){
27763 var event = toggle ? 'toggle' : 'click';
27768 xns: Roo.bootstrap,
27772 enableToggle:toggle !== false,
27774 pressed : toggle ? false : null,
27777 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27778 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27784 // var cb_box = function...
27789 xns: Roo.bootstrap,
27794 xns: Roo.bootstrap,
27798 Roo.each(this.formats, function(f) {
27799 style.menu.items.push({
27801 xns: Roo.bootstrap,
27802 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27807 editorcore.insertTag(this.tagname);
27814 children.push(style);
27816 btn('bold',false,true);
27817 btn('italic',false,true);
27818 btn('align-left', 'justifyleft',true);
27819 btn('align-center', 'justifycenter',true);
27820 btn('align-right' , 'justifyright',true);
27821 btn('link', false, false, function(btn) {
27822 //Roo.log("create link?");
27823 var url = prompt(this.createLinkText, this.defaultLinkValue);
27824 if(url && url != 'http:/'+'/'){
27825 this.editorcore.relayCmd('createlink', url);
27828 btn('list','insertunorderedlist',true);
27829 btn('pencil', false,true, function(btn){
27831 this.toggleSourceEdit(btn.pressed);
27834 if (this.editor.btns.length > 0) {
27835 for (var i = 0; i<this.editor.btns.length; i++) {
27836 children.push(this.editor.btns[i]);
27844 xns: Roo.bootstrap,
27849 xns: Roo.bootstrap,
27854 cog.menu.items.push({
27856 xns: Roo.bootstrap,
27857 html : Clean styles,
27862 editorcore.insertTag(this.tagname);
27871 this.xtype = 'NavSimplebar';
27873 for(var i=0;i< children.length;i++) {
27875 this.buttons.add(this.addxtypeChild(children[i]));
27879 editor.on('editorevent', this.updateToolbar, this);
27881 onBtnClick : function(id)
27883 this.editorcore.relayCmd(id);
27884 this.editorcore.focus();
27888 * Protected method that will not generally be called directly. It triggers
27889 * a toolbar update by reading the markup state of the current selection in the editor.
27891 updateToolbar: function(){
27893 if(!this.editorcore.activated){
27894 this.editor.onFirstFocus(); // is this neeed?
27898 var btns = this.buttons;
27899 var doc = this.editorcore.doc;
27900 btns.get('bold').setActive(doc.queryCommandState('bold'));
27901 btns.get('italic').setActive(doc.queryCommandState('italic'));
27902 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27904 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27905 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27906 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27908 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27909 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27912 var ans = this.editorcore.getAllAncestors();
27913 if (this.formatCombo) {
27916 var store = this.formatCombo.store;
27917 this.formatCombo.setValue("");
27918 for (var i =0; i < ans.length;i++) {
27919 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27921 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27929 // hides menus... - so this cant be on a menu...
27930 Roo.bootstrap.MenuMgr.hideAll();
27932 Roo.bootstrap.MenuMgr.hideAll();
27933 //this.editorsyncValue();
27935 onFirstFocus: function() {
27936 this.buttons.each(function(item){
27940 toggleSourceEdit : function(sourceEditMode){
27943 if(sourceEditMode){
27944 Roo.log("disabling buttons");
27945 this.buttons.each( function(item){
27946 if(item.cmd != 'pencil'){
27952 Roo.log("enabling buttons");
27953 if(this.editorcore.initialized){
27954 this.buttons.each( function(item){
27960 Roo.log("calling toggole on editor");
27961 // tell the editor that it's been pressed..
27962 this.editor.toggleSourceEdit(sourceEditMode);
27976 * @class Roo.bootstrap.Markdown
27977 * @extends Roo.bootstrap.TextArea
27978 * Bootstrap Showdown editable area
27979 * @cfg {string} content
27982 * Create a new Showdown
27985 Roo.bootstrap.Markdown = function(config){
27986 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27990 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27994 initEvents : function()
27997 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27998 this.markdownEl = this.el.createChild({
27999 cls : 'roo-markdown-area'
28001 this.inputEl().addClass('d-none');
28002 if (this.getValue() == '') {
28003 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28006 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28008 this.markdownEl.on('click', this.toggleTextEdit, this);
28009 this.on('blur', this.toggleTextEdit, this);
28010 this.on('specialkey', this.resizeTextArea, this);
28013 toggleTextEdit : function()
28015 var sh = this.markdownEl.getHeight();
28016 this.inputEl().addClass('d-none');
28017 this.markdownEl.addClass('d-none');
28018 if (!this.editing) {
28020 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28021 this.inputEl().removeClass('d-none');
28022 this.inputEl().focus();
28023 this.editing = true;
28026 // show showdown...
28027 this.updateMarkdown();
28028 this.markdownEl.removeClass('d-none');
28029 this.editing = false;
28032 updateMarkdown : function()
28034 if (this.getValue() == '') {
28035 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28039 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28042 resizeTextArea: function () {
28045 Roo.log([sh, this.getValue().split("\n").length * 30]);
28046 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28048 setValue : function(val)
28050 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28051 if (!this.editing) {
28052 this.updateMarkdown();
28058 if (!this.editing) {
28059 this.toggleTextEdit();
28067 * Ext JS Library 1.1.1
28068 * Copyright(c) 2006-2007, Ext JS, LLC.
28070 * Originally Released Under LGPL - original licence link has changed is not relivant.
28073 * <script type="text/javascript">
28077 * @class Roo.bootstrap.PagingToolbar
28078 * @extends Roo.bootstrap.NavSimplebar
28079 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28081 * Create a new PagingToolbar
28082 * @param {Object} config The config object
28083 * @param {Roo.data.Store} store
28085 Roo.bootstrap.PagingToolbar = function(config)
28087 // old args format still supported... - xtype is prefered..
28088 // created from xtype...
28090 this.ds = config.dataSource;
28092 if (config.store && !this.ds) {
28093 this.store= Roo.factory(config.store, Roo.data);
28094 this.ds = this.store;
28095 this.ds.xmodule = this.xmodule || false;
28098 this.toolbarItems = [];
28099 if (config.items) {
28100 this.toolbarItems = config.items;
28103 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28108 this.bind(this.ds);
28111 if (Roo.bootstrap.version == 4) {
28112 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28114 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28119 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28121 * @cfg {Roo.data.Store} dataSource
28122 * The underlying data store providing the paged data
28125 * @cfg {String/HTMLElement/Element} container
28126 * container The id or element that will contain the toolbar
28129 * @cfg {Boolean} displayInfo
28130 * True to display the displayMsg (defaults to false)
28133 * @cfg {Number} pageSize
28134 * The number of records to display per page (defaults to 20)
28138 * @cfg {String} displayMsg
28139 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28141 displayMsg : 'Displaying {0} - {1} of {2}',
28143 * @cfg {String} emptyMsg
28144 * The message to display when no records are found (defaults to "No data to display")
28146 emptyMsg : 'No data to display',
28148 * Customizable piece of the default paging text (defaults to "Page")
28151 beforePageText : "Page",
28153 * Customizable piece of the default paging text (defaults to "of %0")
28156 afterPageText : "of {0}",
28158 * Customizable piece of the default paging text (defaults to "First Page")
28161 firstText : "First Page",
28163 * Customizable piece of the default paging text (defaults to "Previous Page")
28166 prevText : "Previous Page",
28168 * Customizable piece of the default paging text (defaults to "Next Page")
28171 nextText : "Next Page",
28173 * Customizable piece of the default paging text (defaults to "Last Page")
28176 lastText : "Last Page",
28178 * Customizable piece of the default paging text (defaults to "Refresh")
28181 refreshText : "Refresh",
28185 onRender : function(ct, position)
28187 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28188 this.navgroup.parentId = this.id;
28189 this.navgroup.onRender(this.el, null);
28190 // add the buttons to the navgroup
28192 if(this.displayInfo){
28193 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28194 this.displayEl = this.el.select('.x-paging-info', true).first();
28195 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28196 // this.displayEl = navel.el.select('span',true).first();
28202 Roo.each(_this.buttons, function(e){ // this might need to use render????
28203 Roo.factory(e).render(_this.el);
28207 Roo.each(_this.toolbarItems, function(e) {
28208 _this.navgroup.addItem(e);
28212 this.first = this.navgroup.addItem({
28213 tooltip: this.firstText,
28214 cls: "prev btn-outline-secondary",
28215 html : ' <i class="fa fa-step-backward"></i>',
28217 preventDefault: true,
28218 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28221 this.prev = this.navgroup.addItem({
28222 tooltip: this.prevText,
28223 cls: "prev btn-outline-secondary",
28224 html : ' <i class="fa fa-backward"></i>',
28226 preventDefault: true,
28227 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28229 //this.addSeparator();
28232 var field = this.navgroup.addItem( {
28234 cls : 'x-paging-position btn-outline-secondary',
28236 html : this.beforePageText +
28237 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28238 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28241 this.field = field.el.select('input', true).first();
28242 this.field.on("keydown", this.onPagingKeydown, this);
28243 this.field.on("focus", function(){this.dom.select();});
28246 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28247 //this.field.setHeight(18);
28248 //this.addSeparator();
28249 this.next = this.navgroup.addItem({
28250 tooltip: this.nextText,
28251 cls: "next btn-outline-secondary",
28252 html : ' <i class="fa fa-forward"></i>',
28254 preventDefault: true,
28255 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28257 this.last = this.navgroup.addItem({
28258 tooltip: this.lastText,
28259 html : ' <i class="fa fa-step-forward"></i>',
28260 cls: "next btn-outline-secondary",
28262 preventDefault: true,
28263 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28265 //this.addSeparator();
28266 this.loading = this.navgroup.addItem({
28267 tooltip: this.refreshText,
28268 cls: "btn-outline-secondary",
28269 html : ' <i class="fa fa-refresh"></i>',
28270 preventDefault: true,
28271 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28277 updateInfo : function(){
28278 if(this.displayEl){
28279 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28280 var msg = count == 0 ?
28284 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28286 this.displayEl.update(msg);
28291 onLoad : function(ds, r, o)
28293 this.cursor = o.params && o.params.start ? o.params.start : 0;
28295 var d = this.getPageData(),
28300 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28301 this.field.dom.value = ap;
28302 this.first.setDisabled(ap == 1);
28303 this.prev.setDisabled(ap == 1);
28304 this.next.setDisabled(ap == ps);
28305 this.last.setDisabled(ap == ps);
28306 this.loading.enable();
28311 getPageData : function(){
28312 var total = this.ds.getTotalCount();
28315 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28316 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28321 onLoadError : function(){
28322 this.loading.enable();
28326 onPagingKeydown : function(e){
28327 var k = e.getKey();
28328 var d = this.getPageData();
28330 var v = this.field.dom.value, pageNum;
28331 if(!v || isNaN(pageNum = parseInt(v, 10))){
28332 this.field.dom.value = d.activePage;
28335 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28336 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28339 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))
28341 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28342 this.field.dom.value = pageNum;
28343 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28346 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28348 var v = this.field.dom.value, pageNum;
28349 var increment = (e.shiftKey) ? 10 : 1;
28350 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28353 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28354 this.field.dom.value = d.activePage;
28357 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28359 this.field.dom.value = parseInt(v, 10) + increment;
28360 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28361 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28368 beforeLoad : function(){
28370 this.loading.disable();
28375 onClick : function(which){
28384 ds.load({params:{start: 0, limit: this.pageSize}});
28387 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28390 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28393 var total = ds.getTotalCount();
28394 var extra = total % this.pageSize;
28395 var lastStart = extra ? (total - extra) : total-this.pageSize;
28396 ds.load({params:{start: lastStart, limit: this.pageSize}});
28399 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28405 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28406 * @param {Roo.data.Store} store The data store to unbind
28408 unbind : function(ds){
28409 ds.un("beforeload", this.beforeLoad, this);
28410 ds.un("load", this.onLoad, this);
28411 ds.un("loadexception", this.onLoadError, this);
28412 ds.un("remove", this.updateInfo, this);
28413 ds.un("add", this.updateInfo, this);
28414 this.ds = undefined;
28418 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28419 * @param {Roo.data.Store} store The data store to bind
28421 bind : function(ds){
28422 ds.on("beforeload", this.beforeLoad, this);
28423 ds.on("load", this.onLoad, this);
28424 ds.on("loadexception", this.onLoadError, this);
28425 ds.on("remove", this.updateInfo, this);
28426 ds.on("add", this.updateInfo, this);
28437 * @class Roo.bootstrap.MessageBar
28438 * @extends Roo.bootstrap.Component
28439 * Bootstrap MessageBar class
28440 * @cfg {String} html contents of the MessageBar
28441 * @cfg {String} weight (info | success | warning | danger) default info
28442 * @cfg {String} beforeClass insert the bar before the given class
28443 * @cfg {Boolean} closable (true | false) default false
28444 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28447 * Create a new Element
28448 * @param {Object} config The config object
28451 Roo.bootstrap.MessageBar = function(config){
28452 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28455 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28461 beforeClass: 'bootstrap-sticky-wrap',
28463 getAutoCreate : function(){
28467 cls: 'alert alert-dismissable alert-' + this.weight,
28472 html: this.html || ''
28478 cfg.cls += ' alert-messages-fixed';
28492 onRender : function(ct, position)
28494 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28497 var cfg = Roo.apply({}, this.getAutoCreate());
28501 cfg.cls += ' ' + this.cls;
28504 cfg.style = this.style;
28506 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28508 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28511 this.el.select('>button.close').on('click', this.hide, this);
28517 if (!this.rendered) {
28523 this.fireEvent('show', this);
28529 if (!this.rendered) {
28535 this.fireEvent('hide', this);
28538 update : function()
28540 // var e = this.el.dom.firstChild;
28542 // if(this.closable){
28543 // e = e.nextSibling;
28546 // e.data = this.html || '';
28548 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28564 * @class Roo.bootstrap.Graph
28565 * @extends Roo.bootstrap.Component
28566 * Bootstrap Graph class
28570 @cfg {String} graphtype bar | vbar | pie
28571 @cfg {number} g_x coodinator | centre x (pie)
28572 @cfg {number} g_y coodinator | centre y (pie)
28573 @cfg {number} g_r radius (pie)
28574 @cfg {number} g_height height of the chart (respected by all elements in the set)
28575 @cfg {number} g_width width of the chart (respected by all elements in the set)
28576 @cfg {Object} title The title of the chart
28579 -opts (object) options for the chart
28581 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28582 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28584 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.
28585 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28587 o stretch (boolean)
28589 -opts (object) options for the pie
28592 o startAngle (number)
28593 o endAngle (number)
28597 * Create a new Input
28598 * @param {Object} config The config object
28601 Roo.bootstrap.Graph = function(config){
28602 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28608 * The img click event for the img.
28609 * @param {Roo.EventObject} e
28615 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28626 //g_colors: this.colors,
28633 getAutoCreate : function(){
28644 onRender : function(ct,position){
28647 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28649 if (typeof(Raphael) == 'undefined') {
28650 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28654 this.raphael = Raphael(this.el.dom);
28656 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28657 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28658 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28659 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28661 r.text(160, 10, "Single Series Chart").attr(txtattr);
28662 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28663 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28664 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28666 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28667 r.barchart(330, 10, 300, 220, data1);
28668 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28669 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28672 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28673 // r.barchart(30, 30, 560, 250, xdata, {
28674 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28675 // axis : "0 0 1 1",
28676 // axisxlabels : xdata
28677 // //yvalues : cols,
28680 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28682 // this.load(null,xdata,{
28683 // axis : "0 0 1 1",
28684 // axisxlabels : xdata
28689 load : function(graphtype,xdata,opts)
28691 this.raphael.clear();
28693 graphtype = this.graphtype;
28698 var r = this.raphael,
28699 fin = function () {
28700 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28702 fout = function () {
28703 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28705 pfin = function() {
28706 this.sector.stop();
28707 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28710 this.label[0].stop();
28711 this.label[0].attr({ r: 7.5 });
28712 this.label[1].attr({ "font-weight": 800 });
28715 pfout = function() {
28716 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28719 this.label[0].animate({ r: 5 }, 500, "bounce");
28720 this.label[1].attr({ "font-weight": 400 });
28726 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28729 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28732 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28733 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28735 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28742 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28747 setTitle: function(o)
28752 initEvents: function() {
28755 this.el.on('click', this.onClick, this);
28759 onClick : function(e)
28761 Roo.log('img onclick');
28762 this.fireEvent('click', this, e);
28774 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28777 * @class Roo.bootstrap.dash.NumberBox
28778 * @extends Roo.bootstrap.Component
28779 * Bootstrap NumberBox class
28780 * @cfg {String} headline Box headline
28781 * @cfg {String} content Box content
28782 * @cfg {String} icon Box icon
28783 * @cfg {String} footer Footer text
28784 * @cfg {String} fhref Footer href
28787 * Create a new NumberBox
28788 * @param {Object} config The config object
28792 Roo.bootstrap.dash.NumberBox = function(config){
28793 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28797 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28806 getAutoCreate : function(){
28810 cls : 'small-box ',
28818 cls : 'roo-headline',
28819 html : this.headline
28823 cls : 'roo-content',
28824 html : this.content
28838 cls : 'ion ' + this.icon
28847 cls : 'small-box-footer',
28848 href : this.fhref || '#',
28852 cfg.cn.push(footer);
28859 onRender : function(ct,position){
28860 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28867 setHeadline: function (value)
28869 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28872 setFooter: function (value, href)
28874 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28877 this.el.select('a.small-box-footer',true).first().attr('href', href);
28882 setContent: function (value)
28884 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28887 initEvents: function()
28901 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28904 * @class Roo.bootstrap.dash.TabBox
28905 * @extends Roo.bootstrap.Component
28906 * Bootstrap TabBox class
28907 * @cfg {String} title Title of the TabBox
28908 * @cfg {String} icon Icon of the TabBox
28909 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28910 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28913 * Create a new TabBox
28914 * @param {Object} config The config object
28918 Roo.bootstrap.dash.TabBox = function(config){
28919 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28924 * When a pane is added
28925 * @param {Roo.bootstrap.dash.TabPane} pane
28929 * @event activatepane
28930 * When a pane is activated
28931 * @param {Roo.bootstrap.dash.TabPane} pane
28933 "activatepane" : true
28941 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28946 tabScrollable : false,
28948 getChildContainer : function()
28950 return this.el.select('.tab-content', true).first();
28953 getAutoCreate : function(){
28957 cls: 'pull-left header',
28965 cls: 'fa ' + this.icon
28971 cls: 'nav nav-tabs pull-right',
28977 if(this.tabScrollable){
28984 cls: 'nav nav-tabs pull-right',
28995 cls: 'nav-tabs-custom',
29000 cls: 'tab-content no-padding',
29008 initEvents : function()
29010 //Roo.log('add add pane handler');
29011 this.on('addpane', this.onAddPane, this);
29014 * Updates the box title
29015 * @param {String} html to set the title to.
29017 setTitle : function(value)
29019 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29021 onAddPane : function(pane)
29023 this.panes.push(pane);
29024 //Roo.log('addpane');
29026 // tabs are rendere left to right..
29027 if(!this.showtabs){
29031 var ctr = this.el.select('.nav-tabs', true).first();
29034 var existing = ctr.select('.nav-tab',true);
29035 var qty = existing.getCount();;
29038 var tab = ctr.createChild({
29040 cls : 'nav-tab' + (qty ? '' : ' active'),
29048 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29051 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29053 pane.el.addClass('active');
29058 onTabClick : function(ev,un,ob,pane)
29060 //Roo.log('tab - prev default');
29061 ev.preventDefault();
29064 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29065 pane.tab.addClass('active');
29066 //Roo.log(pane.title);
29067 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29068 // technically we should have a deactivate event.. but maybe add later.
29069 // and it should not de-activate the selected tab...
29070 this.fireEvent('activatepane', pane);
29071 pane.el.addClass('active');
29072 pane.fireEvent('activate');
29077 getActivePane : function()
29080 Roo.each(this.panes, function(p) {
29081 if(p.el.hasClass('active')){
29102 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29104 * @class Roo.bootstrap.TabPane
29105 * @extends Roo.bootstrap.Component
29106 * Bootstrap TabPane class
29107 * @cfg {Boolean} active (false | true) Default false
29108 * @cfg {String} title title of panel
29112 * Create a new TabPane
29113 * @param {Object} config The config object
29116 Roo.bootstrap.dash.TabPane = function(config){
29117 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29123 * When a pane is activated
29124 * @param {Roo.bootstrap.dash.TabPane} pane
29131 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29136 // the tabBox that this is attached to.
29139 getAutoCreate : function()
29147 cfg.cls += ' active';
29152 initEvents : function()
29154 //Roo.log('trigger add pane handler');
29155 this.parent().fireEvent('addpane', this)
29159 * Updates the tab title
29160 * @param {String} html to set the title to.
29162 setTitle: function(str)
29168 this.tab.select('a', true).first().dom.innerHTML = str;
29185 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29188 * @class Roo.bootstrap.menu.Menu
29189 * @extends Roo.bootstrap.Component
29190 * Bootstrap Menu class - container for Menu
29191 * @cfg {String} html Text of the menu
29192 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29193 * @cfg {String} icon Font awesome icon
29194 * @cfg {String} pos Menu align to (top | bottom) default bottom
29198 * Create a new Menu
29199 * @param {Object} config The config object
29203 Roo.bootstrap.menu.Menu = function(config){
29204 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29208 * @event beforeshow
29209 * Fires before this menu is displayed
29210 * @param {Roo.bootstrap.menu.Menu} this
29214 * @event beforehide
29215 * Fires before this menu is hidden
29216 * @param {Roo.bootstrap.menu.Menu} this
29221 * Fires after this menu is displayed
29222 * @param {Roo.bootstrap.menu.Menu} this
29227 * Fires after this menu is hidden
29228 * @param {Roo.bootstrap.menu.Menu} this
29233 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29234 * @param {Roo.bootstrap.menu.Menu} this
29235 * @param {Roo.EventObject} e
29242 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29246 weight : 'default',
29251 getChildContainer : function() {
29252 if(this.isSubMenu){
29256 return this.el.select('ul.dropdown-menu', true).first();
29259 getAutoCreate : function()
29264 cls : 'roo-menu-text',
29272 cls : 'fa ' + this.icon
29283 cls : 'dropdown-button btn btn-' + this.weight,
29288 cls : 'dropdown-toggle btn btn-' + this.weight,
29298 cls : 'dropdown-menu'
29304 if(this.pos == 'top'){
29305 cfg.cls += ' dropup';
29308 if(this.isSubMenu){
29311 cls : 'dropdown-menu'
29318 onRender : function(ct, position)
29320 this.isSubMenu = ct.hasClass('dropdown-submenu');
29322 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29325 initEvents : function()
29327 if(this.isSubMenu){
29331 this.hidden = true;
29333 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29334 this.triggerEl.on('click', this.onTriggerPress, this);
29336 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29337 this.buttonEl.on('click', this.onClick, this);
29343 if(this.isSubMenu){
29347 return this.el.select('ul.dropdown-menu', true).first();
29350 onClick : function(e)
29352 this.fireEvent("click", this, e);
29355 onTriggerPress : function(e)
29357 if (this.isVisible()) {
29364 isVisible : function(){
29365 return !this.hidden;
29370 this.fireEvent("beforeshow", this);
29372 this.hidden = false;
29373 this.el.addClass('open');
29375 Roo.get(document).on("mouseup", this.onMouseUp, this);
29377 this.fireEvent("show", this);
29384 this.fireEvent("beforehide", this);
29386 this.hidden = true;
29387 this.el.removeClass('open');
29389 Roo.get(document).un("mouseup", this.onMouseUp);
29391 this.fireEvent("hide", this);
29394 onMouseUp : function()
29408 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29411 * @class Roo.bootstrap.menu.Item
29412 * @extends Roo.bootstrap.Component
29413 * Bootstrap MenuItem class
29414 * @cfg {Boolean} submenu (true | false) default false
29415 * @cfg {String} html text of the item
29416 * @cfg {String} href the link
29417 * @cfg {Boolean} disable (true | false) default false
29418 * @cfg {Boolean} preventDefault (true | false) default true
29419 * @cfg {String} icon Font awesome icon
29420 * @cfg {String} pos Submenu align to (left | right) default right
29424 * Create a new Item
29425 * @param {Object} config The config object
29429 Roo.bootstrap.menu.Item = function(config){
29430 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29434 * Fires when the mouse is hovering over this menu
29435 * @param {Roo.bootstrap.menu.Item} this
29436 * @param {Roo.EventObject} e
29441 * Fires when the mouse exits this menu
29442 * @param {Roo.bootstrap.menu.Item} this
29443 * @param {Roo.EventObject} e
29449 * The raw click event for the entire grid.
29450 * @param {Roo.EventObject} e
29456 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29461 preventDefault: true,
29466 getAutoCreate : function()
29471 cls : 'roo-menu-item-text',
29479 cls : 'fa ' + this.icon
29488 href : this.href || '#',
29495 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29499 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29501 if(this.pos == 'left'){
29502 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29509 initEvents : function()
29511 this.el.on('mouseover', this.onMouseOver, this);
29512 this.el.on('mouseout', this.onMouseOut, this);
29514 this.el.select('a', true).first().on('click', this.onClick, this);
29518 onClick : function(e)
29520 if(this.preventDefault){
29521 e.preventDefault();
29524 this.fireEvent("click", this, e);
29527 onMouseOver : function(e)
29529 if(this.submenu && this.pos == 'left'){
29530 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29533 this.fireEvent("mouseover", this, e);
29536 onMouseOut : function(e)
29538 this.fireEvent("mouseout", this, e);
29550 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29553 * @class Roo.bootstrap.menu.Separator
29554 * @extends Roo.bootstrap.Component
29555 * Bootstrap Separator class
29558 * Create a new Separator
29559 * @param {Object} config The config object
29563 Roo.bootstrap.menu.Separator = function(config){
29564 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29567 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29569 getAutoCreate : function(){
29572 cls: 'dropdown-divider divider'
29590 * @class Roo.bootstrap.Tooltip
29591 * Bootstrap Tooltip class
29592 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29593 * to determine which dom element triggers the tooltip.
29595 * It needs to add support for additional attributes like tooltip-position
29598 * Create a new Toolti
29599 * @param {Object} config The config object
29602 Roo.bootstrap.Tooltip = function(config){
29603 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29605 this.alignment = Roo.bootstrap.Tooltip.alignment;
29607 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29608 this.alignment = config.alignment;
29613 Roo.apply(Roo.bootstrap.Tooltip, {
29615 * @function init initialize tooltip monitoring.
29619 currentTip : false,
29620 currentRegion : false,
29626 Roo.get(document).on('mouseover', this.enter ,this);
29627 Roo.get(document).on('mouseout', this.leave, this);
29630 this.currentTip = new Roo.bootstrap.Tooltip();
29633 enter : function(ev)
29635 var dom = ev.getTarget();
29637 //Roo.log(['enter',dom]);
29638 var el = Roo.fly(dom);
29639 if (this.currentEl) {
29641 //Roo.log(this.currentEl);
29642 //Roo.log(this.currentEl.contains(dom));
29643 if (this.currentEl == el) {
29646 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29652 if (this.currentTip.el) {
29653 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29657 if(!el || el.dom == document){
29663 if (!el.attr('tooltip')) {
29664 pel = el.findParent("[tooltip]");
29666 bindEl = Roo.get(pel);
29672 // you can not look for children, as if el is the body.. then everythign is the child..
29673 if (!pel && !el.attr('tooltip')) { //
29674 if (!el.select("[tooltip]").elements.length) {
29677 // is the mouse over this child...?
29678 bindEl = el.select("[tooltip]").first();
29679 var xy = ev.getXY();
29680 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29681 //Roo.log("not in region.");
29684 //Roo.log("child element over..");
29687 this.currentEl = el;
29688 this.currentTip.bind(bindEl);
29689 this.currentRegion = Roo.lib.Region.getRegion(dom);
29690 this.currentTip.enter();
29693 leave : function(ev)
29695 var dom = ev.getTarget();
29696 //Roo.log(['leave',dom]);
29697 if (!this.currentEl) {
29702 if (dom != this.currentEl.dom) {
29705 var xy = ev.getXY();
29706 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29709 // only activate leave if mouse cursor is outside... bounding box..
29714 if (this.currentTip) {
29715 this.currentTip.leave();
29717 //Roo.log('clear currentEl');
29718 this.currentEl = false;
29723 'left' : ['r-l', [-2,0], 'right'],
29724 'right' : ['l-r', [2,0], 'left'],
29725 'bottom' : ['t-b', [0,2], 'top'],
29726 'top' : [ 'b-t', [0,-2], 'bottom']
29732 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29737 delay : null, // can be { show : 300 , hide: 500}
29741 hoverState : null, //???
29743 placement : 'bottom',
29747 getAutoCreate : function(){
29754 cls : 'tooltip-arrow arrow'
29757 cls : 'tooltip-inner'
29764 bind : function(el)
29769 initEvents : function()
29771 this.arrowEl = this.el.select('.arrow', true).first();
29772 this.innerEl = this.el.select('.tooltip-inner', true).first();
29775 enter : function () {
29777 if (this.timeout != null) {
29778 clearTimeout(this.timeout);
29781 this.hoverState = 'in';
29782 //Roo.log("enter - show");
29783 if (!this.delay || !this.delay.show) {
29788 this.timeout = setTimeout(function () {
29789 if (_t.hoverState == 'in') {
29792 }, this.delay.show);
29796 clearTimeout(this.timeout);
29798 this.hoverState = 'out';
29799 if (!this.delay || !this.delay.hide) {
29805 this.timeout = setTimeout(function () {
29806 //Roo.log("leave - timeout");
29808 if (_t.hoverState == 'out') {
29810 Roo.bootstrap.Tooltip.currentEl = false;
29815 show : function (msg)
29818 this.render(document.body);
29821 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29823 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29825 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29827 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29828 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29830 var placement = typeof this.placement == 'function' ?
29831 this.placement.call(this, this.el, on_el) :
29834 var autoToken = /\s?auto?\s?/i;
29835 var autoPlace = autoToken.test(placement);
29837 placement = placement.replace(autoToken, '') || 'top';
29841 //this.el.setXY([0,0]);
29843 //this.el.dom.style.display='block';
29845 //this.el.appendTo(on_el);
29847 var p = this.getPosition();
29848 var box = this.el.getBox();
29854 var align = this.alignment[placement];
29856 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29858 if(placement == 'top' || placement == 'bottom'){
29860 placement = 'right';
29863 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29864 placement = 'left';
29867 var scroll = Roo.select('body', true).first().getScroll();
29869 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29873 align = this.alignment[placement];
29875 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29879 var elems = document.getElementsByTagName('div');
29880 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29881 for (var i = 0; i < elems.length; i++) {
29882 var zindex = Number.parseInt(
29883 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29886 if (zindex > highest) {
29893 this.el.dom.style.zIndex = highest;
29895 this.el.alignTo(this.bindEl, align[0],align[1]);
29896 //var arrow = this.el.select('.arrow',true).first();
29897 //arrow.set(align[2],
29899 this.el.addClass(placement);
29900 this.el.addClass("bs-tooltip-"+ placement);
29902 this.el.addClass('in fade show');
29904 this.hoverState = null;
29906 if (this.el.hasClass('fade')) {
29921 //this.el.setXY([0,0]);
29922 this.el.removeClass(['show', 'in']);
29938 * @class Roo.bootstrap.LocationPicker
29939 * @extends Roo.bootstrap.Component
29940 * Bootstrap LocationPicker class
29941 * @cfg {Number} latitude Position when init default 0
29942 * @cfg {Number} longitude Position when init default 0
29943 * @cfg {Number} zoom default 15
29944 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29945 * @cfg {Boolean} mapTypeControl default false
29946 * @cfg {Boolean} disableDoubleClickZoom default false
29947 * @cfg {Boolean} scrollwheel default true
29948 * @cfg {Boolean} streetViewControl default false
29949 * @cfg {Number} radius default 0
29950 * @cfg {String} locationName
29951 * @cfg {Boolean} draggable default true
29952 * @cfg {Boolean} enableAutocomplete default false
29953 * @cfg {Boolean} enableReverseGeocode default true
29954 * @cfg {String} markerTitle
29957 * Create a new LocationPicker
29958 * @param {Object} config The config object
29962 Roo.bootstrap.LocationPicker = function(config){
29964 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29969 * Fires when the picker initialized.
29970 * @param {Roo.bootstrap.LocationPicker} this
29971 * @param {Google Location} location
29975 * @event positionchanged
29976 * Fires when the picker position changed.
29977 * @param {Roo.bootstrap.LocationPicker} this
29978 * @param {Google Location} location
29980 positionchanged : true,
29983 * Fires when the map resize.
29984 * @param {Roo.bootstrap.LocationPicker} this
29989 * Fires when the map show.
29990 * @param {Roo.bootstrap.LocationPicker} this
29995 * Fires when the map hide.
29996 * @param {Roo.bootstrap.LocationPicker} this
30001 * Fires when click the map.
30002 * @param {Roo.bootstrap.LocationPicker} this
30003 * @param {Map event} e
30007 * @event mapRightClick
30008 * Fires when right click the map.
30009 * @param {Roo.bootstrap.LocationPicker} this
30010 * @param {Map event} e
30012 mapRightClick : true,
30014 * @event markerClick
30015 * Fires when click the marker.
30016 * @param {Roo.bootstrap.LocationPicker} this
30017 * @param {Map event} e
30019 markerClick : true,
30021 * @event markerRightClick
30022 * Fires when right click the marker.
30023 * @param {Roo.bootstrap.LocationPicker} this
30024 * @param {Map event} e
30026 markerRightClick : true,
30028 * @event OverlayViewDraw
30029 * Fires when OverlayView Draw
30030 * @param {Roo.bootstrap.LocationPicker} this
30032 OverlayViewDraw : true,
30034 * @event OverlayViewOnAdd
30035 * Fires when OverlayView Draw
30036 * @param {Roo.bootstrap.LocationPicker} this
30038 OverlayViewOnAdd : true,
30040 * @event OverlayViewOnRemove
30041 * Fires when OverlayView Draw
30042 * @param {Roo.bootstrap.LocationPicker} this
30044 OverlayViewOnRemove : true,
30046 * @event OverlayViewShow
30047 * Fires when OverlayView Draw
30048 * @param {Roo.bootstrap.LocationPicker} this
30049 * @param {Pixel} cpx
30051 OverlayViewShow : true,
30053 * @event OverlayViewHide
30054 * Fires when OverlayView Draw
30055 * @param {Roo.bootstrap.LocationPicker} this
30057 OverlayViewHide : true,
30059 * @event loadexception
30060 * Fires when load google lib failed.
30061 * @param {Roo.bootstrap.LocationPicker} this
30063 loadexception : true
30068 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30070 gMapContext: false,
30076 mapTypeControl: false,
30077 disableDoubleClickZoom: false,
30079 streetViewControl: false,
30083 enableAutocomplete: false,
30084 enableReverseGeocode: true,
30087 getAutoCreate: function()
30092 cls: 'roo-location-picker'
30098 initEvents: function(ct, position)
30100 if(!this.el.getWidth() || this.isApplied()){
30104 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30109 initial: function()
30111 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30112 this.fireEvent('loadexception', this);
30116 if(!this.mapTypeId){
30117 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30120 this.gMapContext = this.GMapContext();
30122 this.initOverlayView();
30124 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30128 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30129 _this.setPosition(_this.gMapContext.marker.position);
30132 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30133 _this.fireEvent('mapClick', this, event);
30137 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30138 _this.fireEvent('mapRightClick', this, event);
30142 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30143 _this.fireEvent('markerClick', this, event);
30147 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30148 _this.fireEvent('markerRightClick', this, event);
30152 this.setPosition(this.gMapContext.location);
30154 this.fireEvent('initial', this, this.gMapContext.location);
30157 initOverlayView: function()
30161 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30165 _this.fireEvent('OverlayViewDraw', _this);
30170 _this.fireEvent('OverlayViewOnAdd', _this);
30173 onRemove: function()
30175 _this.fireEvent('OverlayViewOnRemove', _this);
30178 show: function(cpx)
30180 _this.fireEvent('OverlayViewShow', _this, cpx);
30185 _this.fireEvent('OverlayViewHide', _this);
30191 fromLatLngToContainerPixel: function(event)
30193 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30196 isApplied: function()
30198 return this.getGmapContext() == false ? false : true;
30201 getGmapContext: function()
30203 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30206 GMapContext: function()
30208 var position = new google.maps.LatLng(this.latitude, this.longitude);
30210 var _map = new google.maps.Map(this.el.dom, {
30213 mapTypeId: this.mapTypeId,
30214 mapTypeControl: this.mapTypeControl,
30215 disableDoubleClickZoom: this.disableDoubleClickZoom,
30216 scrollwheel: this.scrollwheel,
30217 streetViewControl: this.streetViewControl,
30218 locationName: this.locationName,
30219 draggable: this.draggable,
30220 enableAutocomplete: this.enableAutocomplete,
30221 enableReverseGeocode: this.enableReverseGeocode
30224 var _marker = new google.maps.Marker({
30225 position: position,
30227 title: this.markerTitle,
30228 draggable: this.draggable
30235 location: position,
30236 radius: this.radius,
30237 locationName: this.locationName,
30238 addressComponents: {
30239 formatted_address: null,
30240 addressLine1: null,
30241 addressLine2: null,
30243 streetNumber: null,
30247 stateOrProvince: null
30250 domContainer: this.el.dom,
30251 geodecoder: new google.maps.Geocoder()
30255 drawCircle: function(center, radius, options)
30257 if (this.gMapContext.circle != null) {
30258 this.gMapContext.circle.setMap(null);
30262 options = Roo.apply({}, options, {
30263 strokeColor: "#0000FF",
30264 strokeOpacity: .35,
30266 fillColor: "#0000FF",
30270 options.map = this.gMapContext.map;
30271 options.radius = radius;
30272 options.center = center;
30273 this.gMapContext.circle = new google.maps.Circle(options);
30274 return this.gMapContext.circle;
30280 setPosition: function(location)
30282 this.gMapContext.location = location;
30283 this.gMapContext.marker.setPosition(location);
30284 this.gMapContext.map.panTo(location);
30285 this.drawCircle(location, this.gMapContext.radius, {});
30289 if (this.gMapContext.settings.enableReverseGeocode) {
30290 this.gMapContext.geodecoder.geocode({
30291 latLng: this.gMapContext.location
30292 }, function(results, status) {
30294 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30295 _this.gMapContext.locationName = results[0].formatted_address;
30296 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30298 _this.fireEvent('positionchanged', this, location);
30305 this.fireEvent('positionchanged', this, location);
30310 google.maps.event.trigger(this.gMapContext.map, "resize");
30312 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30314 this.fireEvent('resize', this);
30317 setPositionByLatLng: function(latitude, longitude)
30319 this.setPosition(new google.maps.LatLng(latitude, longitude));
30322 getCurrentPosition: function()
30325 latitude: this.gMapContext.location.lat(),
30326 longitude: this.gMapContext.location.lng()
30330 getAddressName: function()
30332 return this.gMapContext.locationName;
30335 getAddressComponents: function()
30337 return this.gMapContext.addressComponents;
30340 address_component_from_google_geocode: function(address_components)
30344 for (var i = 0; i < address_components.length; i++) {
30345 var component = address_components[i];
30346 if (component.types.indexOf("postal_code") >= 0) {
30347 result.postalCode = component.short_name;
30348 } else if (component.types.indexOf("street_number") >= 0) {
30349 result.streetNumber = component.short_name;
30350 } else if (component.types.indexOf("route") >= 0) {
30351 result.streetName = component.short_name;
30352 } else if (component.types.indexOf("neighborhood") >= 0) {
30353 result.city = component.short_name;
30354 } else if (component.types.indexOf("locality") >= 0) {
30355 result.city = component.short_name;
30356 } else if (component.types.indexOf("sublocality") >= 0) {
30357 result.district = component.short_name;
30358 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30359 result.stateOrProvince = component.short_name;
30360 } else if (component.types.indexOf("country") >= 0) {
30361 result.country = component.short_name;
30365 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30366 result.addressLine2 = "";
30370 setZoomLevel: function(zoom)
30372 this.gMapContext.map.setZoom(zoom);
30385 this.fireEvent('show', this);
30396 this.fireEvent('hide', this);
30401 Roo.apply(Roo.bootstrap.LocationPicker, {
30403 OverlayView : function(map, options)
30405 options = options || {};
30412 * @class Roo.bootstrap.Alert
30413 * @extends Roo.bootstrap.Component
30414 * Bootstrap Alert class - shows an alert area box
30416 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30417 Enter a valid email address
30420 * @cfg {String} title The title of alert
30421 * @cfg {String} html The content of alert
30422 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30423 * @cfg {String} fa font-awesomeicon
30424 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30425 * @cfg {Boolean} close true to show a x closer
30429 * Create a new alert
30430 * @param {Object} config The config object
30434 Roo.bootstrap.Alert = function(config){
30435 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30439 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30445 faicon: false, // BC
30449 getAutoCreate : function()
30461 style : this.close ? '' : 'display:none'
30465 cls : 'roo-alert-icon'
30470 cls : 'roo-alert-title',
30475 cls : 'roo-alert-text',
30482 cfg.cn[0].cls += ' fa ' + this.faicon;
30485 cfg.cn[0].cls += ' fa ' + this.fa;
30489 cfg.cls += ' alert-' + this.weight;
30495 initEvents: function()
30497 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30498 this.titleEl = this.el.select('.roo-alert-title',true).first();
30499 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30500 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30501 if (this.seconds > 0) {
30502 this.hide.defer(this.seconds, this);
30506 * Set the Title Message HTML
30507 * @param {String} html
30509 setTitle : function(str)
30511 this.titleEl.dom.innerHTML = str;
30515 * Set the Body Message HTML
30516 * @param {String} html
30518 setHtml : function(str)
30520 this.htmlEl.dom.innerHTML = str;
30523 * Set the Weight of the alert
30524 * @param {String} (success|info|warning|danger) weight
30527 setWeight : function(weight)
30530 this.el.removeClass('alert-' + this.weight);
30533 this.weight = weight;
30535 this.el.addClass('alert-' + this.weight);
30538 * Set the Icon of the alert
30539 * @param {String} see fontawsome names (name without the 'fa-' bit)
30541 setIcon : function(icon)
30544 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30547 this.faicon = icon;
30549 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30574 * @class Roo.bootstrap.UploadCropbox
30575 * @extends Roo.bootstrap.Component
30576 * Bootstrap UploadCropbox class
30577 * @cfg {String} emptyText show when image has been loaded
30578 * @cfg {String} rotateNotify show when image too small to rotate
30579 * @cfg {Number} errorTimeout default 3000
30580 * @cfg {Number} minWidth default 300
30581 * @cfg {Number} minHeight default 300
30582 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30583 * @cfg {Boolean} isDocument (true|false) default false
30584 * @cfg {String} url action url
30585 * @cfg {String} paramName default 'imageUpload'
30586 * @cfg {String} method default POST
30587 * @cfg {Boolean} loadMask (true|false) default true
30588 * @cfg {Boolean} loadingText default 'Loading...'
30591 * Create a new UploadCropbox
30592 * @param {Object} config The config object
30595 Roo.bootstrap.UploadCropbox = function(config){
30596 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30600 * @event beforeselectfile
30601 * Fire before select file
30602 * @param {Roo.bootstrap.UploadCropbox} this
30604 "beforeselectfile" : true,
30607 * Fire after initEvent
30608 * @param {Roo.bootstrap.UploadCropbox} this
30613 * Fire after initEvent
30614 * @param {Roo.bootstrap.UploadCropbox} this
30615 * @param {String} data
30620 * Fire when preparing the file data
30621 * @param {Roo.bootstrap.UploadCropbox} this
30622 * @param {Object} file
30627 * Fire when get exception
30628 * @param {Roo.bootstrap.UploadCropbox} this
30629 * @param {XMLHttpRequest} xhr
30631 "exception" : true,
30633 * @event beforeloadcanvas
30634 * Fire before load the canvas
30635 * @param {Roo.bootstrap.UploadCropbox} this
30636 * @param {String} src
30638 "beforeloadcanvas" : true,
30641 * Fire when trash image
30642 * @param {Roo.bootstrap.UploadCropbox} this
30647 * Fire when download the image
30648 * @param {Roo.bootstrap.UploadCropbox} this
30652 * @event footerbuttonclick
30653 * Fire when footerbuttonclick
30654 * @param {Roo.bootstrap.UploadCropbox} this
30655 * @param {String} type
30657 "footerbuttonclick" : true,
30661 * @param {Roo.bootstrap.UploadCropbox} this
30666 * Fire when rotate the image
30667 * @param {Roo.bootstrap.UploadCropbox} this
30668 * @param {String} pos
30673 * Fire when inspect the file
30674 * @param {Roo.bootstrap.UploadCropbox} this
30675 * @param {Object} file
30680 * Fire when xhr upload the file
30681 * @param {Roo.bootstrap.UploadCropbox} this
30682 * @param {Object} data
30687 * Fire when arrange the file data
30688 * @param {Roo.bootstrap.UploadCropbox} this
30689 * @param {Object} formData
30694 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30697 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30699 emptyText : 'Click to upload image',
30700 rotateNotify : 'Image is too small to rotate',
30701 errorTimeout : 3000,
30715 cropType : 'image/jpeg',
30717 canvasLoaded : false,
30718 isDocument : false,
30720 paramName : 'imageUpload',
30722 loadingText : 'Loading...',
30725 getAutoCreate : function()
30729 cls : 'roo-upload-cropbox',
30733 cls : 'roo-upload-cropbox-selector',
30738 cls : 'roo-upload-cropbox-body',
30739 style : 'cursor:pointer',
30743 cls : 'roo-upload-cropbox-preview'
30747 cls : 'roo-upload-cropbox-thumb'
30751 cls : 'roo-upload-cropbox-empty-notify',
30752 html : this.emptyText
30756 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30757 html : this.rotateNotify
30763 cls : 'roo-upload-cropbox-footer',
30766 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30776 onRender : function(ct, position)
30778 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30780 if (this.buttons.length) {
30782 Roo.each(this.buttons, function(bb) {
30784 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30786 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30792 this.maskEl = this.el;
30796 initEvents : function()
30798 this.urlAPI = (window.createObjectURL && window) ||
30799 (window.URL && URL.revokeObjectURL && URL) ||
30800 (window.webkitURL && webkitURL);
30802 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30803 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30805 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30806 this.selectorEl.hide();
30808 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30809 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30811 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30812 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30813 this.thumbEl.hide();
30815 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30816 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30818 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30819 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30820 this.errorEl.hide();
30822 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30823 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30824 this.footerEl.hide();
30826 this.setThumbBoxSize();
30832 this.fireEvent('initial', this);
30839 window.addEventListener("resize", function() { _this.resize(); } );
30841 this.bodyEl.on('click', this.beforeSelectFile, this);
30844 this.bodyEl.on('touchstart', this.onTouchStart, this);
30845 this.bodyEl.on('touchmove', this.onTouchMove, this);
30846 this.bodyEl.on('touchend', this.onTouchEnd, this);
30850 this.bodyEl.on('mousedown', this.onMouseDown, this);
30851 this.bodyEl.on('mousemove', this.onMouseMove, this);
30852 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30853 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30854 Roo.get(document).on('mouseup', this.onMouseUp, this);
30857 this.selectorEl.on('change', this.onFileSelected, this);
30863 this.baseScale = 1;
30865 this.baseRotate = 1;
30866 this.dragable = false;
30867 this.pinching = false;
30870 this.cropData = false;
30871 this.notifyEl.dom.innerHTML = this.emptyText;
30873 this.selectorEl.dom.value = '';
30877 resize : function()
30879 if(this.fireEvent('resize', this) != false){
30880 this.setThumbBoxPosition();
30881 this.setCanvasPosition();
30885 onFooterButtonClick : function(e, el, o, type)
30888 case 'rotate-left' :
30889 this.onRotateLeft(e);
30891 case 'rotate-right' :
30892 this.onRotateRight(e);
30895 this.beforeSelectFile(e);
30910 this.fireEvent('footerbuttonclick', this, type);
30913 beforeSelectFile : function(e)
30915 e.preventDefault();
30917 if(this.fireEvent('beforeselectfile', this) != false){
30918 this.selectorEl.dom.click();
30922 onFileSelected : function(e)
30924 e.preventDefault();
30926 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30930 var file = this.selectorEl.dom.files[0];
30932 if(this.fireEvent('inspect', this, file) != false){
30933 this.prepare(file);
30938 trash : function(e)
30940 this.fireEvent('trash', this);
30943 download : function(e)
30945 this.fireEvent('download', this);
30948 loadCanvas : function(src)
30950 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30954 this.imageEl = document.createElement('img');
30958 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30960 this.imageEl.src = src;
30964 onLoadCanvas : function()
30966 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30967 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30969 this.bodyEl.un('click', this.beforeSelectFile, this);
30971 this.notifyEl.hide();
30972 this.thumbEl.show();
30973 this.footerEl.show();
30975 this.baseRotateLevel();
30977 if(this.isDocument){
30978 this.setThumbBoxSize();
30981 this.setThumbBoxPosition();
30983 this.baseScaleLevel();
30989 this.canvasLoaded = true;
30992 this.maskEl.unmask();
30997 setCanvasPosition : function()
30999 if(!this.canvasEl){
31003 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31004 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31006 this.previewEl.setLeft(pw);
31007 this.previewEl.setTop(ph);
31011 onMouseDown : function(e)
31015 this.dragable = true;
31016 this.pinching = false;
31018 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31019 this.dragable = false;
31023 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31024 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31028 onMouseMove : function(e)
31032 if(!this.canvasLoaded){
31036 if (!this.dragable){
31040 var minX = Math.ceil(this.thumbEl.getLeft(true));
31041 var minY = Math.ceil(this.thumbEl.getTop(true));
31043 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31044 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31046 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31047 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31049 x = x - this.mouseX;
31050 y = y - this.mouseY;
31052 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31053 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31055 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31056 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31058 this.previewEl.setLeft(bgX);
31059 this.previewEl.setTop(bgY);
31061 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31062 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31065 onMouseUp : function(e)
31069 this.dragable = false;
31072 onMouseWheel : function(e)
31076 this.startScale = this.scale;
31078 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31080 if(!this.zoomable()){
31081 this.scale = this.startScale;
31090 zoomable : function()
31092 var minScale = this.thumbEl.getWidth() / this.minWidth;
31094 if(this.minWidth < this.minHeight){
31095 minScale = this.thumbEl.getHeight() / this.minHeight;
31098 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31099 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31103 (this.rotate == 0 || this.rotate == 180) &&
31105 width > this.imageEl.OriginWidth ||
31106 height > this.imageEl.OriginHeight ||
31107 (width < this.minWidth && height < this.minHeight)
31115 (this.rotate == 90 || this.rotate == 270) &&
31117 width > this.imageEl.OriginWidth ||
31118 height > this.imageEl.OriginHeight ||
31119 (width < this.minHeight && height < this.minWidth)
31126 !this.isDocument &&
31127 (this.rotate == 0 || this.rotate == 180) &&
31129 width < this.minWidth ||
31130 width > this.imageEl.OriginWidth ||
31131 height < this.minHeight ||
31132 height > this.imageEl.OriginHeight
31139 !this.isDocument &&
31140 (this.rotate == 90 || this.rotate == 270) &&
31142 width < this.minHeight ||
31143 width > this.imageEl.OriginWidth ||
31144 height < this.minWidth ||
31145 height > this.imageEl.OriginHeight
31155 onRotateLeft : function(e)
31157 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31159 var minScale = this.thumbEl.getWidth() / this.minWidth;
31161 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31162 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31164 this.startScale = this.scale;
31166 while (this.getScaleLevel() < minScale){
31168 this.scale = this.scale + 1;
31170 if(!this.zoomable()){
31175 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31176 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31181 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31188 this.scale = this.startScale;
31190 this.onRotateFail();
31195 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31197 if(this.isDocument){
31198 this.setThumbBoxSize();
31199 this.setThumbBoxPosition();
31200 this.setCanvasPosition();
31205 this.fireEvent('rotate', this, 'left');
31209 onRotateRight : function(e)
31211 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31213 var minScale = this.thumbEl.getWidth() / this.minWidth;
31215 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31216 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31218 this.startScale = this.scale;
31220 while (this.getScaleLevel() < minScale){
31222 this.scale = this.scale + 1;
31224 if(!this.zoomable()){
31229 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31230 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31235 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31242 this.scale = this.startScale;
31244 this.onRotateFail();
31249 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31251 if(this.isDocument){
31252 this.setThumbBoxSize();
31253 this.setThumbBoxPosition();
31254 this.setCanvasPosition();
31259 this.fireEvent('rotate', this, 'right');
31262 onRotateFail : function()
31264 this.errorEl.show(true);
31268 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31273 this.previewEl.dom.innerHTML = '';
31275 var canvasEl = document.createElement("canvas");
31277 var contextEl = canvasEl.getContext("2d");
31279 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31280 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31281 var center = this.imageEl.OriginWidth / 2;
31283 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31284 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31285 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31286 center = this.imageEl.OriginHeight / 2;
31289 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31291 contextEl.translate(center, center);
31292 contextEl.rotate(this.rotate * Math.PI / 180);
31294 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31296 this.canvasEl = document.createElement("canvas");
31298 this.contextEl = this.canvasEl.getContext("2d");
31300 switch (this.rotate) {
31303 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31304 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31306 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31311 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31312 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31314 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31315 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);
31319 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31324 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31325 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31327 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31328 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);
31332 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);
31337 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31338 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31340 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31341 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31345 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);
31352 this.previewEl.appendChild(this.canvasEl);
31354 this.setCanvasPosition();
31359 if(!this.canvasLoaded){
31363 var imageCanvas = document.createElement("canvas");
31365 var imageContext = imageCanvas.getContext("2d");
31367 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31368 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31370 var center = imageCanvas.width / 2;
31372 imageContext.translate(center, center);
31374 imageContext.rotate(this.rotate * Math.PI / 180);
31376 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31378 var canvas = document.createElement("canvas");
31380 var context = canvas.getContext("2d");
31382 canvas.width = this.minWidth;
31383 canvas.height = this.minHeight;
31385 switch (this.rotate) {
31388 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31389 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31391 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31392 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31394 var targetWidth = this.minWidth - 2 * x;
31395 var targetHeight = this.minHeight - 2 * y;
31399 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31400 scale = targetWidth / width;
31403 if(x > 0 && y == 0){
31404 scale = targetHeight / height;
31407 if(x > 0 && y > 0){
31408 scale = targetWidth / width;
31410 if(width < height){
31411 scale = targetHeight / height;
31415 context.scale(scale, scale);
31417 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31418 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31420 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31421 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31423 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31428 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31429 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31431 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31432 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31434 var targetWidth = this.minWidth - 2 * x;
31435 var targetHeight = this.minHeight - 2 * y;
31439 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31440 scale = targetWidth / width;
31443 if(x > 0 && y == 0){
31444 scale = targetHeight / height;
31447 if(x > 0 && y > 0){
31448 scale = targetWidth / width;
31450 if(width < height){
31451 scale = targetHeight / height;
31455 context.scale(scale, scale);
31457 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31458 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31460 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31461 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31463 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31465 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31470 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31471 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31473 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31474 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31476 var targetWidth = this.minWidth - 2 * x;
31477 var targetHeight = this.minHeight - 2 * y;
31481 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31482 scale = targetWidth / width;
31485 if(x > 0 && y == 0){
31486 scale = targetHeight / height;
31489 if(x > 0 && y > 0){
31490 scale = targetWidth / width;
31492 if(width < height){
31493 scale = targetHeight / height;
31497 context.scale(scale, scale);
31499 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31500 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31502 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31503 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31505 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31506 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31508 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31513 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31514 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31516 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31517 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31519 var targetWidth = this.minWidth - 2 * x;
31520 var targetHeight = this.minHeight - 2 * y;
31524 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31525 scale = targetWidth / width;
31528 if(x > 0 && y == 0){
31529 scale = targetHeight / height;
31532 if(x > 0 && y > 0){
31533 scale = targetWidth / width;
31535 if(width < height){
31536 scale = targetHeight / height;
31540 context.scale(scale, scale);
31542 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31543 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31545 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31546 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31548 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31550 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31557 this.cropData = canvas.toDataURL(this.cropType);
31559 if(this.fireEvent('crop', this, this.cropData) !== false){
31560 this.process(this.file, this.cropData);
31567 setThumbBoxSize : function()
31571 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31572 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31573 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31575 this.minWidth = width;
31576 this.minHeight = height;
31578 if(this.rotate == 90 || this.rotate == 270){
31579 this.minWidth = height;
31580 this.minHeight = width;
31585 width = Math.ceil(this.minWidth * height / this.minHeight);
31587 if(this.minWidth > this.minHeight){
31589 height = Math.ceil(this.minHeight * width / this.minWidth);
31592 this.thumbEl.setStyle({
31593 width : width + 'px',
31594 height : height + 'px'
31601 setThumbBoxPosition : function()
31603 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31604 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31606 this.thumbEl.setLeft(x);
31607 this.thumbEl.setTop(y);
31611 baseRotateLevel : function()
31613 this.baseRotate = 1;
31616 typeof(this.exif) != 'undefined' &&
31617 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31618 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31620 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31623 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31627 baseScaleLevel : function()
31631 if(this.isDocument){
31633 if(this.baseRotate == 6 || this.baseRotate == 8){
31635 height = this.thumbEl.getHeight();
31636 this.baseScale = height / this.imageEl.OriginWidth;
31638 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31639 width = this.thumbEl.getWidth();
31640 this.baseScale = width / this.imageEl.OriginHeight;
31646 height = this.thumbEl.getHeight();
31647 this.baseScale = height / this.imageEl.OriginHeight;
31649 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31650 width = this.thumbEl.getWidth();
31651 this.baseScale = width / this.imageEl.OriginWidth;
31657 if(this.baseRotate == 6 || this.baseRotate == 8){
31659 width = this.thumbEl.getHeight();
31660 this.baseScale = width / this.imageEl.OriginHeight;
31662 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31663 height = this.thumbEl.getWidth();
31664 this.baseScale = height / this.imageEl.OriginHeight;
31667 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31668 height = this.thumbEl.getWidth();
31669 this.baseScale = height / this.imageEl.OriginHeight;
31671 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31672 width = this.thumbEl.getHeight();
31673 this.baseScale = width / this.imageEl.OriginWidth;
31680 width = this.thumbEl.getWidth();
31681 this.baseScale = width / this.imageEl.OriginWidth;
31683 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31684 height = this.thumbEl.getHeight();
31685 this.baseScale = height / this.imageEl.OriginHeight;
31688 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31690 height = this.thumbEl.getHeight();
31691 this.baseScale = height / this.imageEl.OriginHeight;
31693 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31694 width = this.thumbEl.getWidth();
31695 this.baseScale = width / this.imageEl.OriginWidth;
31703 getScaleLevel : function()
31705 return this.baseScale * Math.pow(1.1, this.scale);
31708 onTouchStart : function(e)
31710 if(!this.canvasLoaded){
31711 this.beforeSelectFile(e);
31715 var touches = e.browserEvent.touches;
31721 if(touches.length == 1){
31722 this.onMouseDown(e);
31726 if(touches.length != 2){
31732 for(var i = 0, finger; finger = touches[i]; i++){
31733 coords.push(finger.pageX, finger.pageY);
31736 var x = Math.pow(coords[0] - coords[2], 2);
31737 var y = Math.pow(coords[1] - coords[3], 2);
31739 this.startDistance = Math.sqrt(x + y);
31741 this.startScale = this.scale;
31743 this.pinching = true;
31744 this.dragable = false;
31748 onTouchMove : function(e)
31750 if(!this.pinching && !this.dragable){
31754 var touches = e.browserEvent.touches;
31761 this.onMouseMove(e);
31767 for(var i = 0, finger; finger = touches[i]; i++){
31768 coords.push(finger.pageX, finger.pageY);
31771 var x = Math.pow(coords[0] - coords[2], 2);
31772 var y = Math.pow(coords[1] - coords[3], 2);
31774 this.endDistance = Math.sqrt(x + y);
31776 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31778 if(!this.zoomable()){
31779 this.scale = this.startScale;
31787 onTouchEnd : function(e)
31789 this.pinching = false;
31790 this.dragable = false;
31794 process : function(file, crop)
31797 this.maskEl.mask(this.loadingText);
31800 this.xhr = new XMLHttpRequest();
31802 file.xhr = this.xhr;
31804 this.xhr.open(this.method, this.url, true);
31807 "Accept": "application/json",
31808 "Cache-Control": "no-cache",
31809 "X-Requested-With": "XMLHttpRequest"
31812 for (var headerName in headers) {
31813 var headerValue = headers[headerName];
31815 this.xhr.setRequestHeader(headerName, headerValue);
31821 this.xhr.onload = function()
31823 _this.xhrOnLoad(_this.xhr);
31826 this.xhr.onerror = function()
31828 _this.xhrOnError(_this.xhr);
31831 var formData = new FormData();
31833 formData.append('returnHTML', 'NO');
31836 formData.append('crop', crop);
31839 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31840 formData.append(this.paramName, file, file.name);
31843 if(typeof(file.filename) != 'undefined'){
31844 formData.append('filename', file.filename);
31847 if(typeof(file.mimetype) != 'undefined'){
31848 formData.append('mimetype', file.mimetype);
31851 if(this.fireEvent('arrange', this, formData) != false){
31852 this.xhr.send(formData);
31856 xhrOnLoad : function(xhr)
31859 this.maskEl.unmask();
31862 if (xhr.readyState !== 4) {
31863 this.fireEvent('exception', this, xhr);
31867 var response = Roo.decode(xhr.responseText);
31869 if(!response.success){
31870 this.fireEvent('exception', this, xhr);
31874 var response = Roo.decode(xhr.responseText);
31876 this.fireEvent('upload', this, response);
31880 xhrOnError : function()
31883 this.maskEl.unmask();
31886 Roo.log('xhr on error');
31888 var response = Roo.decode(xhr.responseText);
31894 prepare : function(file)
31897 this.maskEl.mask(this.loadingText);
31903 if(typeof(file) === 'string'){
31904 this.loadCanvas(file);
31908 if(!file || !this.urlAPI){
31913 this.cropType = file.type;
31917 if(this.fireEvent('prepare', this, this.file) != false){
31919 var reader = new FileReader();
31921 reader.onload = function (e) {
31922 if (e.target.error) {
31923 Roo.log(e.target.error);
31927 var buffer = e.target.result,
31928 dataView = new DataView(buffer),
31930 maxOffset = dataView.byteLength - 4,
31934 if (dataView.getUint16(0) === 0xffd8) {
31935 while (offset < maxOffset) {
31936 markerBytes = dataView.getUint16(offset);
31938 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31939 markerLength = dataView.getUint16(offset + 2) + 2;
31940 if (offset + markerLength > dataView.byteLength) {
31941 Roo.log('Invalid meta data: Invalid segment size.');
31945 if(markerBytes == 0xffe1){
31946 _this.parseExifData(
31953 offset += markerLength;
31963 var url = _this.urlAPI.createObjectURL(_this.file);
31965 _this.loadCanvas(url);
31970 reader.readAsArrayBuffer(this.file);
31976 parseExifData : function(dataView, offset, length)
31978 var tiffOffset = offset + 10,
31982 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31983 // No Exif data, might be XMP data instead
31987 // Check for the ASCII code for "Exif" (0x45786966):
31988 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31989 // No Exif data, might be XMP data instead
31992 if (tiffOffset + 8 > dataView.byteLength) {
31993 Roo.log('Invalid Exif data: Invalid segment size.');
31996 // Check for the two null bytes:
31997 if (dataView.getUint16(offset + 8) !== 0x0000) {
31998 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32001 // Check the byte alignment:
32002 switch (dataView.getUint16(tiffOffset)) {
32004 littleEndian = true;
32007 littleEndian = false;
32010 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32013 // Check for the TIFF tag marker (0x002A):
32014 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32015 Roo.log('Invalid Exif data: Missing TIFF marker.');
32018 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32019 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32021 this.parseExifTags(
32024 tiffOffset + dirOffset,
32029 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32034 if (dirOffset + 6 > dataView.byteLength) {
32035 Roo.log('Invalid Exif data: Invalid directory offset.');
32038 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32039 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32040 if (dirEndOffset + 4 > dataView.byteLength) {
32041 Roo.log('Invalid Exif data: Invalid directory size.');
32044 for (i = 0; i < tagsNumber; i += 1) {
32048 dirOffset + 2 + 12 * i, // tag offset
32052 // Return the offset to the next directory:
32053 return dataView.getUint32(dirEndOffset, littleEndian);
32056 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32058 var tag = dataView.getUint16(offset, littleEndian);
32060 this.exif[tag] = this.getExifValue(
32064 dataView.getUint16(offset + 2, littleEndian), // tag type
32065 dataView.getUint32(offset + 4, littleEndian), // tag length
32070 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32072 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32081 Roo.log('Invalid Exif data: Invalid tag type.');
32085 tagSize = tagType.size * length;
32086 // Determine if the value is contained in the dataOffset bytes,
32087 // or if the value at the dataOffset is a pointer to the actual data:
32088 dataOffset = tagSize > 4 ?
32089 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32090 if (dataOffset + tagSize > dataView.byteLength) {
32091 Roo.log('Invalid Exif data: Invalid data offset.');
32094 if (length === 1) {
32095 return tagType.getValue(dataView, dataOffset, littleEndian);
32098 for (i = 0; i < length; i += 1) {
32099 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32102 if (tagType.ascii) {
32104 // Concatenate the chars:
32105 for (i = 0; i < values.length; i += 1) {
32107 // Ignore the terminating NULL byte(s):
32108 if (c === '\u0000') {
32120 Roo.apply(Roo.bootstrap.UploadCropbox, {
32122 'Orientation': 0x0112
32126 1: 0, //'top-left',
32128 3: 180, //'bottom-right',
32129 // 4: 'bottom-left',
32131 6: 90, //'right-top',
32132 // 7: 'right-bottom',
32133 8: 270 //'left-bottom'
32137 // byte, 8-bit unsigned int:
32139 getValue: function (dataView, dataOffset) {
32140 return dataView.getUint8(dataOffset);
32144 // ascii, 8-bit byte:
32146 getValue: function (dataView, dataOffset) {
32147 return String.fromCharCode(dataView.getUint8(dataOffset));
32152 // short, 16 bit int:
32154 getValue: function (dataView, dataOffset, littleEndian) {
32155 return dataView.getUint16(dataOffset, littleEndian);
32159 // long, 32 bit int:
32161 getValue: function (dataView, dataOffset, littleEndian) {
32162 return dataView.getUint32(dataOffset, littleEndian);
32166 // rational = two long values, first is numerator, second is denominator:
32168 getValue: function (dataView, dataOffset, littleEndian) {
32169 return dataView.getUint32(dataOffset, littleEndian) /
32170 dataView.getUint32(dataOffset + 4, littleEndian);
32174 // slong, 32 bit signed int:
32176 getValue: function (dataView, dataOffset, littleEndian) {
32177 return dataView.getInt32(dataOffset, littleEndian);
32181 // srational, two slongs, first is numerator, second is denominator:
32183 getValue: function (dataView, dataOffset, littleEndian) {
32184 return dataView.getInt32(dataOffset, littleEndian) /
32185 dataView.getInt32(dataOffset + 4, littleEndian);
32195 cls : 'btn-group roo-upload-cropbox-rotate-left',
32196 action : 'rotate-left',
32200 cls : 'btn btn-default',
32201 html : '<i class="fa fa-undo"></i>'
32207 cls : 'btn-group roo-upload-cropbox-picture',
32208 action : 'picture',
32212 cls : 'btn btn-default',
32213 html : '<i class="fa fa-picture-o"></i>'
32219 cls : 'btn-group roo-upload-cropbox-rotate-right',
32220 action : 'rotate-right',
32224 cls : 'btn btn-default',
32225 html : '<i class="fa fa-repeat"></i>'
32233 cls : 'btn-group roo-upload-cropbox-rotate-left',
32234 action : 'rotate-left',
32238 cls : 'btn btn-default',
32239 html : '<i class="fa fa-undo"></i>'
32245 cls : 'btn-group roo-upload-cropbox-download',
32246 action : 'download',
32250 cls : 'btn btn-default',
32251 html : '<i class="fa fa-download"></i>'
32257 cls : 'btn-group roo-upload-cropbox-crop',
32262 cls : 'btn btn-default',
32263 html : '<i class="fa fa-crop"></i>'
32269 cls : 'btn-group roo-upload-cropbox-trash',
32274 cls : 'btn btn-default',
32275 html : '<i class="fa fa-trash"></i>'
32281 cls : 'btn-group roo-upload-cropbox-rotate-right',
32282 action : 'rotate-right',
32286 cls : 'btn btn-default',
32287 html : '<i class="fa fa-repeat"></i>'
32295 cls : 'btn-group roo-upload-cropbox-rotate-left',
32296 action : 'rotate-left',
32300 cls : 'btn btn-default',
32301 html : '<i class="fa fa-undo"></i>'
32307 cls : 'btn-group roo-upload-cropbox-rotate-right',
32308 action : 'rotate-right',
32312 cls : 'btn btn-default',
32313 html : '<i class="fa fa-repeat"></i>'
32326 * @class Roo.bootstrap.DocumentManager
32327 * @extends Roo.bootstrap.Component
32328 * Bootstrap DocumentManager class
32329 * @cfg {String} paramName default 'imageUpload'
32330 * @cfg {String} toolTipName default 'filename'
32331 * @cfg {String} method default POST
32332 * @cfg {String} url action url
32333 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32334 * @cfg {Boolean} multiple multiple upload default true
32335 * @cfg {Number} thumbSize default 300
32336 * @cfg {String} fieldLabel
32337 * @cfg {Number} labelWidth default 4
32338 * @cfg {String} labelAlign (left|top) default left
32339 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32340 * @cfg {Number} labellg set the width of label (1-12)
32341 * @cfg {Number} labelmd set the width of label (1-12)
32342 * @cfg {Number} labelsm set the width of label (1-12)
32343 * @cfg {Number} labelxs set the width of label (1-12)
32346 * Create a new DocumentManager
32347 * @param {Object} config The config object
32350 Roo.bootstrap.DocumentManager = function(config){
32351 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32354 this.delegates = [];
32359 * Fire when initial the DocumentManager
32360 * @param {Roo.bootstrap.DocumentManager} this
32365 * inspect selected file
32366 * @param {Roo.bootstrap.DocumentManager} this
32367 * @param {File} file
32372 * Fire when xhr load exception
32373 * @param {Roo.bootstrap.DocumentManager} this
32374 * @param {XMLHttpRequest} xhr
32376 "exception" : true,
32378 * @event afterupload
32379 * Fire when xhr load exception
32380 * @param {Roo.bootstrap.DocumentManager} this
32381 * @param {XMLHttpRequest} xhr
32383 "afterupload" : true,
32386 * prepare the form data
32387 * @param {Roo.bootstrap.DocumentManager} this
32388 * @param {Object} formData
32393 * Fire when remove the file
32394 * @param {Roo.bootstrap.DocumentManager} this
32395 * @param {Object} file
32400 * Fire after refresh the file
32401 * @param {Roo.bootstrap.DocumentManager} this
32406 * Fire after click the image
32407 * @param {Roo.bootstrap.DocumentManager} this
32408 * @param {Object} file
32413 * Fire when upload a image and editable set to true
32414 * @param {Roo.bootstrap.DocumentManager} this
32415 * @param {Object} file
32419 * @event beforeselectfile
32420 * Fire before select file
32421 * @param {Roo.bootstrap.DocumentManager} this
32423 "beforeselectfile" : true,
32426 * Fire before process file
32427 * @param {Roo.bootstrap.DocumentManager} this
32428 * @param {Object} file
32432 * @event previewrendered
32433 * Fire when preview rendered
32434 * @param {Roo.bootstrap.DocumentManager} this
32435 * @param {Object} file
32437 "previewrendered" : true,
32440 "previewResize" : true
32445 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32454 paramName : 'imageUpload',
32455 toolTipName : 'filename',
32458 labelAlign : 'left',
32468 getAutoCreate : function()
32470 var managerWidget = {
32472 cls : 'roo-document-manager',
32476 cls : 'roo-document-manager-selector',
32481 cls : 'roo-document-manager-uploader',
32485 cls : 'roo-document-manager-upload-btn',
32486 html : '<i class="fa fa-plus"></i>'
32497 cls : 'column col-md-12',
32502 if(this.fieldLabel.length){
32507 cls : 'column col-md-12',
32508 html : this.fieldLabel
32512 cls : 'column col-md-12',
32517 if(this.labelAlign == 'left'){
32522 html : this.fieldLabel
32531 if(this.labelWidth > 12){
32532 content[0].style = "width: " + this.labelWidth + 'px';
32535 if(this.labelWidth < 13 && this.labelmd == 0){
32536 this.labelmd = this.labelWidth;
32539 if(this.labellg > 0){
32540 content[0].cls += ' col-lg-' + this.labellg;
32541 content[1].cls += ' col-lg-' + (12 - this.labellg);
32544 if(this.labelmd > 0){
32545 content[0].cls += ' col-md-' + this.labelmd;
32546 content[1].cls += ' col-md-' + (12 - this.labelmd);
32549 if(this.labelsm > 0){
32550 content[0].cls += ' col-sm-' + this.labelsm;
32551 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32554 if(this.labelxs > 0){
32555 content[0].cls += ' col-xs-' + this.labelxs;
32556 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32564 cls : 'row clearfix',
32572 initEvents : function()
32574 this.managerEl = this.el.select('.roo-document-manager', true).first();
32575 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32577 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32578 this.selectorEl.hide();
32581 this.selectorEl.attr('multiple', 'multiple');
32584 this.selectorEl.on('change', this.onFileSelected, this);
32586 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32587 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32589 this.uploader.on('click', this.onUploaderClick, this);
32591 this.renderProgressDialog();
32595 window.addEventListener("resize", function() { _this.refresh(); } );
32597 this.fireEvent('initial', this);
32600 renderProgressDialog : function()
32604 this.progressDialog = new Roo.bootstrap.Modal({
32605 cls : 'roo-document-manager-progress-dialog',
32606 allow_close : false,
32617 btnclick : function() {
32618 _this.uploadCancel();
32624 this.progressDialog.render(Roo.get(document.body));
32626 this.progress = new Roo.bootstrap.Progress({
32627 cls : 'roo-document-manager-progress',
32632 this.progress.render(this.progressDialog.getChildContainer());
32634 this.progressBar = new Roo.bootstrap.ProgressBar({
32635 cls : 'roo-document-manager-progress-bar',
32638 aria_valuemax : 12,
32642 this.progressBar.render(this.progress.getChildContainer());
32645 onUploaderClick : function(e)
32647 e.preventDefault();
32649 if(this.fireEvent('beforeselectfile', this) != false){
32650 this.selectorEl.dom.click();
32655 onFileSelected : function(e)
32657 e.preventDefault();
32659 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32663 Roo.each(this.selectorEl.dom.files, function(file){
32664 if(this.fireEvent('inspect', this, file) != false){
32665 this.files.push(file);
32675 this.selectorEl.dom.value = '';
32677 if(!this.files || !this.files.length){
32681 if(this.boxes > 0 && this.files.length > this.boxes){
32682 this.files = this.files.slice(0, this.boxes);
32685 this.uploader.show();
32687 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32688 this.uploader.hide();
32697 Roo.each(this.files, function(file){
32699 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32700 var f = this.renderPreview(file);
32705 if(file.type.indexOf('image') != -1){
32706 this.delegates.push(
32708 _this.process(file);
32709 }).createDelegate(this)
32717 _this.process(file);
32718 }).createDelegate(this)
32723 this.files = files;
32725 this.delegates = this.delegates.concat(docs);
32727 if(!this.delegates.length){
32732 this.progressBar.aria_valuemax = this.delegates.length;
32739 arrange : function()
32741 if(!this.delegates.length){
32742 this.progressDialog.hide();
32747 var delegate = this.delegates.shift();
32749 this.progressDialog.show();
32751 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32753 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32758 refresh : function()
32760 this.uploader.show();
32762 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32763 this.uploader.hide();
32766 Roo.isTouch ? this.closable(false) : this.closable(true);
32768 this.fireEvent('refresh', this);
32771 onRemove : function(e, el, o)
32773 e.preventDefault();
32775 this.fireEvent('remove', this, o);
32779 remove : function(o)
32783 Roo.each(this.files, function(file){
32784 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32793 this.files = files;
32800 Roo.each(this.files, function(file){
32805 file.target.remove();
32814 onClick : function(e, el, o)
32816 e.preventDefault();
32818 this.fireEvent('click', this, o);
32822 closable : function(closable)
32824 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32826 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32838 xhrOnLoad : function(xhr)
32840 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32844 if (xhr.readyState !== 4) {
32846 this.fireEvent('exception', this, xhr);
32850 var response = Roo.decode(xhr.responseText);
32852 if(!response.success){
32854 this.fireEvent('exception', this, xhr);
32858 var file = this.renderPreview(response.data);
32860 this.files.push(file);
32864 this.fireEvent('afterupload', this, xhr);
32868 xhrOnError : function(xhr)
32870 Roo.log('xhr on error');
32872 var response = Roo.decode(xhr.responseText);
32879 process : function(file)
32881 if(this.fireEvent('process', this, file) !== false){
32882 if(this.editable && file.type.indexOf('image') != -1){
32883 this.fireEvent('edit', this, file);
32887 this.uploadStart(file, false);
32894 uploadStart : function(file, crop)
32896 this.xhr = new XMLHttpRequest();
32898 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32903 file.xhr = this.xhr;
32905 this.managerEl.createChild({
32907 cls : 'roo-document-manager-loading',
32911 tooltip : file.name,
32912 cls : 'roo-document-manager-thumb',
32913 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32919 this.xhr.open(this.method, this.url, true);
32922 "Accept": "application/json",
32923 "Cache-Control": "no-cache",
32924 "X-Requested-With": "XMLHttpRequest"
32927 for (var headerName in headers) {
32928 var headerValue = headers[headerName];
32930 this.xhr.setRequestHeader(headerName, headerValue);
32936 this.xhr.onload = function()
32938 _this.xhrOnLoad(_this.xhr);
32941 this.xhr.onerror = function()
32943 _this.xhrOnError(_this.xhr);
32946 var formData = new FormData();
32948 formData.append('returnHTML', 'NO');
32951 formData.append('crop', crop);
32954 formData.append(this.paramName, file, file.name);
32961 if(this.fireEvent('prepare', this, formData, options) != false){
32963 if(options.manually){
32967 this.xhr.send(formData);
32971 this.uploadCancel();
32974 uploadCancel : function()
32980 this.delegates = [];
32982 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32989 renderPreview : function(file)
32991 if(typeof(file.target) != 'undefined' && file.target){
32995 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32997 var previewEl = this.managerEl.createChild({
32999 cls : 'roo-document-manager-preview',
33003 tooltip : file[this.toolTipName],
33004 cls : 'roo-document-manager-thumb',
33005 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33010 html : '<i class="fa fa-times-circle"></i>'
33015 var close = previewEl.select('button.close', true).first();
33017 close.on('click', this.onRemove, this, file);
33019 file.target = previewEl;
33021 var image = previewEl.select('img', true).first();
33025 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33027 image.on('click', this.onClick, this, file);
33029 this.fireEvent('previewrendered', this, file);
33035 onPreviewLoad : function(file, image)
33037 if(typeof(file.target) == 'undefined' || !file.target){
33041 var width = image.dom.naturalWidth || image.dom.width;
33042 var height = image.dom.naturalHeight || image.dom.height;
33044 if(!this.previewResize) {
33048 if(width > height){
33049 file.target.addClass('wide');
33053 file.target.addClass('tall');
33058 uploadFromSource : function(file, crop)
33060 this.xhr = new XMLHttpRequest();
33062 this.managerEl.createChild({
33064 cls : 'roo-document-manager-loading',
33068 tooltip : file.name,
33069 cls : 'roo-document-manager-thumb',
33070 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33076 this.xhr.open(this.method, this.url, true);
33079 "Accept": "application/json",
33080 "Cache-Control": "no-cache",
33081 "X-Requested-With": "XMLHttpRequest"
33084 for (var headerName in headers) {
33085 var headerValue = headers[headerName];
33087 this.xhr.setRequestHeader(headerName, headerValue);
33093 this.xhr.onload = function()
33095 _this.xhrOnLoad(_this.xhr);
33098 this.xhr.onerror = function()
33100 _this.xhrOnError(_this.xhr);
33103 var formData = new FormData();
33105 formData.append('returnHTML', 'NO');
33107 formData.append('crop', crop);
33109 if(typeof(file.filename) != 'undefined'){
33110 formData.append('filename', file.filename);
33113 if(typeof(file.mimetype) != 'undefined'){
33114 formData.append('mimetype', file.mimetype);
33119 if(this.fireEvent('prepare', this, formData) != false){
33120 this.xhr.send(formData);
33130 * @class Roo.bootstrap.DocumentViewer
33131 * @extends Roo.bootstrap.Component
33132 * Bootstrap DocumentViewer class
33133 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33134 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33137 * Create a new DocumentViewer
33138 * @param {Object} config The config object
33141 Roo.bootstrap.DocumentViewer = function(config){
33142 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33147 * Fire after initEvent
33148 * @param {Roo.bootstrap.DocumentViewer} this
33154 * @param {Roo.bootstrap.DocumentViewer} this
33159 * Fire after download button
33160 * @param {Roo.bootstrap.DocumentViewer} this
33165 * Fire after trash button
33166 * @param {Roo.bootstrap.DocumentViewer} this
33173 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33175 showDownload : true,
33179 getAutoCreate : function()
33183 cls : 'roo-document-viewer',
33187 cls : 'roo-document-viewer-body',
33191 cls : 'roo-document-viewer-thumb',
33195 cls : 'roo-document-viewer-image'
33203 cls : 'roo-document-viewer-footer',
33206 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33210 cls : 'btn-group roo-document-viewer-download',
33214 cls : 'btn btn-default',
33215 html : '<i class="fa fa-download"></i>'
33221 cls : 'btn-group roo-document-viewer-trash',
33225 cls : 'btn btn-default',
33226 html : '<i class="fa fa-trash"></i>'
33239 initEvents : function()
33241 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33242 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33244 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33245 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33247 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33248 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33250 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33251 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33253 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33254 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33256 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33257 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33259 this.bodyEl.on('click', this.onClick, this);
33260 this.downloadBtn.on('click', this.onDownload, this);
33261 this.trashBtn.on('click', this.onTrash, this);
33263 this.downloadBtn.hide();
33264 this.trashBtn.hide();
33266 if(this.showDownload){
33267 this.downloadBtn.show();
33270 if(this.showTrash){
33271 this.trashBtn.show();
33274 if(!this.showDownload && !this.showTrash) {
33275 this.footerEl.hide();
33280 initial : function()
33282 this.fireEvent('initial', this);
33286 onClick : function(e)
33288 e.preventDefault();
33290 this.fireEvent('click', this);
33293 onDownload : function(e)
33295 e.preventDefault();
33297 this.fireEvent('download', this);
33300 onTrash : function(e)
33302 e.preventDefault();
33304 this.fireEvent('trash', this);
33316 * @class Roo.bootstrap.NavProgressBar
33317 * @extends Roo.bootstrap.Component
33318 * Bootstrap NavProgressBar class
33321 * Create a new nav progress bar
33322 * @param {Object} config The config object
33325 Roo.bootstrap.NavProgressBar = function(config){
33326 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33328 this.bullets = this.bullets || [];
33330 // Roo.bootstrap.NavProgressBar.register(this);
33334 * Fires when the active item changes
33335 * @param {Roo.bootstrap.NavProgressBar} this
33336 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33337 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33344 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33349 getAutoCreate : function()
33351 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33355 cls : 'roo-navigation-bar-group',
33359 cls : 'roo-navigation-top-bar'
33363 cls : 'roo-navigation-bullets-bar',
33367 cls : 'roo-navigation-bar'
33374 cls : 'roo-navigation-bottom-bar'
33384 initEvents: function()
33389 onRender : function(ct, position)
33391 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33393 if(this.bullets.length){
33394 Roo.each(this.bullets, function(b){
33403 addItem : function(cfg)
33405 var item = new Roo.bootstrap.NavProgressItem(cfg);
33407 item.parentId = this.id;
33408 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33411 var top = new Roo.bootstrap.Element({
33413 cls : 'roo-navigation-bar-text'
33416 var bottom = new Roo.bootstrap.Element({
33418 cls : 'roo-navigation-bar-text'
33421 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33422 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33424 var topText = new Roo.bootstrap.Element({
33426 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33429 var bottomText = new Roo.bootstrap.Element({
33431 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33434 topText.onRender(top.el, null);
33435 bottomText.onRender(bottom.el, null);
33438 item.bottomEl = bottom;
33441 this.barItems.push(item);
33446 getActive : function()
33448 var active = false;
33450 Roo.each(this.barItems, function(v){
33452 if (!v.isActive()) {
33464 setActiveItem : function(item)
33468 Roo.each(this.barItems, function(v){
33469 if (v.rid == item.rid) {
33473 if (v.isActive()) {
33474 v.setActive(false);
33479 item.setActive(true);
33481 this.fireEvent('changed', this, item, prev);
33484 getBarItem: function(rid)
33488 Roo.each(this.barItems, function(e) {
33489 if (e.rid != rid) {
33500 indexOfItem : function(item)
33504 Roo.each(this.barItems, function(v, i){
33506 if (v.rid != item.rid) {
33517 setActiveNext : function()
33519 var i = this.indexOfItem(this.getActive());
33521 if (i > this.barItems.length) {
33525 this.setActiveItem(this.barItems[i+1]);
33528 setActivePrev : function()
33530 var i = this.indexOfItem(this.getActive());
33536 this.setActiveItem(this.barItems[i-1]);
33539 format : function()
33541 if(!this.barItems.length){
33545 var width = 100 / this.barItems.length;
33547 Roo.each(this.barItems, function(i){
33548 i.el.setStyle('width', width + '%');
33549 i.topEl.el.setStyle('width', width + '%');
33550 i.bottomEl.el.setStyle('width', width + '%');
33559 * Nav Progress Item
33564 * @class Roo.bootstrap.NavProgressItem
33565 * @extends Roo.bootstrap.Component
33566 * Bootstrap NavProgressItem class
33567 * @cfg {String} rid the reference id
33568 * @cfg {Boolean} active (true|false) Is item active default false
33569 * @cfg {Boolean} disabled (true|false) Is item active default false
33570 * @cfg {String} html
33571 * @cfg {String} position (top|bottom) text position default bottom
33572 * @cfg {String} icon show icon instead of number
33575 * Create a new NavProgressItem
33576 * @param {Object} config The config object
33578 Roo.bootstrap.NavProgressItem = function(config){
33579 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33584 * The raw click event for the entire grid.
33585 * @param {Roo.bootstrap.NavProgressItem} this
33586 * @param {Roo.EventObject} e
33593 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33599 position : 'bottom',
33602 getAutoCreate : function()
33604 var iconCls = 'roo-navigation-bar-item-icon';
33606 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33610 cls: 'roo-navigation-bar-item',
33620 cfg.cls += ' active';
33623 cfg.cls += ' disabled';
33629 disable : function()
33631 this.setDisabled(true);
33634 enable : function()
33636 this.setDisabled(false);
33639 initEvents: function()
33641 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33643 this.iconEl.on('click', this.onClick, this);
33646 onClick : function(e)
33648 e.preventDefault();
33654 if(this.fireEvent('click', this, e) === false){
33658 this.parent().setActiveItem(this);
33661 isActive: function ()
33663 return this.active;
33666 setActive : function(state)
33668 if(this.active == state){
33672 this.active = state;
33675 this.el.addClass('active');
33679 this.el.removeClass('active');
33684 setDisabled : function(state)
33686 if(this.disabled == state){
33690 this.disabled = state;
33693 this.el.addClass('disabled');
33697 this.el.removeClass('disabled');
33700 tooltipEl : function()
33702 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33715 * @class Roo.bootstrap.FieldLabel
33716 * @extends Roo.bootstrap.Component
33717 * Bootstrap FieldLabel class
33718 * @cfg {String} html contents of the element
33719 * @cfg {String} tag tag of the element default label
33720 * @cfg {String} cls class of the element
33721 * @cfg {String} target label target
33722 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33723 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33724 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33725 * @cfg {String} iconTooltip default "This field is required"
33726 * @cfg {String} indicatorpos (left|right) default left
33729 * Create a new FieldLabel
33730 * @param {Object} config The config object
33733 Roo.bootstrap.FieldLabel = function(config){
33734 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33739 * Fires after the field has been marked as invalid.
33740 * @param {Roo.form.FieldLabel} this
33741 * @param {String} msg The validation message
33746 * Fires after the field has been validated with no errors.
33747 * @param {Roo.form.FieldLabel} this
33753 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33760 invalidClass : 'has-warning',
33761 validClass : 'has-success',
33762 iconTooltip : 'This field is required',
33763 indicatorpos : 'left',
33765 getAutoCreate : function(){
33768 if (!this.allowBlank) {
33774 cls : 'roo-bootstrap-field-label ' + this.cls,
33779 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33780 tooltip : this.iconTooltip
33789 if(this.indicatorpos == 'right'){
33792 cls : 'roo-bootstrap-field-label ' + this.cls,
33801 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33802 tooltip : this.iconTooltip
33811 initEvents: function()
33813 Roo.bootstrap.Element.superclass.initEvents.call(this);
33815 this.indicator = this.indicatorEl();
33817 if(this.indicator){
33818 this.indicator.removeClass('visible');
33819 this.indicator.addClass('invisible');
33822 Roo.bootstrap.FieldLabel.register(this);
33825 indicatorEl : function()
33827 var indicator = this.el.select('i.roo-required-indicator',true).first();
33838 * Mark this field as valid
33840 markValid : function()
33842 if(this.indicator){
33843 this.indicator.removeClass('visible');
33844 this.indicator.addClass('invisible');
33846 if (Roo.bootstrap.version == 3) {
33847 this.el.removeClass(this.invalidClass);
33848 this.el.addClass(this.validClass);
33850 this.el.removeClass('is-invalid');
33851 this.el.addClass('is-valid');
33855 this.fireEvent('valid', this);
33859 * Mark this field as invalid
33860 * @param {String} msg The validation message
33862 markInvalid : function(msg)
33864 if(this.indicator){
33865 this.indicator.removeClass('invisible');
33866 this.indicator.addClass('visible');
33868 if (Roo.bootstrap.version == 3) {
33869 this.el.removeClass(this.validClass);
33870 this.el.addClass(this.invalidClass);
33872 this.el.removeClass('is-valid');
33873 this.el.addClass('is-invalid');
33877 this.fireEvent('invalid', this, msg);
33883 Roo.apply(Roo.bootstrap.FieldLabel, {
33888 * register a FieldLabel Group
33889 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33891 register : function(label)
33893 if(this.groups.hasOwnProperty(label.target)){
33897 this.groups[label.target] = label;
33901 * fetch a FieldLabel Group based on the target
33902 * @param {string} target
33903 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33905 get: function(target) {
33906 if (typeof(this.groups[target]) == 'undefined') {
33910 return this.groups[target] ;
33919 * page DateSplitField.
33925 * @class Roo.bootstrap.DateSplitField
33926 * @extends Roo.bootstrap.Component
33927 * Bootstrap DateSplitField class
33928 * @cfg {string} fieldLabel - the label associated
33929 * @cfg {Number} labelWidth set the width of label (0-12)
33930 * @cfg {String} labelAlign (top|left)
33931 * @cfg {Boolean} dayAllowBlank (true|false) default false
33932 * @cfg {Boolean} monthAllowBlank (true|false) default false
33933 * @cfg {Boolean} yearAllowBlank (true|false) default false
33934 * @cfg {string} dayPlaceholder
33935 * @cfg {string} monthPlaceholder
33936 * @cfg {string} yearPlaceholder
33937 * @cfg {string} dayFormat default 'd'
33938 * @cfg {string} monthFormat default 'm'
33939 * @cfg {string} yearFormat default 'Y'
33940 * @cfg {Number} labellg set the width of label (1-12)
33941 * @cfg {Number} labelmd set the width of label (1-12)
33942 * @cfg {Number} labelsm set the width of label (1-12)
33943 * @cfg {Number} labelxs set the width of label (1-12)
33947 * Create a new DateSplitField
33948 * @param {Object} config The config object
33951 Roo.bootstrap.DateSplitField = function(config){
33952 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33958 * getting the data of years
33959 * @param {Roo.bootstrap.DateSplitField} this
33960 * @param {Object} years
33965 * getting the data of days
33966 * @param {Roo.bootstrap.DateSplitField} this
33967 * @param {Object} days
33972 * Fires after the field has been marked as invalid.
33973 * @param {Roo.form.Field} this
33974 * @param {String} msg The validation message
33979 * Fires after the field has been validated with no errors.
33980 * @param {Roo.form.Field} this
33986 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33989 labelAlign : 'top',
33991 dayAllowBlank : false,
33992 monthAllowBlank : false,
33993 yearAllowBlank : false,
33994 dayPlaceholder : '',
33995 monthPlaceholder : '',
33996 yearPlaceholder : '',
34000 isFormField : true,
34006 getAutoCreate : function()
34010 cls : 'row roo-date-split-field-group',
34015 cls : 'form-hidden-field roo-date-split-field-group-value',
34021 var labelCls = 'col-md-12';
34022 var contentCls = 'col-md-4';
34024 if(this.fieldLabel){
34028 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34032 html : this.fieldLabel
34037 if(this.labelAlign == 'left'){
34039 if(this.labelWidth > 12){
34040 label.style = "width: " + this.labelWidth + 'px';
34043 if(this.labelWidth < 13 && this.labelmd == 0){
34044 this.labelmd = this.labelWidth;
34047 if(this.labellg > 0){
34048 labelCls = ' col-lg-' + this.labellg;
34049 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34052 if(this.labelmd > 0){
34053 labelCls = ' col-md-' + this.labelmd;
34054 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34057 if(this.labelsm > 0){
34058 labelCls = ' col-sm-' + this.labelsm;
34059 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34062 if(this.labelxs > 0){
34063 labelCls = ' col-xs-' + this.labelxs;
34064 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34068 label.cls += ' ' + labelCls;
34070 cfg.cn.push(label);
34073 Roo.each(['day', 'month', 'year'], function(t){
34076 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34083 inputEl: function ()
34085 return this.el.select('.roo-date-split-field-group-value', true).first();
34088 onRender : function(ct, position)
34092 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34094 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34096 this.dayField = new Roo.bootstrap.ComboBox({
34097 allowBlank : this.dayAllowBlank,
34098 alwaysQuery : true,
34099 displayField : 'value',
34102 forceSelection : true,
34104 placeholder : this.dayPlaceholder,
34105 selectOnFocus : true,
34106 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34107 triggerAction : 'all',
34109 valueField : 'value',
34110 store : new Roo.data.SimpleStore({
34111 data : (function() {
34113 _this.fireEvent('days', _this, days);
34116 fields : [ 'value' ]
34119 select : function (_self, record, index)
34121 _this.setValue(_this.getValue());
34126 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34128 this.monthField = new Roo.bootstrap.MonthField({
34129 after : '<i class=\"fa fa-calendar\"></i>',
34130 allowBlank : this.monthAllowBlank,
34131 placeholder : this.monthPlaceholder,
34134 render : function (_self)
34136 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34137 e.preventDefault();
34141 select : function (_self, oldvalue, newvalue)
34143 _this.setValue(_this.getValue());
34148 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34150 this.yearField = new Roo.bootstrap.ComboBox({
34151 allowBlank : this.yearAllowBlank,
34152 alwaysQuery : true,
34153 displayField : 'value',
34156 forceSelection : true,
34158 placeholder : this.yearPlaceholder,
34159 selectOnFocus : true,
34160 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34161 triggerAction : 'all',
34163 valueField : 'value',
34164 store : new Roo.data.SimpleStore({
34165 data : (function() {
34167 _this.fireEvent('years', _this, years);
34170 fields : [ 'value' ]
34173 select : function (_self, record, index)
34175 _this.setValue(_this.getValue());
34180 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34183 setValue : function(v, format)
34185 this.inputEl.dom.value = v;
34187 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34189 var d = Date.parseDate(v, f);
34196 this.setDay(d.format(this.dayFormat));
34197 this.setMonth(d.format(this.monthFormat));
34198 this.setYear(d.format(this.yearFormat));
34205 setDay : function(v)
34207 this.dayField.setValue(v);
34208 this.inputEl.dom.value = this.getValue();
34213 setMonth : function(v)
34215 this.monthField.setValue(v, true);
34216 this.inputEl.dom.value = this.getValue();
34221 setYear : function(v)
34223 this.yearField.setValue(v);
34224 this.inputEl.dom.value = this.getValue();
34229 getDay : function()
34231 return this.dayField.getValue();
34234 getMonth : function()
34236 return this.monthField.getValue();
34239 getYear : function()
34241 return this.yearField.getValue();
34244 getValue : function()
34246 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34248 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34258 this.inputEl.dom.value = '';
34263 validate : function()
34265 var d = this.dayField.validate();
34266 var m = this.monthField.validate();
34267 var y = this.yearField.validate();
34272 (!this.dayAllowBlank && !d) ||
34273 (!this.monthAllowBlank && !m) ||
34274 (!this.yearAllowBlank && !y)
34279 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34288 this.markInvalid();
34293 markValid : function()
34296 var label = this.el.select('label', true).first();
34297 var icon = this.el.select('i.fa-star', true).first();
34303 this.fireEvent('valid', this);
34307 * Mark this field as invalid
34308 * @param {String} msg The validation message
34310 markInvalid : function(msg)
34313 var label = this.el.select('label', true).first();
34314 var icon = this.el.select('i.fa-star', true).first();
34316 if(label && !icon){
34317 this.el.select('.roo-date-split-field-label', true).createChild({
34319 cls : 'text-danger fa fa-lg fa-star',
34320 tooltip : 'This field is required',
34321 style : 'margin-right:5px;'
34325 this.fireEvent('invalid', this, msg);
34328 clearInvalid : function()
34330 var label = this.el.select('label', true).first();
34331 var icon = this.el.select('i.fa-star', true).first();
34337 this.fireEvent('valid', this);
34340 getName: function()
34350 * http://masonry.desandro.com
34352 * The idea is to render all the bricks based on vertical width...
34354 * The original code extends 'outlayer' - we might need to use that....
34360 * @class Roo.bootstrap.LayoutMasonry
34361 * @extends Roo.bootstrap.Component
34362 * Bootstrap Layout Masonry class
34365 * Create a new Element
34366 * @param {Object} config The config object
34369 Roo.bootstrap.LayoutMasonry = function(config){
34371 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34375 Roo.bootstrap.LayoutMasonry.register(this);
34381 * Fire after layout the items
34382 * @param {Roo.bootstrap.LayoutMasonry} this
34383 * @param {Roo.EventObject} e
34390 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34393 * @cfg {Boolean} isLayoutInstant = no animation?
34395 isLayoutInstant : false, // needed?
34398 * @cfg {Number} boxWidth width of the columns
34403 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34408 * @cfg {Number} padWidth padding below box..
34413 * @cfg {Number} gutter gutter width..
34418 * @cfg {Number} maxCols maximum number of columns
34424 * @cfg {Boolean} isAutoInitial defalut true
34426 isAutoInitial : true,
34431 * @cfg {Boolean} isHorizontal defalut false
34433 isHorizontal : false,
34435 currentSize : null,
34441 bricks: null, //CompositeElement
34445 _isLayoutInited : false,
34447 // isAlternative : false, // only use for vertical layout...
34450 * @cfg {Number} alternativePadWidth padding below box..
34452 alternativePadWidth : 50,
34454 selectedBrick : [],
34456 getAutoCreate : function(){
34458 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34462 cls: 'blog-masonary-wrapper ' + this.cls,
34464 cls : 'mas-boxes masonary'
34471 getChildContainer: function( )
34473 if (this.boxesEl) {
34474 return this.boxesEl;
34477 this.boxesEl = this.el.select('.mas-boxes').first();
34479 return this.boxesEl;
34483 initEvents : function()
34487 if(this.isAutoInitial){
34488 Roo.log('hook children rendered');
34489 this.on('childrenrendered', function() {
34490 Roo.log('children rendered');
34496 initial : function()
34498 this.selectedBrick = [];
34500 this.currentSize = this.el.getBox(true);
34502 Roo.EventManager.onWindowResize(this.resize, this);
34504 if(!this.isAutoInitial){
34512 //this.layout.defer(500,this);
34516 resize : function()
34518 var cs = this.el.getBox(true);
34521 this.currentSize.width == cs.width &&
34522 this.currentSize.x == cs.x &&
34523 this.currentSize.height == cs.height &&
34524 this.currentSize.y == cs.y
34526 Roo.log("no change in with or X or Y");
34530 this.currentSize = cs;
34536 layout : function()
34538 this._resetLayout();
34540 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34542 this.layoutItems( isInstant );
34544 this._isLayoutInited = true;
34546 this.fireEvent('layout', this);
34550 _resetLayout : function()
34552 if(this.isHorizontal){
34553 this.horizontalMeasureColumns();
34557 this.verticalMeasureColumns();
34561 verticalMeasureColumns : function()
34563 this.getContainerWidth();
34565 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34566 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34570 var boxWidth = this.boxWidth + this.padWidth;
34572 if(this.containerWidth < this.boxWidth){
34573 boxWidth = this.containerWidth
34576 var containerWidth = this.containerWidth;
34578 var cols = Math.floor(containerWidth / boxWidth);
34580 this.cols = Math.max( cols, 1 );
34582 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34584 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34586 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34588 this.colWidth = boxWidth + avail - this.padWidth;
34590 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34591 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34594 horizontalMeasureColumns : function()
34596 this.getContainerWidth();
34598 var boxWidth = this.boxWidth;
34600 if(this.containerWidth < boxWidth){
34601 boxWidth = this.containerWidth;
34604 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34606 this.el.setHeight(boxWidth);
34610 getContainerWidth : function()
34612 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34615 layoutItems : function( isInstant )
34617 Roo.log(this.bricks);
34619 var items = Roo.apply([], this.bricks);
34621 if(this.isHorizontal){
34622 this._horizontalLayoutItems( items , isInstant );
34626 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34627 // this._verticalAlternativeLayoutItems( items , isInstant );
34631 this._verticalLayoutItems( items , isInstant );
34635 _verticalLayoutItems : function ( items , isInstant)
34637 if ( !items || !items.length ) {
34642 ['xs', 'xs', 'xs', 'tall'],
34643 ['xs', 'xs', 'tall'],
34644 ['xs', 'xs', 'sm'],
34645 ['xs', 'xs', 'xs'],
34651 ['sm', 'xs', 'xs'],
34655 ['tall', 'xs', 'xs', 'xs'],
34656 ['tall', 'xs', 'xs'],
34668 Roo.each(items, function(item, k){
34670 switch (item.size) {
34671 // these layouts take up a full box,
34682 boxes.push([item]);
34705 var filterPattern = function(box, length)
34713 var pattern = box.slice(0, length);
34717 Roo.each(pattern, function(i){
34718 format.push(i.size);
34721 Roo.each(standard, function(s){
34723 if(String(s) != String(format)){
34732 if(!match && length == 1){
34737 filterPattern(box, length - 1);
34741 queue.push(pattern);
34743 box = box.slice(length, box.length);
34745 filterPattern(box, 4);
34751 Roo.each(boxes, function(box, k){
34757 if(box.length == 1){
34762 filterPattern(box, 4);
34766 this._processVerticalLayoutQueue( queue, isInstant );
34770 // _verticalAlternativeLayoutItems : function( items , isInstant )
34772 // if ( !items || !items.length ) {
34776 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34780 _horizontalLayoutItems : function ( items , isInstant)
34782 if ( !items || !items.length || items.length < 3) {
34788 var eItems = items.slice(0, 3);
34790 items = items.slice(3, items.length);
34793 ['xs', 'xs', 'xs', 'wide'],
34794 ['xs', 'xs', 'wide'],
34795 ['xs', 'xs', 'sm'],
34796 ['xs', 'xs', 'xs'],
34802 ['sm', 'xs', 'xs'],
34806 ['wide', 'xs', 'xs', 'xs'],
34807 ['wide', 'xs', 'xs'],
34820 Roo.each(items, function(item, k){
34822 switch (item.size) {
34833 boxes.push([item]);
34857 var filterPattern = function(box, length)
34865 var pattern = box.slice(0, length);
34869 Roo.each(pattern, function(i){
34870 format.push(i.size);
34873 Roo.each(standard, function(s){
34875 if(String(s) != String(format)){
34884 if(!match && length == 1){
34889 filterPattern(box, length - 1);
34893 queue.push(pattern);
34895 box = box.slice(length, box.length);
34897 filterPattern(box, 4);
34903 Roo.each(boxes, function(box, k){
34909 if(box.length == 1){
34914 filterPattern(box, 4);
34921 var pos = this.el.getBox(true);
34925 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34927 var hit_end = false;
34929 Roo.each(queue, function(box){
34933 Roo.each(box, function(b){
34935 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34945 Roo.each(box, function(b){
34947 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34950 mx = Math.max(mx, b.x);
34954 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34958 Roo.each(box, function(b){
34960 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34974 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34977 /** Sets position of item in DOM
34978 * @param {Element} item
34979 * @param {Number} x - horizontal position
34980 * @param {Number} y - vertical position
34981 * @param {Boolean} isInstant - disables transitions
34983 _processVerticalLayoutQueue : function( queue, isInstant )
34985 var pos = this.el.getBox(true);
34990 for (var i = 0; i < this.cols; i++){
34994 Roo.each(queue, function(box, k){
34996 var col = k % this.cols;
34998 Roo.each(box, function(b,kk){
35000 b.el.position('absolute');
35002 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35003 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35005 if(b.size == 'md-left' || b.size == 'md-right'){
35006 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35007 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35010 b.el.setWidth(width);
35011 b.el.setHeight(height);
35013 b.el.select('iframe',true).setSize(width,height);
35017 for (var i = 0; i < this.cols; i++){
35019 if(maxY[i] < maxY[col]){
35024 col = Math.min(col, i);
35028 x = pos.x + col * (this.colWidth + this.padWidth);
35032 var positions = [];
35034 switch (box.length){
35036 positions = this.getVerticalOneBoxColPositions(x, y, box);
35039 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35042 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35045 positions = this.getVerticalFourBoxColPositions(x, y, box);
35051 Roo.each(box, function(b,kk){
35053 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35055 var sz = b.el.getSize();
35057 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35065 for (var i = 0; i < this.cols; i++){
35066 mY = Math.max(mY, maxY[i]);
35069 this.el.setHeight(mY - pos.y);
35073 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35075 // var pos = this.el.getBox(true);
35078 // var maxX = pos.right;
35080 // var maxHeight = 0;
35082 // Roo.each(items, function(item, k){
35086 // item.el.position('absolute');
35088 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35090 // item.el.setWidth(width);
35092 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35094 // item.el.setHeight(height);
35097 // item.el.setXY([x, y], isInstant ? false : true);
35099 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35102 // y = y + height + this.alternativePadWidth;
35104 // maxHeight = maxHeight + height + this.alternativePadWidth;
35108 // this.el.setHeight(maxHeight);
35112 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35114 var pos = this.el.getBox(true);
35119 var maxX = pos.right;
35121 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35123 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35125 Roo.each(queue, function(box, k){
35127 Roo.each(box, function(b, kk){
35129 b.el.position('absolute');
35131 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35132 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35134 if(b.size == 'md-left' || b.size == 'md-right'){
35135 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35136 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35139 b.el.setWidth(width);
35140 b.el.setHeight(height);
35148 var positions = [];
35150 switch (box.length){
35152 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35155 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35158 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35161 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35167 Roo.each(box, function(b,kk){
35169 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35171 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35179 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35181 Roo.each(eItems, function(b,k){
35183 b.size = (k == 0) ? 'sm' : 'xs';
35184 b.x = (k == 0) ? 2 : 1;
35185 b.y = (k == 0) ? 2 : 1;
35187 b.el.position('absolute');
35189 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35191 b.el.setWidth(width);
35193 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35195 b.el.setHeight(height);
35199 var positions = [];
35202 x : maxX - this.unitWidth * 2 - this.gutter,
35207 x : maxX - this.unitWidth,
35208 y : minY + (this.unitWidth + this.gutter) * 2
35212 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35216 Roo.each(eItems, function(b,k){
35218 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35224 getVerticalOneBoxColPositions : function(x, y, box)
35228 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35230 if(box[0].size == 'md-left'){
35234 if(box[0].size == 'md-right'){
35239 x : x + (this.unitWidth + this.gutter) * rand,
35246 getVerticalTwoBoxColPositions : function(x, y, box)
35250 if(box[0].size == 'xs'){
35254 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35258 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35272 x : x + (this.unitWidth + this.gutter) * 2,
35273 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35280 getVerticalThreeBoxColPositions : function(x, y, box)
35284 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35292 x : x + (this.unitWidth + this.gutter) * 1,
35297 x : x + (this.unitWidth + this.gutter) * 2,
35305 if(box[0].size == 'xs' && box[1].size == 'xs'){
35314 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35318 x : x + (this.unitWidth + this.gutter) * 1,
35332 x : x + (this.unitWidth + this.gutter) * 2,
35337 x : x + (this.unitWidth + this.gutter) * 2,
35338 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35345 getVerticalFourBoxColPositions : function(x, y, box)
35349 if(box[0].size == 'xs'){
35358 y : y + (this.unitHeight + this.gutter) * 1
35363 y : y + (this.unitHeight + this.gutter) * 2
35367 x : x + (this.unitWidth + this.gutter) * 1,
35381 x : x + (this.unitWidth + this.gutter) * 2,
35386 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35387 y : y + (this.unitHeight + this.gutter) * 1
35391 x : x + (this.unitWidth + this.gutter) * 2,
35392 y : y + (this.unitWidth + this.gutter) * 2
35399 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35403 if(box[0].size == 'md-left'){
35405 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35412 if(box[0].size == 'md-right'){
35414 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35415 y : minY + (this.unitWidth + this.gutter) * 1
35421 var rand = Math.floor(Math.random() * (4 - box[0].y));
35424 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35425 y : minY + (this.unitWidth + this.gutter) * rand
35432 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35436 if(box[0].size == 'xs'){
35439 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35444 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35445 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35453 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35458 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35459 y : minY + (this.unitWidth + this.gutter) * 2
35466 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35470 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35473 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35478 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35479 y : minY + (this.unitWidth + this.gutter) * 1
35483 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35484 y : minY + (this.unitWidth + this.gutter) * 2
35491 if(box[0].size == 'xs' && box[1].size == 'xs'){
35494 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35499 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35504 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35505 y : minY + (this.unitWidth + this.gutter) * 1
35513 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35518 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35519 y : minY + (this.unitWidth + this.gutter) * 2
35523 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35524 y : minY + (this.unitWidth + this.gutter) * 2
35531 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35535 if(box[0].size == 'xs'){
35538 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35543 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35548 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),
35553 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35554 y : minY + (this.unitWidth + this.gutter) * 1
35562 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35567 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35568 y : minY + (this.unitWidth + this.gutter) * 2
35572 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35573 y : minY + (this.unitWidth + this.gutter) * 2
35577 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),
35578 y : minY + (this.unitWidth + this.gutter) * 2
35586 * remove a Masonry Brick
35587 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35589 removeBrick : function(brick_id)
35595 for (var i = 0; i<this.bricks.length; i++) {
35596 if (this.bricks[i].id == brick_id) {
35597 this.bricks.splice(i,1);
35598 this.el.dom.removeChild(Roo.get(brick_id).dom);
35605 * adds a Masonry Brick
35606 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35608 addBrick : function(cfg)
35610 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35611 //this.register(cn);
35612 cn.parentId = this.id;
35613 cn.render(this.el);
35618 * register a Masonry Brick
35619 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35622 register : function(brick)
35624 this.bricks.push(brick);
35625 brick.masonryId = this.id;
35629 * clear all the Masonry Brick
35631 clearAll : function()
35634 //this.getChildContainer().dom.innerHTML = "";
35635 this.el.dom.innerHTML = '';
35638 getSelected : function()
35640 if (!this.selectedBrick) {
35644 return this.selectedBrick;
35648 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35652 * register a Masonry Layout
35653 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35656 register : function(layout)
35658 this.groups[layout.id] = layout;
35661 * fetch a Masonry Layout based on the masonry layout ID
35662 * @param {string} the masonry layout to add
35663 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35666 get: function(layout_id) {
35667 if (typeof(this.groups[layout_id]) == 'undefined') {
35670 return this.groups[layout_id] ;
35682 * http://masonry.desandro.com
35684 * The idea is to render all the bricks based on vertical width...
35686 * The original code extends 'outlayer' - we might need to use that....
35692 * @class Roo.bootstrap.LayoutMasonryAuto
35693 * @extends Roo.bootstrap.Component
35694 * Bootstrap Layout Masonry class
35697 * Create a new Element
35698 * @param {Object} config The config object
35701 Roo.bootstrap.LayoutMasonryAuto = function(config){
35702 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35705 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35708 * @cfg {Boolean} isFitWidth - resize the width..
35710 isFitWidth : false, // options..
35712 * @cfg {Boolean} isOriginLeft = left align?
35714 isOriginLeft : true,
35716 * @cfg {Boolean} isOriginTop = top align?
35718 isOriginTop : false,
35720 * @cfg {Boolean} isLayoutInstant = no animation?
35722 isLayoutInstant : false, // needed?
35724 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35726 isResizingContainer : true,
35728 * @cfg {Number} columnWidth width of the columns
35734 * @cfg {Number} maxCols maximum number of columns
35739 * @cfg {Number} padHeight padding below box..
35745 * @cfg {Boolean} isAutoInitial defalut true
35748 isAutoInitial : true,
35754 initialColumnWidth : 0,
35755 currentSize : null,
35757 colYs : null, // array.
35764 bricks: null, //CompositeElement
35765 cols : 0, // array?
35766 // element : null, // wrapped now this.el
35767 _isLayoutInited : null,
35770 getAutoCreate : function(){
35774 cls: 'blog-masonary-wrapper ' + this.cls,
35776 cls : 'mas-boxes masonary'
35783 getChildContainer: function( )
35785 if (this.boxesEl) {
35786 return this.boxesEl;
35789 this.boxesEl = this.el.select('.mas-boxes').first();
35791 return this.boxesEl;
35795 initEvents : function()
35799 if(this.isAutoInitial){
35800 Roo.log('hook children rendered');
35801 this.on('childrenrendered', function() {
35802 Roo.log('children rendered');
35809 initial : function()
35811 this.reloadItems();
35813 this.currentSize = this.el.getBox(true);
35815 /// was window resize... - let's see if this works..
35816 Roo.EventManager.onWindowResize(this.resize, this);
35818 if(!this.isAutoInitial){
35823 this.layout.defer(500,this);
35826 reloadItems: function()
35828 this.bricks = this.el.select('.masonry-brick', true);
35830 this.bricks.each(function(b) {
35831 //Roo.log(b.getSize());
35832 if (!b.attr('originalwidth')) {
35833 b.attr('originalwidth', b.getSize().width);
35838 Roo.log(this.bricks.elements.length);
35841 resize : function()
35844 var cs = this.el.getBox(true);
35846 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35847 Roo.log("no change in with or X");
35850 this.currentSize = cs;
35854 layout : function()
35857 this._resetLayout();
35858 //this._manageStamps();
35860 // don't animate first layout
35861 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35862 this.layoutItems( isInstant );
35864 // flag for initalized
35865 this._isLayoutInited = true;
35868 layoutItems : function( isInstant )
35870 //var items = this._getItemsForLayout( this.items );
35871 // original code supports filtering layout items.. we just ignore it..
35873 this._layoutItems( this.bricks , isInstant );
35875 this._postLayout();
35877 _layoutItems : function ( items , isInstant)
35879 //this.fireEvent( 'layout', this, items );
35882 if ( !items || !items.elements.length ) {
35883 // no items, emit event with empty array
35888 items.each(function(item) {
35889 Roo.log("layout item");
35891 // get x/y object from method
35892 var position = this._getItemLayoutPosition( item );
35894 position.item = item;
35895 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35896 queue.push( position );
35899 this._processLayoutQueue( queue );
35901 /** Sets position of item in DOM
35902 * @param {Element} item
35903 * @param {Number} x - horizontal position
35904 * @param {Number} y - vertical position
35905 * @param {Boolean} isInstant - disables transitions
35907 _processLayoutQueue : function( queue )
35909 for ( var i=0, len = queue.length; i < len; i++ ) {
35910 var obj = queue[i];
35911 obj.item.position('absolute');
35912 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35918 * Any logic you want to do after each layout,
35919 * i.e. size the container
35921 _postLayout : function()
35923 this.resizeContainer();
35926 resizeContainer : function()
35928 if ( !this.isResizingContainer ) {
35931 var size = this._getContainerSize();
35933 this.el.setSize(size.width,size.height);
35934 this.boxesEl.setSize(size.width,size.height);
35940 _resetLayout : function()
35942 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35943 this.colWidth = this.el.getWidth();
35944 //this.gutter = this.el.getWidth();
35946 this.measureColumns();
35952 this.colYs.push( 0 );
35958 measureColumns : function()
35960 this.getContainerWidth();
35961 // if columnWidth is 0, default to outerWidth of first item
35962 if ( !this.columnWidth ) {
35963 var firstItem = this.bricks.first();
35964 Roo.log(firstItem);
35965 this.columnWidth = this.containerWidth;
35966 if (firstItem && firstItem.attr('originalwidth') ) {
35967 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35969 // columnWidth fall back to item of first element
35970 Roo.log("set column width?");
35971 this.initialColumnWidth = this.columnWidth ;
35973 // if first elem has no width, default to size of container
35978 if (this.initialColumnWidth) {
35979 this.columnWidth = this.initialColumnWidth;
35984 // column width is fixed at the top - however if container width get's smaller we should
35987 // this bit calcs how man columns..
35989 var columnWidth = this.columnWidth += this.gutter;
35991 // calculate columns
35992 var containerWidth = this.containerWidth + this.gutter;
35994 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35995 // fix rounding errors, typically with gutters
35996 var excess = columnWidth - containerWidth % columnWidth;
35999 // if overshoot is less than a pixel, round up, otherwise floor it
36000 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36001 cols = Math[ mathMethod ]( cols );
36002 this.cols = Math.max( cols, 1 );
36003 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36005 // padding positioning..
36006 var totalColWidth = this.cols * this.columnWidth;
36007 var padavail = this.containerWidth - totalColWidth;
36008 // so for 2 columns - we need 3 'pads'
36010 var padNeeded = (1+this.cols) * this.padWidth;
36012 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36014 this.columnWidth += padExtra
36015 //this.padWidth = Math.floor(padavail / ( this.cols));
36017 // adjust colum width so that padding is fixed??
36019 // we have 3 columns ... total = width * 3
36020 // we have X left over... that should be used by
36022 //if (this.expandC) {
36030 getContainerWidth : function()
36032 /* // container is parent if fit width
36033 var container = this.isFitWidth ? this.element.parentNode : this.element;
36034 // check that this.size and size are there
36035 // IE8 triggers resize on body size change, so they might not be
36037 var size = getSize( container ); //FIXME
36038 this.containerWidth = size && size.innerWidth; //FIXME
36041 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36045 _getItemLayoutPosition : function( item ) // what is item?
36047 // we resize the item to our columnWidth..
36049 item.setWidth(this.columnWidth);
36050 item.autoBoxAdjust = false;
36052 var sz = item.getSize();
36054 // how many columns does this brick span
36055 var remainder = this.containerWidth % this.columnWidth;
36057 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36058 // round if off by 1 pixel, otherwise use ceil
36059 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36060 colSpan = Math.min( colSpan, this.cols );
36062 // normally this should be '1' as we dont' currently allow multi width columns..
36064 var colGroup = this._getColGroup( colSpan );
36065 // get the minimum Y value from the columns
36066 var minimumY = Math.min.apply( Math, colGroup );
36067 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36069 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36071 // position the brick
36073 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36074 y: this.currentSize.y + minimumY + this.padHeight
36078 // apply setHeight to necessary columns
36079 var setHeight = minimumY + sz.height + this.padHeight;
36080 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36082 var setSpan = this.cols + 1 - colGroup.length;
36083 for ( var i = 0; i < setSpan; i++ ) {
36084 this.colYs[ shortColIndex + i ] = setHeight ;
36091 * @param {Number} colSpan - number of columns the element spans
36092 * @returns {Array} colGroup
36094 _getColGroup : function( colSpan )
36096 if ( colSpan < 2 ) {
36097 // if brick spans only one column, use all the column Ys
36102 // how many different places could this brick fit horizontally
36103 var groupCount = this.cols + 1 - colSpan;
36104 // for each group potential horizontal position
36105 for ( var i = 0; i < groupCount; i++ ) {
36106 // make an array of colY values for that one group
36107 var groupColYs = this.colYs.slice( i, i + colSpan );
36108 // and get the max value of the array
36109 colGroup[i] = Math.max.apply( Math, groupColYs );
36114 _manageStamp : function( stamp )
36116 var stampSize = stamp.getSize();
36117 var offset = stamp.getBox();
36118 // get the columns that this stamp affects
36119 var firstX = this.isOriginLeft ? offset.x : offset.right;
36120 var lastX = firstX + stampSize.width;
36121 var firstCol = Math.floor( firstX / this.columnWidth );
36122 firstCol = Math.max( 0, firstCol );
36124 var lastCol = Math.floor( lastX / this.columnWidth );
36125 // lastCol should not go over if multiple of columnWidth #425
36126 lastCol -= lastX % this.columnWidth ? 0 : 1;
36127 lastCol = Math.min( this.cols - 1, lastCol );
36129 // set colYs to bottom of the stamp
36130 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36133 for ( var i = firstCol; i <= lastCol; i++ ) {
36134 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36139 _getContainerSize : function()
36141 this.maxY = Math.max.apply( Math, this.colYs );
36146 if ( this.isFitWidth ) {
36147 size.width = this._getContainerFitWidth();
36153 _getContainerFitWidth : function()
36155 var unusedCols = 0;
36156 // count unused columns
36159 if ( this.colYs[i] !== 0 ) {
36164 // fit container to columns that have been used
36165 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36168 needsResizeLayout : function()
36170 var previousWidth = this.containerWidth;
36171 this.getContainerWidth();
36172 return previousWidth !== this.containerWidth;
36187 * @class Roo.bootstrap.MasonryBrick
36188 * @extends Roo.bootstrap.Component
36189 * Bootstrap MasonryBrick class
36192 * Create a new MasonryBrick
36193 * @param {Object} config The config object
36196 Roo.bootstrap.MasonryBrick = function(config){
36198 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36200 Roo.bootstrap.MasonryBrick.register(this);
36206 * When a MasonryBrick is clcik
36207 * @param {Roo.bootstrap.MasonryBrick} this
36208 * @param {Roo.EventObject} e
36214 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36217 * @cfg {String} title
36221 * @cfg {String} html
36225 * @cfg {String} bgimage
36229 * @cfg {String} videourl
36233 * @cfg {String} cls
36237 * @cfg {String} href
36241 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36246 * @cfg {String} placetitle (center|bottom)
36251 * @cfg {Boolean} isFitContainer defalut true
36253 isFitContainer : true,
36256 * @cfg {Boolean} preventDefault defalut false
36258 preventDefault : false,
36261 * @cfg {Boolean} inverse defalut false
36263 maskInverse : false,
36265 getAutoCreate : function()
36267 if(!this.isFitContainer){
36268 return this.getSplitAutoCreate();
36271 var cls = 'masonry-brick masonry-brick-full';
36273 if(this.href.length){
36274 cls += ' masonry-brick-link';
36277 if(this.bgimage.length){
36278 cls += ' masonry-brick-image';
36281 if(this.maskInverse){
36282 cls += ' mask-inverse';
36285 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36286 cls += ' enable-mask';
36290 cls += ' masonry-' + this.size + '-brick';
36293 if(this.placetitle.length){
36295 switch (this.placetitle) {
36297 cls += ' masonry-center-title';
36300 cls += ' masonry-bottom-title';
36307 if(!this.html.length && !this.bgimage.length){
36308 cls += ' masonry-center-title';
36311 if(!this.html.length && this.bgimage.length){
36312 cls += ' masonry-bottom-title';
36317 cls += ' ' + this.cls;
36321 tag: (this.href.length) ? 'a' : 'div',
36326 cls: 'masonry-brick-mask'
36330 cls: 'masonry-brick-paragraph',
36336 if(this.href.length){
36337 cfg.href = this.href;
36340 var cn = cfg.cn[1].cn;
36342 if(this.title.length){
36345 cls: 'masonry-brick-title',
36350 if(this.html.length){
36353 cls: 'masonry-brick-text',
36358 if (!this.title.length && !this.html.length) {
36359 cfg.cn[1].cls += ' hide';
36362 if(this.bgimage.length){
36365 cls: 'masonry-brick-image-view',
36370 if(this.videourl.length){
36371 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36372 // youtube support only?
36375 cls: 'masonry-brick-image-view',
36378 allowfullscreen : true
36386 getSplitAutoCreate : function()
36388 var cls = 'masonry-brick masonry-brick-split';
36390 if(this.href.length){
36391 cls += ' masonry-brick-link';
36394 if(this.bgimage.length){
36395 cls += ' masonry-brick-image';
36399 cls += ' masonry-' + this.size + '-brick';
36402 switch (this.placetitle) {
36404 cls += ' masonry-center-title';
36407 cls += ' masonry-bottom-title';
36410 if(!this.bgimage.length){
36411 cls += ' masonry-center-title';
36414 if(this.bgimage.length){
36415 cls += ' masonry-bottom-title';
36421 cls += ' ' + this.cls;
36425 tag: (this.href.length) ? 'a' : 'div',
36430 cls: 'masonry-brick-split-head',
36434 cls: 'masonry-brick-paragraph',
36441 cls: 'masonry-brick-split-body',
36447 if(this.href.length){
36448 cfg.href = this.href;
36451 if(this.title.length){
36452 cfg.cn[0].cn[0].cn.push({
36454 cls: 'masonry-brick-title',
36459 if(this.html.length){
36460 cfg.cn[1].cn.push({
36462 cls: 'masonry-brick-text',
36467 if(this.bgimage.length){
36468 cfg.cn[0].cn.push({
36470 cls: 'masonry-brick-image-view',
36475 if(this.videourl.length){
36476 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36477 // youtube support only?
36478 cfg.cn[0].cn.cn.push({
36480 cls: 'masonry-brick-image-view',
36483 allowfullscreen : true
36490 initEvents: function()
36492 switch (this.size) {
36525 this.el.on('touchstart', this.onTouchStart, this);
36526 this.el.on('touchmove', this.onTouchMove, this);
36527 this.el.on('touchend', this.onTouchEnd, this);
36528 this.el.on('contextmenu', this.onContextMenu, this);
36530 this.el.on('mouseenter' ,this.enter, this);
36531 this.el.on('mouseleave', this.leave, this);
36532 this.el.on('click', this.onClick, this);
36535 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36536 this.parent().bricks.push(this);
36541 onClick: function(e, el)
36543 var time = this.endTimer - this.startTimer;
36544 // Roo.log(e.preventDefault());
36547 e.preventDefault();
36552 if(!this.preventDefault){
36556 e.preventDefault();
36558 if (this.activeClass != '') {
36559 this.selectBrick();
36562 this.fireEvent('click', this, e);
36565 enter: function(e, el)
36567 e.preventDefault();
36569 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36573 if(this.bgimage.length && this.html.length){
36574 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36578 leave: function(e, el)
36580 e.preventDefault();
36582 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36586 if(this.bgimage.length && this.html.length){
36587 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36591 onTouchStart: function(e, el)
36593 // e.preventDefault();
36595 this.touchmoved = false;
36597 if(!this.isFitContainer){
36601 if(!this.bgimage.length || !this.html.length){
36605 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36607 this.timer = new Date().getTime();
36611 onTouchMove: function(e, el)
36613 this.touchmoved = true;
36616 onContextMenu : function(e,el)
36618 e.preventDefault();
36619 e.stopPropagation();
36623 onTouchEnd: function(e, el)
36625 // e.preventDefault();
36627 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36634 if(!this.bgimage.length || !this.html.length){
36636 if(this.href.length){
36637 window.location.href = this.href;
36643 if(!this.isFitContainer){
36647 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36649 window.location.href = this.href;
36652 //selection on single brick only
36653 selectBrick : function() {
36655 if (!this.parentId) {
36659 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36660 var index = m.selectedBrick.indexOf(this.id);
36663 m.selectedBrick.splice(index,1);
36664 this.el.removeClass(this.activeClass);
36668 for(var i = 0; i < m.selectedBrick.length; i++) {
36669 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36670 b.el.removeClass(b.activeClass);
36673 m.selectedBrick = [];
36675 m.selectedBrick.push(this.id);
36676 this.el.addClass(this.activeClass);
36680 isSelected : function(){
36681 return this.el.hasClass(this.activeClass);
36686 Roo.apply(Roo.bootstrap.MasonryBrick, {
36689 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36691 * register a Masonry Brick
36692 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36695 register : function(brick)
36697 //this.groups[brick.id] = brick;
36698 this.groups.add(brick.id, brick);
36701 * fetch a masonry brick based on the masonry brick ID
36702 * @param {string} the masonry brick to add
36703 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36706 get: function(brick_id)
36708 // if (typeof(this.groups[brick_id]) == 'undefined') {
36711 // return this.groups[brick_id] ;
36713 if(this.groups.key(brick_id)) {
36714 return this.groups.key(brick_id);
36732 * @class Roo.bootstrap.Brick
36733 * @extends Roo.bootstrap.Component
36734 * Bootstrap Brick class
36737 * Create a new Brick
36738 * @param {Object} config The config object
36741 Roo.bootstrap.Brick = function(config){
36742 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36748 * When a Brick is click
36749 * @param {Roo.bootstrap.Brick} this
36750 * @param {Roo.EventObject} e
36756 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36759 * @cfg {String} title
36763 * @cfg {String} html
36767 * @cfg {String} bgimage
36771 * @cfg {String} cls
36775 * @cfg {String} href
36779 * @cfg {String} video
36783 * @cfg {Boolean} square
36787 getAutoCreate : function()
36789 var cls = 'roo-brick';
36791 if(this.href.length){
36792 cls += ' roo-brick-link';
36795 if(this.bgimage.length){
36796 cls += ' roo-brick-image';
36799 if(!this.html.length && !this.bgimage.length){
36800 cls += ' roo-brick-center-title';
36803 if(!this.html.length && this.bgimage.length){
36804 cls += ' roo-brick-bottom-title';
36808 cls += ' ' + this.cls;
36812 tag: (this.href.length) ? 'a' : 'div',
36817 cls: 'roo-brick-paragraph',
36823 if(this.href.length){
36824 cfg.href = this.href;
36827 var cn = cfg.cn[0].cn;
36829 if(this.title.length){
36832 cls: 'roo-brick-title',
36837 if(this.html.length){
36840 cls: 'roo-brick-text',
36847 if(this.bgimage.length){
36850 cls: 'roo-brick-image-view',
36858 initEvents: function()
36860 if(this.title.length || this.html.length){
36861 this.el.on('mouseenter' ,this.enter, this);
36862 this.el.on('mouseleave', this.leave, this);
36865 Roo.EventManager.onWindowResize(this.resize, this);
36867 if(this.bgimage.length){
36868 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36869 this.imageEl.on('load', this.onImageLoad, this);
36876 onImageLoad : function()
36881 resize : function()
36883 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36885 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36887 if(this.bgimage.length){
36888 var image = this.el.select('.roo-brick-image-view', true).first();
36890 image.setWidth(paragraph.getWidth());
36893 image.setHeight(paragraph.getWidth());
36896 this.el.setHeight(image.getHeight());
36897 paragraph.setHeight(image.getHeight());
36903 enter: function(e, el)
36905 e.preventDefault();
36907 if(this.bgimage.length){
36908 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36909 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36913 leave: function(e, el)
36915 e.preventDefault();
36917 if(this.bgimage.length){
36918 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36919 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36934 * @class Roo.bootstrap.NumberField
36935 * @extends Roo.bootstrap.Input
36936 * Bootstrap NumberField class
36942 * Create a new NumberField
36943 * @param {Object} config The config object
36946 Roo.bootstrap.NumberField = function(config){
36947 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36950 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36953 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36955 allowDecimals : true,
36957 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36959 decimalSeparator : ".",
36961 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36963 decimalPrecision : 2,
36965 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36967 allowNegative : true,
36970 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36974 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36976 minValue : Number.NEGATIVE_INFINITY,
36978 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36980 maxValue : Number.MAX_VALUE,
36982 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36984 minText : "The minimum value for this field is {0}",
36986 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36988 maxText : "The maximum value for this field is {0}",
36990 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36991 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36993 nanText : "{0} is not a valid number",
36995 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36997 thousandsDelimiter : false,
36999 * @cfg {String} valueAlign alignment of value
37001 valueAlign : "left",
37003 getAutoCreate : function()
37005 var hiddenInput = {
37009 cls: 'hidden-number-input'
37013 hiddenInput.name = this.name;
37018 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37020 this.name = hiddenInput.name;
37022 if(cfg.cn.length > 0) {
37023 cfg.cn.push(hiddenInput);
37030 initEvents : function()
37032 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37034 var allowed = "0123456789";
37036 if(this.allowDecimals){
37037 allowed += this.decimalSeparator;
37040 if(this.allowNegative){
37044 if(this.thousandsDelimiter) {
37048 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37050 var keyPress = function(e){
37052 var k = e.getKey();
37054 var c = e.getCharCode();
37057 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37058 allowed.indexOf(String.fromCharCode(c)) === -1
37064 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37068 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37073 this.el.on("keypress", keyPress, this);
37076 validateValue : function(value)
37079 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37083 var num = this.parseValue(value);
37086 this.markInvalid(String.format(this.nanText, value));
37090 if(num < this.minValue){
37091 this.markInvalid(String.format(this.minText, this.minValue));
37095 if(num > this.maxValue){
37096 this.markInvalid(String.format(this.maxText, this.maxValue));
37103 getValue : function()
37105 var v = this.hiddenEl().getValue();
37107 return this.fixPrecision(this.parseValue(v));
37110 parseValue : function(value)
37112 if(this.thousandsDelimiter) {
37114 r = new RegExp(",", "g");
37115 value = value.replace(r, "");
37118 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37119 return isNaN(value) ? '' : value;
37122 fixPrecision : function(value)
37124 if(this.thousandsDelimiter) {
37126 r = new RegExp(",", "g");
37127 value = value.replace(r, "");
37130 var nan = isNaN(value);
37132 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37133 return nan ? '' : value;
37135 return parseFloat(value).toFixed(this.decimalPrecision);
37138 setValue : function(v)
37140 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37146 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37148 this.inputEl().dom.value = (v == '') ? '' :
37149 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37151 if(!this.allowZero && v === '0') {
37152 this.hiddenEl().dom.value = '';
37153 this.inputEl().dom.value = '';
37160 decimalPrecisionFcn : function(v)
37162 return Math.floor(v);
37165 beforeBlur : function()
37167 var v = this.parseValue(this.getRawValue());
37169 if(v || v === 0 || v === ''){
37174 hiddenEl : function()
37176 return this.el.select('input.hidden-number-input',true).first();
37188 * @class Roo.bootstrap.DocumentSlider
37189 * @extends Roo.bootstrap.Component
37190 * Bootstrap DocumentSlider class
37193 * Create a new DocumentViewer
37194 * @param {Object} config The config object
37197 Roo.bootstrap.DocumentSlider = function(config){
37198 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37205 * Fire after initEvent
37206 * @param {Roo.bootstrap.DocumentSlider} this
37211 * Fire after update
37212 * @param {Roo.bootstrap.DocumentSlider} this
37218 * @param {Roo.bootstrap.DocumentSlider} this
37224 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37230 getAutoCreate : function()
37234 cls : 'roo-document-slider',
37238 cls : 'roo-document-slider-header',
37242 cls : 'roo-document-slider-header-title'
37248 cls : 'roo-document-slider-body',
37252 cls : 'roo-document-slider-prev',
37256 cls : 'fa fa-chevron-left'
37262 cls : 'roo-document-slider-thumb',
37266 cls : 'roo-document-slider-image'
37272 cls : 'roo-document-slider-next',
37276 cls : 'fa fa-chevron-right'
37288 initEvents : function()
37290 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37291 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37293 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37294 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37296 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37297 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37299 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37300 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37302 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37303 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37305 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37306 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37308 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37309 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37311 this.thumbEl.on('click', this.onClick, this);
37313 this.prevIndicator.on('click', this.prev, this);
37315 this.nextIndicator.on('click', this.next, this);
37319 initial : function()
37321 if(this.files.length){
37322 this.indicator = 1;
37326 this.fireEvent('initial', this);
37329 update : function()
37331 this.imageEl.attr('src', this.files[this.indicator - 1]);
37333 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37335 this.prevIndicator.show();
37337 if(this.indicator == 1){
37338 this.prevIndicator.hide();
37341 this.nextIndicator.show();
37343 if(this.indicator == this.files.length){
37344 this.nextIndicator.hide();
37347 this.thumbEl.scrollTo('top');
37349 this.fireEvent('update', this);
37352 onClick : function(e)
37354 e.preventDefault();
37356 this.fireEvent('click', this);
37361 e.preventDefault();
37363 this.indicator = Math.max(1, this.indicator - 1);
37370 e.preventDefault();
37372 this.indicator = Math.min(this.files.length, this.indicator + 1);
37386 * @class Roo.bootstrap.RadioSet
37387 * @extends Roo.bootstrap.Input
37388 * Bootstrap RadioSet class
37389 * @cfg {String} indicatorpos (left|right) default left
37390 * @cfg {Boolean} inline (true|false) inline the element (default true)
37391 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37393 * Create a new RadioSet
37394 * @param {Object} config The config object
37397 Roo.bootstrap.RadioSet = function(config){
37399 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37403 Roo.bootstrap.RadioSet.register(this);
37408 * Fires when the element is checked or unchecked.
37409 * @param {Roo.bootstrap.RadioSet} this This radio
37410 * @param {Roo.bootstrap.Radio} item The checked item
37415 * Fires when the element is click.
37416 * @param {Roo.bootstrap.RadioSet} this This radio set
37417 * @param {Roo.bootstrap.Radio} item The checked item
37418 * @param {Roo.EventObject} e The event object
37425 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37433 indicatorpos : 'left',
37435 getAutoCreate : function()
37439 cls : 'roo-radio-set-label',
37443 html : this.fieldLabel
37447 if (Roo.bootstrap.version == 3) {
37450 if(this.indicatorpos == 'left'){
37453 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37454 tooltip : 'This field is required'
37459 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37460 tooltip : 'This field is required'
37466 cls : 'roo-radio-set-items'
37469 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37471 if (align === 'left' && this.fieldLabel.length) {
37474 cls : "roo-radio-set-right",
37480 if(this.labelWidth > 12){
37481 label.style = "width: " + this.labelWidth + 'px';
37484 if(this.labelWidth < 13 && this.labelmd == 0){
37485 this.labelmd = this.labelWidth;
37488 if(this.labellg > 0){
37489 label.cls += ' col-lg-' + this.labellg;
37490 items.cls += ' col-lg-' + (12 - this.labellg);
37493 if(this.labelmd > 0){
37494 label.cls += ' col-md-' + this.labelmd;
37495 items.cls += ' col-md-' + (12 - this.labelmd);
37498 if(this.labelsm > 0){
37499 label.cls += ' col-sm-' + this.labelsm;
37500 items.cls += ' col-sm-' + (12 - this.labelsm);
37503 if(this.labelxs > 0){
37504 label.cls += ' col-xs-' + this.labelxs;
37505 items.cls += ' col-xs-' + (12 - this.labelxs);
37511 cls : 'roo-radio-set',
37515 cls : 'roo-radio-set-input',
37518 value : this.value ? this.value : ''
37525 if(this.weight.length){
37526 cfg.cls += ' roo-radio-' + this.weight;
37530 cfg.cls += ' roo-radio-set-inline';
37534 ['xs','sm','md','lg'].map(function(size){
37535 if (settings[size]) {
37536 cfg.cls += ' col-' + size + '-' + settings[size];
37544 initEvents : function()
37546 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37547 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37549 if(!this.fieldLabel.length){
37550 this.labelEl.hide();
37553 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37554 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37556 this.indicator = this.indicatorEl();
37558 if(this.indicator){
37559 this.indicator.addClass('invisible');
37562 this.originalValue = this.getValue();
37566 inputEl: function ()
37568 return this.el.select('.roo-radio-set-input', true).first();
37571 getChildContainer : function()
37573 return this.itemsEl;
37576 register : function(item)
37578 this.radioes.push(item);
37582 validate : function()
37584 if(this.getVisibilityEl().hasClass('hidden')){
37590 Roo.each(this.radioes, function(i){
37599 if(this.allowBlank) {
37603 if(this.disabled || valid){
37608 this.markInvalid();
37613 markValid : function()
37615 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37616 this.indicatorEl().removeClass('visible');
37617 this.indicatorEl().addClass('invisible');
37621 if (Roo.bootstrap.version == 3) {
37622 this.el.removeClass([this.invalidClass, this.validClass]);
37623 this.el.addClass(this.validClass);
37625 this.el.removeClass(['is-invalid','is-valid']);
37626 this.el.addClass(['is-valid']);
37628 this.fireEvent('valid', this);
37631 markInvalid : function(msg)
37633 if(this.allowBlank || this.disabled){
37637 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37638 this.indicatorEl().removeClass('invisible');
37639 this.indicatorEl().addClass('visible');
37641 if (Roo.bootstrap.version == 3) {
37642 this.el.removeClass([this.invalidClass, this.validClass]);
37643 this.el.addClass(this.invalidClass);
37645 this.el.removeClass(['is-invalid','is-valid']);
37646 this.el.addClass(['is-invalid']);
37649 this.fireEvent('invalid', this, msg);
37653 setValue : function(v, suppressEvent)
37655 if(this.value === v){
37662 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37665 Roo.each(this.radioes, function(i){
37667 i.el.removeClass('checked');
37670 Roo.each(this.radioes, function(i){
37672 if(i.value === v || i.value.toString() === v.toString()){
37674 i.el.addClass('checked');
37676 if(suppressEvent !== true){
37677 this.fireEvent('check', this, i);
37688 clearInvalid : function(){
37690 if(!this.el || this.preventMark){
37694 this.el.removeClass([this.invalidClass]);
37696 this.fireEvent('valid', this);
37701 Roo.apply(Roo.bootstrap.RadioSet, {
37705 register : function(set)
37707 this.groups[set.name] = set;
37710 get: function(name)
37712 if (typeof(this.groups[name]) == 'undefined') {
37716 return this.groups[name] ;
37722 * Ext JS Library 1.1.1
37723 * Copyright(c) 2006-2007, Ext JS, LLC.
37725 * Originally Released Under LGPL - original licence link has changed is not relivant.
37728 * <script type="text/javascript">
37733 * @class Roo.bootstrap.SplitBar
37734 * @extends Roo.util.Observable
37735 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37739 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37740 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37741 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37742 split.minSize = 100;
37743 split.maxSize = 600;
37744 split.animate = true;
37745 split.on('moved', splitterMoved);
37748 * Create a new SplitBar
37749 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37750 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37751 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37752 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37753 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37754 position of the SplitBar).
37756 Roo.bootstrap.SplitBar = function(cfg){
37761 // dragElement : elm
37762 // resizingElement: el,
37764 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37765 // placement : Roo.bootstrap.SplitBar.LEFT ,
37766 // existingProxy ???
37769 this.el = Roo.get(cfg.dragElement, true);
37770 this.el.dom.unselectable = "on";
37772 this.resizingEl = Roo.get(cfg.resizingElement, true);
37776 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37777 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37780 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37783 * The minimum size of the resizing element. (Defaults to 0)
37789 * The maximum size of the resizing element. (Defaults to 2000)
37792 this.maxSize = 2000;
37795 * Whether to animate the transition to the new size
37798 this.animate = false;
37801 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37804 this.useShim = false;
37809 if(!cfg.existingProxy){
37811 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37813 this.proxy = Roo.get(cfg.existingProxy).dom;
37816 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37819 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37822 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37825 this.dragSpecs = {};
37828 * @private The adapter to use to positon and resize elements
37830 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37831 this.adapter.init(this);
37833 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37835 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37836 this.el.addClass("roo-splitbar-h");
37839 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37840 this.el.addClass("roo-splitbar-v");
37846 * Fires when the splitter is moved (alias for {@link #event-moved})
37847 * @param {Roo.bootstrap.SplitBar} this
37848 * @param {Number} newSize the new width or height
37853 * Fires when the splitter is moved
37854 * @param {Roo.bootstrap.SplitBar} this
37855 * @param {Number} newSize the new width or height
37859 * @event beforeresize
37860 * Fires before the splitter is dragged
37861 * @param {Roo.bootstrap.SplitBar} this
37863 "beforeresize" : true,
37865 "beforeapply" : true
37868 Roo.util.Observable.call(this);
37871 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37872 onStartProxyDrag : function(x, y){
37873 this.fireEvent("beforeresize", this);
37875 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37877 o.enableDisplayMode("block");
37878 // all splitbars share the same overlay
37879 Roo.bootstrap.SplitBar.prototype.overlay = o;
37881 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37882 this.overlay.show();
37883 Roo.get(this.proxy).setDisplayed("block");
37884 var size = this.adapter.getElementSize(this);
37885 this.activeMinSize = this.getMinimumSize();;
37886 this.activeMaxSize = this.getMaximumSize();;
37887 var c1 = size - this.activeMinSize;
37888 var c2 = Math.max(this.activeMaxSize - size, 0);
37889 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37890 this.dd.resetConstraints();
37891 this.dd.setXConstraint(
37892 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37893 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37895 this.dd.setYConstraint(0, 0);
37897 this.dd.resetConstraints();
37898 this.dd.setXConstraint(0, 0);
37899 this.dd.setYConstraint(
37900 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37901 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37904 this.dragSpecs.startSize = size;
37905 this.dragSpecs.startPoint = [x, y];
37906 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37910 * @private Called after the drag operation by the DDProxy
37912 onEndProxyDrag : function(e){
37913 Roo.get(this.proxy).setDisplayed(false);
37914 var endPoint = Roo.lib.Event.getXY(e);
37916 this.overlay.hide();
37919 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37920 newSize = this.dragSpecs.startSize +
37921 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37922 endPoint[0] - this.dragSpecs.startPoint[0] :
37923 this.dragSpecs.startPoint[0] - endPoint[0]
37926 newSize = this.dragSpecs.startSize +
37927 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37928 endPoint[1] - this.dragSpecs.startPoint[1] :
37929 this.dragSpecs.startPoint[1] - endPoint[1]
37932 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37933 if(newSize != this.dragSpecs.startSize){
37934 if(this.fireEvent('beforeapply', this, newSize) !== false){
37935 this.adapter.setElementSize(this, newSize);
37936 this.fireEvent("moved", this, newSize);
37937 this.fireEvent("resize", this, newSize);
37943 * Get the adapter this SplitBar uses
37944 * @return The adapter object
37946 getAdapter : function(){
37947 return this.adapter;
37951 * Set the adapter this SplitBar uses
37952 * @param {Object} adapter A SplitBar adapter object
37954 setAdapter : function(adapter){
37955 this.adapter = adapter;
37956 this.adapter.init(this);
37960 * Gets the minimum size for the resizing element
37961 * @return {Number} The minimum size
37963 getMinimumSize : function(){
37964 return this.minSize;
37968 * Sets the minimum size for the resizing element
37969 * @param {Number} minSize The minimum size
37971 setMinimumSize : function(minSize){
37972 this.minSize = minSize;
37976 * Gets the maximum size for the resizing element
37977 * @return {Number} The maximum size
37979 getMaximumSize : function(){
37980 return this.maxSize;
37984 * Sets the maximum size for the resizing element
37985 * @param {Number} maxSize The maximum size
37987 setMaximumSize : function(maxSize){
37988 this.maxSize = maxSize;
37992 * Sets the initialize size for the resizing element
37993 * @param {Number} size The initial size
37995 setCurrentSize : function(size){
37996 var oldAnimate = this.animate;
37997 this.animate = false;
37998 this.adapter.setElementSize(this, size);
37999 this.animate = oldAnimate;
38003 * Destroy this splitbar.
38004 * @param {Boolean} removeEl True to remove the element
38006 destroy : function(removeEl){
38008 this.shim.remove();
38011 this.proxy.parentNode.removeChild(this.proxy);
38019 * @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.
38021 Roo.bootstrap.SplitBar.createProxy = function(dir){
38022 var proxy = new Roo.Element(document.createElement("div"));
38023 proxy.unselectable();
38024 var cls = 'roo-splitbar-proxy';
38025 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38026 document.body.appendChild(proxy.dom);
38031 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38032 * Default Adapter. It assumes the splitter and resizing element are not positioned
38033 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38035 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38038 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38039 // do nothing for now
38040 init : function(s){
38044 * Called before drag operations to get the current size of the resizing element.
38045 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38047 getElementSize : function(s){
38048 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38049 return s.resizingEl.getWidth();
38051 return s.resizingEl.getHeight();
38056 * Called after drag operations to set the size of the resizing element.
38057 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38058 * @param {Number} newSize The new size to set
38059 * @param {Function} onComplete A function to be invoked when resizing is complete
38061 setElementSize : function(s, newSize, onComplete){
38062 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38064 s.resizingEl.setWidth(newSize);
38066 onComplete(s, newSize);
38069 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38074 s.resizingEl.setHeight(newSize);
38076 onComplete(s, newSize);
38079 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38086 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38087 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38088 * Adapter that moves the splitter element to align with the resized sizing element.
38089 * Used with an absolute positioned SplitBar.
38090 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38091 * document.body, make sure you assign an id to the body element.
38093 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38094 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38095 this.container = Roo.get(container);
38098 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38099 init : function(s){
38100 this.basic.init(s);
38103 getElementSize : function(s){
38104 return this.basic.getElementSize(s);
38107 setElementSize : function(s, newSize, onComplete){
38108 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38111 moveSplitter : function(s){
38112 var yes = Roo.bootstrap.SplitBar;
38113 switch(s.placement){
38115 s.el.setX(s.resizingEl.getRight());
38118 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38121 s.el.setY(s.resizingEl.getBottom());
38124 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38131 * Orientation constant - Create a vertical SplitBar
38135 Roo.bootstrap.SplitBar.VERTICAL = 1;
38138 * Orientation constant - Create a horizontal SplitBar
38142 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38145 * Placement constant - The resizing element is to the left of the splitter element
38149 Roo.bootstrap.SplitBar.LEFT = 1;
38152 * Placement constant - The resizing element is to the right of the splitter element
38156 Roo.bootstrap.SplitBar.RIGHT = 2;
38159 * Placement constant - The resizing element is positioned above the splitter element
38163 Roo.bootstrap.SplitBar.TOP = 3;
38166 * Placement constant - The resizing element is positioned under splitter element
38170 Roo.bootstrap.SplitBar.BOTTOM = 4;
38171 Roo.namespace("Roo.bootstrap.layout");/*
38173 * Ext JS Library 1.1.1
38174 * Copyright(c) 2006-2007, Ext JS, LLC.
38176 * Originally Released Under LGPL - original licence link has changed is not relivant.
38179 * <script type="text/javascript">
38183 * @class Roo.bootstrap.layout.Manager
38184 * @extends Roo.bootstrap.Component
38185 * Base class for layout managers.
38187 Roo.bootstrap.layout.Manager = function(config)
38189 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38195 /** false to disable window resize monitoring @type Boolean */
38196 this.monitorWindowResize = true;
38201 * Fires when a layout is performed.
38202 * @param {Roo.LayoutManager} this
38206 * @event regionresized
38207 * Fires when the user resizes a region.
38208 * @param {Roo.LayoutRegion} region The resized region
38209 * @param {Number} newSize The new size (width for east/west, height for north/south)
38211 "regionresized" : true,
38213 * @event regioncollapsed
38214 * Fires when a region is collapsed.
38215 * @param {Roo.LayoutRegion} region The collapsed region
38217 "regioncollapsed" : true,
38219 * @event regionexpanded
38220 * Fires when a region is expanded.
38221 * @param {Roo.LayoutRegion} region The expanded region
38223 "regionexpanded" : true
38225 this.updating = false;
38228 this.el = Roo.get(config.el);
38234 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38239 monitorWindowResize : true,
38245 onRender : function(ct, position)
38248 this.el = Roo.get(ct);
38251 //this.fireEvent('render',this);
38255 initEvents: function()
38259 // ie scrollbar fix
38260 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38261 document.body.scroll = "no";
38262 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38263 this.el.position('relative');
38265 this.id = this.el.id;
38266 this.el.addClass("roo-layout-container");
38267 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38268 if(this.el.dom != document.body ) {
38269 this.el.on('resize', this.layout,this);
38270 this.el.on('show', this.layout,this);
38276 * Returns true if this layout is currently being updated
38277 * @return {Boolean}
38279 isUpdating : function(){
38280 return this.updating;
38284 * Suspend the LayoutManager from doing auto-layouts while
38285 * making multiple add or remove calls
38287 beginUpdate : function(){
38288 this.updating = true;
38292 * Restore auto-layouts and optionally disable the manager from performing a layout
38293 * @param {Boolean} noLayout true to disable a layout update
38295 endUpdate : function(noLayout){
38296 this.updating = false;
38302 layout: function(){
38306 onRegionResized : function(region, newSize){
38307 this.fireEvent("regionresized", region, newSize);
38311 onRegionCollapsed : function(region){
38312 this.fireEvent("regioncollapsed", region);
38315 onRegionExpanded : function(region){
38316 this.fireEvent("regionexpanded", region);
38320 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38321 * performs box-model adjustments.
38322 * @return {Object} The size as an object {width: (the width), height: (the height)}
38324 getViewSize : function()
38327 if(this.el.dom != document.body){
38328 size = this.el.getSize();
38330 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38332 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38333 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38338 * Returns the Element this layout is bound to.
38339 * @return {Roo.Element}
38341 getEl : function(){
38346 * Returns the specified region.
38347 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38348 * @return {Roo.LayoutRegion}
38350 getRegion : function(target){
38351 return this.regions[target.toLowerCase()];
38354 onWindowResize : function(){
38355 if(this.monitorWindowResize){
38362 * Ext JS Library 1.1.1
38363 * Copyright(c) 2006-2007, Ext JS, LLC.
38365 * Originally Released Under LGPL - original licence link has changed is not relivant.
38368 * <script type="text/javascript">
38371 * @class Roo.bootstrap.layout.Border
38372 * @extends Roo.bootstrap.layout.Manager
38373 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38374 * please see: examples/bootstrap/nested.html<br><br>
38376 <b>The container the layout is rendered into can be either the body element or any other element.
38377 If it is not the body element, the container needs to either be an absolute positioned element,
38378 or you will need to add "position:relative" to the css of the container. You will also need to specify
38379 the container size if it is not the body element.</b>
38382 * Create a new Border
38383 * @param {Object} config Configuration options
38385 Roo.bootstrap.layout.Border = function(config){
38386 config = config || {};
38387 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38391 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38392 if(config[region]){
38393 config[region].region = region;
38394 this.addRegion(config[region]);
38400 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38402 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38404 parent : false, // this might point to a 'nest' or a ???
38407 * Creates and adds a new region if it doesn't already exist.
38408 * @param {String} target The target region key (north, south, east, west or center).
38409 * @param {Object} config The regions config object
38410 * @return {BorderLayoutRegion} The new region
38412 addRegion : function(config)
38414 if(!this.regions[config.region]){
38415 var r = this.factory(config);
38416 this.bindRegion(r);
38418 return this.regions[config.region];
38422 bindRegion : function(r){
38423 this.regions[r.config.region] = r;
38425 r.on("visibilitychange", this.layout, this);
38426 r.on("paneladded", this.layout, this);
38427 r.on("panelremoved", this.layout, this);
38428 r.on("invalidated", this.layout, this);
38429 r.on("resized", this.onRegionResized, this);
38430 r.on("collapsed", this.onRegionCollapsed, this);
38431 r.on("expanded", this.onRegionExpanded, this);
38435 * Performs a layout update.
38437 layout : function()
38439 if(this.updating) {
38443 // render all the rebions if they have not been done alreayd?
38444 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38445 if(this.regions[region] && !this.regions[region].bodyEl){
38446 this.regions[region].onRender(this.el)
38450 var size = this.getViewSize();
38451 var w = size.width;
38452 var h = size.height;
38457 //var x = 0, y = 0;
38459 var rs = this.regions;
38460 var north = rs["north"];
38461 var south = rs["south"];
38462 var west = rs["west"];
38463 var east = rs["east"];
38464 var center = rs["center"];
38465 //if(this.hideOnLayout){ // not supported anymore
38466 //c.el.setStyle("display", "none");
38468 if(north && north.isVisible()){
38469 var b = north.getBox();
38470 var m = north.getMargins();
38471 b.width = w - (m.left+m.right);
38474 centerY = b.height + b.y + m.bottom;
38475 centerH -= centerY;
38476 north.updateBox(this.safeBox(b));
38478 if(south && south.isVisible()){
38479 var b = south.getBox();
38480 var m = south.getMargins();
38481 b.width = w - (m.left+m.right);
38483 var totalHeight = (b.height + m.top + m.bottom);
38484 b.y = h - totalHeight + m.top;
38485 centerH -= totalHeight;
38486 south.updateBox(this.safeBox(b));
38488 if(west && west.isVisible()){
38489 var b = west.getBox();
38490 var m = west.getMargins();
38491 b.height = centerH - (m.top+m.bottom);
38493 b.y = centerY + m.top;
38494 var totalWidth = (b.width + m.left + m.right);
38495 centerX += totalWidth;
38496 centerW -= totalWidth;
38497 west.updateBox(this.safeBox(b));
38499 if(east && east.isVisible()){
38500 var b = east.getBox();
38501 var m = east.getMargins();
38502 b.height = centerH - (m.top+m.bottom);
38503 var totalWidth = (b.width + m.left + m.right);
38504 b.x = w - totalWidth + m.left;
38505 b.y = centerY + m.top;
38506 centerW -= totalWidth;
38507 east.updateBox(this.safeBox(b));
38510 var m = center.getMargins();
38512 x: centerX + m.left,
38513 y: centerY + m.top,
38514 width: centerW - (m.left+m.right),
38515 height: centerH - (m.top+m.bottom)
38517 //if(this.hideOnLayout){
38518 //center.el.setStyle("display", "block");
38520 center.updateBox(this.safeBox(centerBox));
38523 this.fireEvent("layout", this);
38527 safeBox : function(box){
38528 box.width = Math.max(0, box.width);
38529 box.height = Math.max(0, box.height);
38534 * Adds a ContentPanel (or subclass) to this layout.
38535 * @param {String} target The target region key (north, south, east, west or center).
38536 * @param {Roo.ContentPanel} panel The panel to add
38537 * @return {Roo.ContentPanel} The added panel
38539 add : function(target, panel){
38541 target = target.toLowerCase();
38542 return this.regions[target].add(panel);
38546 * Remove a ContentPanel (or subclass) to this layout.
38547 * @param {String} target The target region key (north, south, east, west or center).
38548 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38549 * @return {Roo.ContentPanel} The removed panel
38551 remove : function(target, panel){
38552 target = target.toLowerCase();
38553 return this.regions[target].remove(panel);
38557 * Searches all regions for a panel with the specified id
38558 * @param {String} panelId
38559 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38561 findPanel : function(panelId){
38562 var rs = this.regions;
38563 for(var target in rs){
38564 if(typeof rs[target] != "function"){
38565 var p = rs[target].getPanel(panelId);
38575 * Searches all regions for a panel with the specified id and activates (shows) it.
38576 * @param {String/ContentPanel} panelId The panels id or the panel itself
38577 * @return {Roo.ContentPanel} The shown panel or null
38579 showPanel : function(panelId) {
38580 var rs = this.regions;
38581 for(var target in rs){
38582 var r = rs[target];
38583 if(typeof r != "function"){
38584 if(r.hasPanel(panelId)){
38585 return r.showPanel(panelId);
38593 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38594 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38597 restoreState : function(provider){
38599 provider = Roo.state.Manager;
38601 var sm = new Roo.LayoutStateManager();
38602 sm.init(this, provider);
38608 * Adds a xtype elements to the layout.
38612 xtype : 'ContentPanel',
38619 xtype : 'NestedLayoutPanel',
38625 items : [ ... list of content panels or nested layout panels.. ]
38629 * @param {Object} cfg Xtype definition of item to add.
38631 addxtype : function(cfg)
38633 // basically accepts a pannel...
38634 // can accept a layout region..!?!?
38635 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38638 // theory? children can only be panels??
38640 //if (!cfg.xtype.match(/Panel$/)) {
38645 if (typeof(cfg.region) == 'undefined') {
38646 Roo.log("Failed to add Panel, region was not set");
38650 var region = cfg.region;
38656 xitems = cfg.items;
38661 if ( region == 'center') {
38662 Roo.log("Center: " + cfg.title);
38668 case 'Content': // ContentPanel (el, cfg)
38669 case 'Scroll': // ContentPanel (el, cfg)
38671 cfg.autoCreate = cfg.autoCreate || true;
38672 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38674 // var el = this.el.createChild();
38675 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38678 this.add(region, ret);
38682 case 'TreePanel': // our new panel!
38683 cfg.el = this.el.createChild();
38684 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38685 this.add(region, ret);
38690 // create a new Layout (which is a Border Layout...
38692 var clayout = cfg.layout;
38693 clayout.el = this.el.createChild();
38694 clayout.items = clayout.items || [];
38698 // replace this exitems with the clayout ones..
38699 xitems = clayout.items;
38701 // force background off if it's in center...
38702 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38703 cfg.background = false;
38705 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38708 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38709 //console.log('adding nested layout panel ' + cfg.toSource());
38710 this.add(region, ret);
38711 nb = {}; /// find first...
38716 // needs grid and region
38718 //var el = this.getRegion(region).el.createChild();
38720 *var el = this.el.createChild();
38721 // create the grid first...
38722 cfg.grid.container = el;
38723 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38726 if (region == 'center' && this.active ) {
38727 cfg.background = false;
38730 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38732 this.add(region, ret);
38734 if (cfg.background) {
38735 // render grid on panel activation (if panel background)
38736 ret.on('activate', function(gp) {
38737 if (!gp.grid.rendered) {
38738 // gp.grid.render(el);
38742 // cfg.grid.render(el);
38748 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38749 // it was the old xcomponent building that caused this before.
38750 // espeically if border is the top element in the tree.
38760 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38762 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38763 this.add(region, ret);
38767 throw "Can not add '" + cfg.xtype + "' to Border";
38773 this.beginUpdate();
38777 Roo.each(xitems, function(i) {
38778 region = nb && i.region ? i.region : false;
38780 var add = ret.addxtype(i);
38783 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38784 if (!i.background) {
38785 abn[region] = nb[region] ;
38792 // make the last non-background panel active..
38793 //if (nb) { Roo.log(abn); }
38796 for(var r in abn) {
38797 region = this.getRegion(r);
38799 // tried using nb[r], but it does not work..
38801 region.showPanel(abn[r]);
38812 factory : function(cfg)
38815 var validRegions = Roo.bootstrap.layout.Border.regions;
38817 var target = cfg.region;
38820 var r = Roo.bootstrap.layout;
38824 return new r.North(cfg);
38826 return new r.South(cfg);
38828 return new r.East(cfg);
38830 return new r.West(cfg);
38832 return new r.Center(cfg);
38834 throw 'Layout region "'+target+'" not supported.';
38841 * Ext JS Library 1.1.1
38842 * Copyright(c) 2006-2007, Ext JS, LLC.
38844 * Originally Released Under LGPL - original licence link has changed is not relivant.
38847 * <script type="text/javascript">
38851 * @class Roo.bootstrap.layout.Basic
38852 * @extends Roo.util.Observable
38853 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38854 * and does not have a titlebar, tabs or any other features. All it does is size and position
38855 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38856 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38857 * @cfg {string} region the region that it inhabits..
38858 * @cfg {bool} skipConfig skip config?
38862 Roo.bootstrap.layout.Basic = function(config){
38864 this.mgr = config.mgr;
38866 this.position = config.region;
38868 var skipConfig = config.skipConfig;
38872 * @scope Roo.BasicLayoutRegion
38876 * @event beforeremove
38877 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38878 * @param {Roo.LayoutRegion} this
38879 * @param {Roo.ContentPanel} panel The panel
38880 * @param {Object} e The cancel event object
38882 "beforeremove" : true,
38884 * @event invalidated
38885 * Fires when the layout for this region is changed.
38886 * @param {Roo.LayoutRegion} this
38888 "invalidated" : true,
38890 * @event visibilitychange
38891 * Fires when this region is shown or hidden
38892 * @param {Roo.LayoutRegion} this
38893 * @param {Boolean} visibility true or false
38895 "visibilitychange" : true,
38897 * @event paneladded
38898 * Fires when a panel is added.
38899 * @param {Roo.LayoutRegion} this
38900 * @param {Roo.ContentPanel} panel The panel
38902 "paneladded" : true,
38904 * @event panelremoved
38905 * Fires when a panel is removed.
38906 * @param {Roo.LayoutRegion} this
38907 * @param {Roo.ContentPanel} panel The panel
38909 "panelremoved" : true,
38911 * @event beforecollapse
38912 * Fires when this region before collapse.
38913 * @param {Roo.LayoutRegion} this
38915 "beforecollapse" : true,
38918 * Fires when this region is collapsed.
38919 * @param {Roo.LayoutRegion} this
38921 "collapsed" : true,
38924 * Fires when this region is expanded.
38925 * @param {Roo.LayoutRegion} this
38930 * Fires when this region is slid into view.
38931 * @param {Roo.LayoutRegion} this
38933 "slideshow" : true,
38936 * Fires when this region slides out of view.
38937 * @param {Roo.LayoutRegion} this
38939 "slidehide" : true,
38941 * @event panelactivated
38942 * Fires when a panel is activated.
38943 * @param {Roo.LayoutRegion} this
38944 * @param {Roo.ContentPanel} panel The activated panel
38946 "panelactivated" : true,
38949 * Fires when the user resizes this region.
38950 * @param {Roo.LayoutRegion} this
38951 * @param {Number} newSize The new size (width for east/west, height for north/south)
38955 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38956 this.panels = new Roo.util.MixedCollection();
38957 this.panels.getKey = this.getPanelId.createDelegate(this);
38959 this.activePanel = null;
38960 // ensure listeners are added...
38962 if (config.listeners || config.events) {
38963 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38964 listeners : config.listeners || {},
38965 events : config.events || {}
38969 if(skipConfig !== true){
38970 this.applyConfig(config);
38974 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38976 getPanelId : function(p){
38980 applyConfig : function(config){
38981 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38982 this.config = config;
38987 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38988 * the width, for horizontal (north, south) the height.
38989 * @param {Number} newSize The new width or height
38991 resizeTo : function(newSize){
38992 var el = this.el ? this.el :
38993 (this.activePanel ? this.activePanel.getEl() : null);
38995 switch(this.position){
38998 el.setWidth(newSize);
38999 this.fireEvent("resized", this, newSize);
39003 el.setHeight(newSize);
39004 this.fireEvent("resized", this, newSize);
39010 getBox : function(){
39011 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39014 getMargins : function(){
39015 return this.margins;
39018 updateBox : function(box){
39020 var el = this.activePanel.getEl();
39021 el.dom.style.left = box.x + "px";
39022 el.dom.style.top = box.y + "px";
39023 this.activePanel.setSize(box.width, box.height);
39027 * Returns the container element for this region.
39028 * @return {Roo.Element}
39030 getEl : function(){
39031 return this.activePanel;
39035 * Returns true if this region is currently visible.
39036 * @return {Boolean}
39038 isVisible : function(){
39039 return this.activePanel ? true : false;
39042 setActivePanel : function(panel){
39043 panel = this.getPanel(panel);
39044 if(this.activePanel && this.activePanel != panel){
39045 this.activePanel.setActiveState(false);
39046 this.activePanel.getEl().setLeftTop(-10000,-10000);
39048 this.activePanel = panel;
39049 panel.setActiveState(true);
39051 panel.setSize(this.box.width, this.box.height);
39053 this.fireEvent("panelactivated", this, panel);
39054 this.fireEvent("invalidated");
39058 * Show the specified panel.
39059 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39060 * @return {Roo.ContentPanel} The shown panel or null
39062 showPanel : function(panel){
39063 panel = this.getPanel(panel);
39065 this.setActivePanel(panel);
39071 * Get the active panel for this region.
39072 * @return {Roo.ContentPanel} The active panel or null
39074 getActivePanel : function(){
39075 return this.activePanel;
39079 * Add the passed ContentPanel(s)
39080 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39081 * @return {Roo.ContentPanel} The panel added (if only one was added)
39083 add : function(panel){
39084 if(arguments.length > 1){
39085 for(var i = 0, len = arguments.length; i < len; i++) {
39086 this.add(arguments[i]);
39090 if(this.hasPanel(panel)){
39091 this.showPanel(panel);
39094 var el = panel.getEl();
39095 if(el.dom.parentNode != this.mgr.el.dom){
39096 this.mgr.el.dom.appendChild(el.dom);
39098 if(panel.setRegion){
39099 panel.setRegion(this);
39101 this.panels.add(panel);
39102 el.setStyle("position", "absolute");
39103 if(!panel.background){
39104 this.setActivePanel(panel);
39105 if(this.config.initialSize && this.panels.getCount()==1){
39106 this.resizeTo(this.config.initialSize);
39109 this.fireEvent("paneladded", this, panel);
39114 * Returns true if the panel is in this region.
39115 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39116 * @return {Boolean}
39118 hasPanel : function(panel){
39119 if(typeof panel == "object"){ // must be panel obj
39120 panel = panel.getId();
39122 return this.getPanel(panel) ? true : false;
39126 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39127 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39128 * @param {Boolean} preservePanel Overrides the config preservePanel option
39129 * @return {Roo.ContentPanel} The panel that was removed
39131 remove : function(panel, preservePanel){
39132 panel = this.getPanel(panel);
39137 this.fireEvent("beforeremove", this, panel, e);
39138 if(e.cancel === true){
39141 var panelId = panel.getId();
39142 this.panels.removeKey(panelId);
39147 * Returns the panel specified or null if it's not in this region.
39148 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39149 * @return {Roo.ContentPanel}
39151 getPanel : function(id){
39152 if(typeof id == "object"){ // must be panel obj
39155 return this.panels.get(id);
39159 * Returns this regions position (north/south/east/west/center).
39162 getPosition: function(){
39163 return this.position;
39167 * Ext JS Library 1.1.1
39168 * Copyright(c) 2006-2007, Ext JS, LLC.
39170 * Originally Released Under LGPL - original licence link has changed is not relivant.
39173 * <script type="text/javascript">
39177 * @class Roo.bootstrap.layout.Region
39178 * @extends Roo.bootstrap.layout.Basic
39179 * This class represents a region in a layout manager.
39181 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39182 * @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})
39183 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39184 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39185 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39186 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39187 * @cfg {String} title The title for the region (overrides panel titles)
39188 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39189 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39190 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39191 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39192 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39193 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39194 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39195 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39196 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39197 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39199 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39200 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39201 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39202 * @cfg {Number} width For East/West panels
39203 * @cfg {Number} height For North/South panels
39204 * @cfg {Boolean} split To show the splitter
39205 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39207 * @cfg {string} cls Extra CSS classes to add to region
39209 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39210 * @cfg {string} region the region that it inhabits..
39213 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39214 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39216 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39217 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39218 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39220 Roo.bootstrap.layout.Region = function(config)
39222 this.applyConfig(config);
39224 var mgr = config.mgr;
39225 var pos = config.region;
39226 config.skipConfig = true;
39227 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39230 this.onRender(mgr.el);
39233 this.visible = true;
39234 this.collapsed = false;
39235 this.unrendered_panels = [];
39238 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39240 position: '', // set by wrapper (eg. north/south etc..)
39241 unrendered_panels : null, // unrendered panels.
39243 tabPosition : false,
39245 mgr: false, // points to 'Border'
39248 createBody : function(){
39249 /** This region's body element
39250 * @type Roo.Element */
39251 this.bodyEl = this.el.createChild({
39253 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39257 onRender: function(ctr, pos)
39259 var dh = Roo.DomHelper;
39260 /** This region's container element
39261 * @type Roo.Element */
39262 this.el = dh.append(ctr.dom, {
39264 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39266 /** This region's title element
39267 * @type Roo.Element */
39269 this.titleEl = dh.append(this.el.dom, {
39271 unselectable: "on",
39272 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39274 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39275 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39279 this.titleEl.enableDisplayMode();
39280 /** This region's title text element
39281 * @type HTMLElement */
39282 this.titleTextEl = this.titleEl.dom.firstChild;
39283 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39285 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39286 this.closeBtn.enableDisplayMode();
39287 this.closeBtn.on("click", this.closeClicked, this);
39288 this.closeBtn.hide();
39290 this.createBody(this.config);
39291 if(this.config.hideWhenEmpty){
39293 this.on("paneladded", this.validateVisibility, this);
39294 this.on("panelremoved", this.validateVisibility, this);
39296 if(this.autoScroll){
39297 this.bodyEl.setStyle("overflow", "auto");
39299 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39301 //if(c.titlebar !== false){
39302 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39303 this.titleEl.hide();
39305 this.titleEl.show();
39306 if(this.config.title){
39307 this.titleTextEl.innerHTML = this.config.title;
39311 if(this.config.collapsed){
39312 this.collapse(true);
39314 if(this.config.hidden){
39318 if (this.unrendered_panels && this.unrendered_panels.length) {
39319 for (var i =0;i< this.unrendered_panels.length; i++) {
39320 this.add(this.unrendered_panels[i]);
39322 this.unrendered_panels = null;
39328 applyConfig : function(c)
39331 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39332 var dh = Roo.DomHelper;
39333 if(c.titlebar !== false){
39334 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39335 this.collapseBtn.on("click", this.collapse, this);
39336 this.collapseBtn.enableDisplayMode();
39338 if(c.showPin === true || this.showPin){
39339 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39340 this.stickBtn.enableDisplayMode();
39341 this.stickBtn.on("click", this.expand, this);
39342 this.stickBtn.hide();
39347 /** This region's collapsed element
39348 * @type Roo.Element */
39351 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39352 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39355 if(c.floatable !== false){
39356 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39357 this.collapsedEl.on("click", this.collapseClick, this);
39360 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39361 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39362 id: "message", unselectable: "on", style:{"float":"left"}});
39363 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39365 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39366 this.expandBtn.on("click", this.expand, this);
39370 if(this.collapseBtn){
39371 this.collapseBtn.setVisible(c.collapsible == true);
39374 this.cmargins = c.cmargins || this.cmargins ||
39375 (this.position == "west" || this.position == "east" ?
39376 {top: 0, left: 2, right:2, bottom: 0} :
39377 {top: 2, left: 0, right:0, bottom: 2});
39379 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39382 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39384 this.autoScroll = c.autoScroll || false;
39389 this.duration = c.duration || .30;
39390 this.slideDuration = c.slideDuration || .45;
39395 * Returns true if this region is currently visible.
39396 * @return {Boolean}
39398 isVisible : function(){
39399 return this.visible;
39403 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39404 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39406 //setCollapsedTitle : function(title){
39407 // title = title || " ";
39408 // if(this.collapsedTitleTextEl){
39409 // this.collapsedTitleTextEl.innerHTML = title;
39413 getBox : function(){
39415 // if(!this.collapsed){
39416 b = this.el.getBox(false, true);
39418 // b = this.collapsedEl.getBox(false, true);
39423 getMargins : function(){
39424 return this.margins;
39425 //return this.collapsed ? this.cmargins : this.margins;
39428 highlight : function(){
39429 this.el.addClass("x-layout-panel-dragover");
39432 unhighlight : function(){
39433 this.el.removeClass("x-layout-panel-dragover");
39436 updateBox : function(box)
39438 if (!this.bodyEl) {
39439 return; // not rendered yet..
39443 if(!this.collapsed){
39444 this.el.dom.style.left = box.x + "px";
39445 this.el.dom.style.top = box.y + "px";
39446 this.updateBody(box.width, box.height);
39448 this.collapsedEl.dom.style.left = box.x + "px";
39449 this.collapsedEl.dom.style.top = box.y + "px";
39450 this.collapsedEl.setSize(box.width, box.height);
39453 this.tabs.autoSizeTabs();
39457 updateBody : function(w, h)
39460 this.el.setWidth(w);
39461 w -= this.el.getBorderWidth("rl");
39462 if(this.config.adjustments){
39463 w += this.config.adjustments[0];
39466 if(h !== null && h > 0){
39467 this.el.setHeight(h);
39468 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39469 h -= this.el.getBorderWidth("tb");
39470 if(this.config.adjustments){
39471 h += this.config.adjustments[1];
39473 this.bodyEl.setHeight(h);
39475 h = this.tabs.syncHeight(h);
39478 if(this.panelSize){
39479 w = w !== null ? w : this.panelSize.width;
39480 h = h !== null ? h : this.panelSize.height;
39482 if(this.activePanel){
39483 var el = this.activePanel.getEl();
39484 w = w !== null ? w : el.getWidth();
39485 h = h !== null ? h : el.getHeight();
39486 this.panelSize = {width: w, height: h};
39487 this.activePanel.setSize(w, h);
39489 if(Roo.isIE && this.tabs){
39490 this.tabs.el.repaint();
39495 * Returns the container element for this region.
39496 * @return {Roo.Element}
39498 getEl : function(){
39503 * Hides this region.
39506 //if(!this.collapsed){
39507 this.el.dom.style.left = "-2000px";
39510 // this.collapsedEl.dom.style.left = "-2000px";
39511 // this.collapsedEl.hide();
39513 this.visible = false;
39514 this.fireEvent("visibilitychange", this, false);
39518 * Shows this region if it was previously hidden.
39521 //if(!this.collapsed){
39524 // this.collapsedEl.show();
39526 this.visible = true;
39527 this.fireEvent("visibilitychange", this, true);
39530 closeClicked : function(){
39531 if(this.activePanel){
39532 this.remove(this.activePanel);
39536 collapseClick : function(e){
39538 e.stopPropagation();
39541 e.stopPropagation();
39547 * Collapses this region.
39548 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39551 collapse : function(skipAnim, skipCheck = false){
39552 if(this.collapsed) {
39556 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39558 this.collapsed = true;
39560 this.split.el.hide();
39562 if(this.config.animate && skipAnim !== true){
39563 this.fireEvent("invalidated", this);
39564 this.animateCollapse();
39566 this.el.setLocation(-20000,-20000);
39568 this.collapsedEl.show();
39569 this.fireEvent("collapsed", this);
39570 this.fireEvent("invalidated", this);
39576 animateCollapse : function(){
39581 * Expands this region if it was previously collapsed.
39582 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39583 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39586 expand : function(e, skipAnim){
39588 e.stopPropagation();
39590 if(!this.collapsed || this.el.hasActiveFx()) {
39594 this.afterSlideIn();
39597 this.collapsed = false;
39598 if(this.config.animate && skipAnim !== true){
39599 this.animateExpand();
39603 this.split.el.show();
39605 this.collapsedEl.setLocation(-2000,-2000);
39606 this.collapsedEl.hide();
39607 this.fireEvent("invalidated", this);
39608 this.fireEvent("expanded", this);
39612 animateExpand : function(){
39616 initTabs : function()
39618 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39620 var ts = new Roo.bootstrap.panel.Tabs({
39621 el: this.bodyEl.dom,
39623 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39624 disableTooltips: this.config.disableTabTips,
39625 toolbar : this.config.toolbar
39628 if(this.config.hideTabs){
39629 ts.stripWrap.setDisplayed(false);
39632 ts.resizeTabs = this.config.resizeTabs === true;
39633 ts.minTabWidth = this.config.minTabWidth || 40;
39634 ts.maxTabWidth = this.config.maxTabWidth || 250;
39635 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39636 ts.monitorResize = false;
39637 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39638 ts.bodyEl.addClass('roo-layout-tabs-body');
39639 this.panels.each(this.initPanelAsTab, this);
39642 initPanelAsTab : function(panel){
39643 var ti = this.tabs.addTab(
39647 this.config.closeOnTab && panel.isClosable(),
39650 if(panel.tabTip !== undefined){
39651 ti.setTooltip(panel.tabTip);
39653 ti.on("activate", function(){
39654 this.setActivePanel(panel);
39657 if(this.config.closeOnTab){
39658 ti.on("beforeclose", function(t, e){
39660 this.remove(panel);
39664 panel.tabItem = ti;
39669 updatePanelTitle : function(panel, title)
39671 if(this.activePanel == panel){
39672 this.updateTitle(title);
39675 var ti = this.tabs.getTab(panel.getEl().id);
39677 if(panel.tabTip !== undefined){
39678 ti.setTooltip(panel.tabTip);
39683 updateTitle : function(title){
39684 if(this.titleTextEl && !this.config.title){
39685 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39689 setActivePanel : function(panel)
39691 panel = this.getPanel(panel);
39692 if(this.activePanel && this.activePanel != panel){
39693 if(this.activePanel.setActiveState(false) === false){
39697 this.activePanel = panel;
39698 panel.setActiveState(true);
39699 if(this.panelSize){
39700 panel.setSize(this.panelSize.width, this.panelSize.height);
39703 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39705 this.updateTitle(panel.getTitle());
39707 this.fireEvent("invalidated", this);
39709 this.fireEvent("panelactivated", this, panel);
39713 * Shows the specified panel.
39714 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39715 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39717 showPanel : function(panel)
39719 panel = this.getPanel(panel);
39722 var tab = this.tabs.getTab(panel.getEl().id);
39723 if(tab.isHidden()){
39724 this.tabs.unhideTab(tab.id);
39728 this.setActivePanel(panel);
39735 * Get the active panel for this region.
39736 * @return {Roo.ContentPanel} The active panel or null
39738 getActivePanel : function(){
39739 return this.activePanel;
39742 validateVisibility : function(){
39743 if(this.panels.getCount() < 1){
39744 this.updateTitle(" ");
39745 this.closeBtn.hide();
39748 if(!this.isVisible()){
39755 * Adds the passed ContentPanel(s) to this region.
39756 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39757 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39759 add : function(panel)
39761 if(arguments.length > 1){
39762 for(var i = 0, len = arguments.length; i < len; i++) {
39763 this.add(arguments[i]);
39768 // if we have not been rendered yet, then we can not really do much of this..
39769 if (!this.bodyEl) {
39770 this.unrendered_panels.push(panel);
39777 if(this.hasPanel(panel)){
39778 this.showPanel(panel);
39781 panel.setRegion(this);
39782 this.panels.add(panel);
39783 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39784 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39785 // and hide them... ???
39786 this.bodyEl.dom.appendChild(panel.getEl().dom);
39787 if(panel.background !== true){
39788 this.setActivePanel(panel);
39790 this.fireEvent("paneladded", this, panel);
39797 this.initPanelAsTab(panel);
39801 if(panel.background !== true){
39802 this.tabs.activate(panel.getEl().id);
39804 this.fireEvent("paneladded", this, panel);
39809 * Hides the tab for the specified panel.
39810 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39812 hidePanel : function(panel){
39813 if(this.tabs && (panel = this.getPanel(panel))){
39814 this.tabs.hideTab(panel.getEl().id);
39819 * Unhides the tab for a previously hidden panel.
39820 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39822 unhidePanel : function(panel){
39823 if(this.tabs && (panel = this.getPanel(panel))){
39824 this.tabs.unhideTab(panel.getEl().id);
39828 clearPanels : function(){
39829 while(this.panels.getCount() > 0){
39830 this.remove(this.panels.first());
39835 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39836 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39837 * @param {Boolean} preservePanel Overrides the config preservePanel option
39838 * @return {Roo.ContentPanel} The panel that was removed
39840 remove : function(panel, preservePanel)
39842 panel = this.getPanel(panel);
39847 this.fireEvent("beforeremove", this, panel, e);
39848 if(e.cancel === true){
39851 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39852 var panelId = panel.getId();
39853 this.panels.removeKey(panelId);
39855 document.body.appendChild(panel.getEl().dom);
39858 this.tabs.removeTab(panel.getEl().id);
39859 }else if (!preservePanel){
39860 this.bodyEl.dom.removeChild(panel.getEl().dom);
39862 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39863 var p = this.panels.first();
39864 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39865 tempEl.appendChild(p.getEl().dom);
39866 this.bodyEl.update("");
39867 this.bodyEl.dom.appendChild(p.getEl().dom);
39869 this.updateTitle(p.getTitle());
39871 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39872 this.setActivePanel(p);
39874 panel.setRegion(null);
39875 if(this.activePanel == panel){
39876 this.activePanel = null;
39878 if(this.config.autoDestroy !== false && preservePanel !== true){
39879 try{panel.destroy();}catch(e){}
39881 this.fireEvent("panelremoved", this, panel);
39886 * Returns the TabPanel component used by this region
39887 * @return {Roo.TabPanel}
39889 getTabs : function(){
39893 createTool : function(parentEl, className){
39894 var btn = Roo.DomHelper.append(parentEl, {
39896 cls: "x-layout-tools-button",
39899 cls: "roo-layout-tools-button-inner " + className,
39903 btn.addClassOnOver("roo-layout-tools-button-over");
39908 * Ext JS Library 1.1.1
39909 * Copyright(c) 2006-2007, Ext JS, LLC.
39911 * Originally Released Under LGPL - original licence link has changed is not relivant.
39914 * <script type="text/javascript">
39920 * @class Roo.SplitLayoutRegion
39921 * @extends Roo.LayoutRegion
39922 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39924 Roo.bootstrap.layout.Split = function(config){
39925 this.cursor = config.cursor;
39926 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39929 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39931 splitTip : "Drag to resize.",
39932 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39933 useSplitTips : false,
39935 applyConfig : function(config){
39936 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39939 onRender : function(ctr,pos) {
39941 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39942 if(!this.config.split){
39947 var splitEl = Roo.DomHelper.append(ctr.dom, {
39949 id: this.el.id + "-split",
39950 cls: "roo-layout-split roo-layout-split-"+this.position,
39953 /** The SplitBar for this region
39954 * @type Roo.SplitBar */
39955 // does not exist yet...
39956 Roo.log([this.position, this.orientation]);
39958 this.split = new Roo.bootstrap.SplitBar({
39959 dragElement : splitEl,
39960 resizingElement: this.el,
39961 orientation : this.orientation
39964 this.split.on("moved", this.onSplitMove, this);
39965 this.split.useShim = this.config.useShim === true;
39966 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39967 if(this.useSplitTips){
39968 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39970 //if(config.collapsible){
39971 // this.split.el.on("dblclick", this.collapse, this);
39974 if(typeof this.config.minSize != "undefined"){
39975 this.split.minSize = this.config.minSize;
39977 if(typeof this.config.maxSize != "undefined"){
39978 this.split.maxSize = this.config.maxSize;
39980 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39981 this.hideSplitter();
39986 getHMaxSize : function(){
39987 var cmax = this.config.maxSize || 10000;
39988 var center = this.mgr.getRegion("center");
39989 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39992 getVMaxSize : function(){
39993 var cmax = this.config.maxSize || 10000;
39994 var center = this.mgr.getRegion("center");
39995 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39998 onSplitMove : function(split, newSize){
39999 this.fireEvent("resized", this, newSize);
40003 * Returns the {@link Roo.SplitBar} for this region.
40004 * @return {Roo.SplitBar}
40006 getSplitBar : function(){
40011 this.hideSplitter();
40012 Roo.bootstrap.layout.Split.superclass.hide.call(this);
40015 hideSplitter : function(){
40017 this.split.el.setLocation(-2000,-2000);
40018 this.split.el.hide();
40024 this.split.el.show();
40026 Roo.bootstrap.layout.Split.superclass.show.call(this);
40029 beforeSlide: function(){
40030 if(Roo.isGecko){// firefox overflow auto bug workaround
40031 this.bodyEl.clip();
40033 this.tabs.bodyEl.clip();
40035 if(this.activePanel){
40036 this.activePanel.getEl().clip();
40038 if(this.activePanel.beforeSlide){
40039 this.activePanel.beforeSlide();
40045 afterSlide : function(){
40046 if(Roo.isGecko){// firefox overflow auto bug workaround
40047 this.bodyEl.unclip();
40049 this.tabs.bodyEl.unclip();
40051 if(this.activePanel){
40052 this.activePanel.getEl().unclip();
40053 if(this.activePanel.afterSlide){
40054 this.activePanel.afterSlide();
40060 initAutoHide : function(){
40061 if(this.autoHide !== false){
40062 if(!this.autoHideHd){
40063 var st = new Roo.util.DelayedTask(this.slideIn, this);
40064 this.autoHideHd = {
40065 "mouseout": function(e){
40066 if(!e.within(this.el, true)){
40070 "mouseover" : function(e){
40076 this.el.on(this.autoHideHd);
40080 clearAutoHide : function(){
40081 if(this.autoHide !== false){
40082 this.el.un("mouseout", this.autoHideHd.mouseout);
40083 this.el.un("mouseover", this.autoHideHd.mouseover);
40087 clearMonitor : function(){
40088 Roo.get(document).un("click", this.slideInIf, this);
40091 // these names are backwards but not changed for compat
40092 slideOut : function(){
40093 if(this.isSlid || this.el.hasActiveFx()){
40096 this.isSlid = true;
40097 if(this.collapseBtn){
40098 this.collapseBtn.hide();
40100 this.closeBtnState = this.closeBtn.getStyle('display');
40101 this.closeBtn.hide();
40103 this.stickBtn.show();
40106 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40107 this.beforeSlide();
40108 this.el.setStyle("z-index", 10001);
40109 this.el.slideIn(this.getSlideAnchor(), {
40110 callback: function(){
40112 this.initAutoHide();
40113 Roo.get(document).on("click", this.slideInIf, this);
40114 this.fireEvent("slideshow", this);
40121 afterSlideIn : function(){
40122 this.clearAutoHide();
40123 this.isSlid = false;
40124 this.clearMonitor();
40125 this.el.setStyle("z-index", "");
40126 if(this.collapseBtn){
40127 this.collapseBtn.show();
40129 this.closeBtn.setStyle('display', this.closeBtnState);
40131 this.stickBtn.hide();
40133 this.fireEvent("slidehide", this);
40136 slideIn : function(cb){
40137 if(!this.isSlid || this.el.hasActiveFx()){
40141 this.isSlid = false;
40142 this.beforeSlide();
40143 this.el.slideOut(this.getSlideAnchor(), {
40144 callback: function(){
40145 this.el.setLeftTop(-10000, -10000);
40147 this.afterSlideIn();
40155 slideInIf : function(e){
40156 if(!e.within(this.el)){
40161 animateCollapse : function(){
40162 this.beforeSlide();
40163 this.el.setStyle("z-index", 20000);
40164 var anchor = this.getSlideAnchor();
40165 this.el.slideOut(anchor, {
40166 callback : function(){
40167 this.el.setStyle("z-index", "");
40168 this.collapsedEl.slideIn(anchor, {duration:.3});
40170 this.el.setLocation(-10000,-10000);
40172 this.fireEvent("collapsed", this);
40179 animateExpand : function(){
40180 this.beforeSlide();
40181 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40182 this.el.setStyle("z-index", 20000);
40183 this.collapsedEl.hide({
40186 this.el.slideIn(this.getSlideAnchor(), {
40187 callback : function(){
40188 this.el.setStyle("z-index", "");
40191 this.split.el.show();
40193 this.fireEvent("invalidated", this);
40194 this.fireEvent("expanded", this);
40222 getAnchor : function(){
40223 return this.anchors[this.position];
40226 getCollapseAnchor : function(){
40227 return this.canchors[this.position];
40230 getSlideAnchor : function(){
40231 return this.sanchors[this.position];
40234 getAlignAdj : function(){
40235 var cm = this.cmargins;
40236 switch(this.position){
40252 getExpandAdj : function(){
40253 var c = this.collapsedEl, cm = this.cmargins;
40254 switch(this.position){
40256 return [-(cm.right+c.getWidth()+cm.left), 0];
40259 return [cm.right+c.getWidth()+cm.left, 0];
40262 return [0, -(cm.top+cm.bottom+c.getHeight())];
40265 return [0, cm.top+cm.bottom+c.getHeight()];
40271 * Ext JS Library 1.1.1
40272 * Copyright(c) 2006-2007, Ext JS, LLC.
40274 * Originally Released Under LGPL - original licence link has changed is not relivant.
40277 * <script type="text/javascript">
40280 * These classes are private internal classes
40282 Roo.bootstrap.layout.Center = function(config){
40283 config.region = "center";
40284 Roo.bootstrap.layout.Region.call(this, config);
40285 this.visible = true;
40286 this.minWidth = config.minWidth || 20;
40287 this.minHeight = config.minHeight || 20;
40290 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40292 // center panel can't be hidden
40296 // center panel can't be hidden
40299 getMinWidth: function(){
40300 return this.minWidth;
40303 getMinHeight: function(){
40304 return this.minHeight;
40318 Roo.bootstrap.layout.North = function(config)
40320 config.region = 'north';
40321 config.cursor = 'n-resize';
40323 Roo.bootstrap.layout.Split.call(this, config);
40327 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40328 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40329 this.split.el.addClass("roo-layout-split-v");
40331 //var size = config.initialSize || config.height;
40332 //if(this.el && typeof size != "undefined"){
40333 // this.el.setHeight(size);
40336 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40338 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40341 onRender : function(ctr, pos)
40343 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40344 var size = this.config.initialSize || this.config.height;
40345 if(this.el && typeof size != "undefined"){
40346 this.el.setHeight(size);
40351 getBox : function(){
40352 if(this.collapsed){
40353 return this.collapsedEl.getBox();
40355 var box = this.el.getBox();
40357 box.height += this.split.el.getHeight();
40362 updateBox : function(box){
40363 if(this.split && !this.collapsed){
40364 box.height -= this.split.el.getHeight();
40365 this.split.el.setLeft(box.x);
40366 this.split.el.setTop(box.y+box.height);
40367 this.split.el.setWidth(box.width);
40369 if(this.collapsed){
40370 this.updateBody(box.width, null);
40372 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40380 Roo.bootstrap.layout.South = function(config){
40381 config.region = 'south';
40382 config.cursor = 's-resize';
40383 Roo.bootstrap.layout.Split.call(this, config);
40385 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40386 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40387 this.split.el.addClass("roo-layout-split-v");
40392 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40393 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40395 onRender : function(ctr, pos)
40397 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40398 var size = this.config.initialSize || this.config.height;
40399 if(this.el && typeof size != "undefined"){
40400 this.el.setHeight(size);
40405 getBox : function(){
40406 if(this.collapsed){
40407 return this.collapsedEl.getBox();
40409 var box = this.el.getBox();
40411 var sh = this.split.el.getHeight();
40418 updateBox : function(box){
40419 if(this.split && !this.collapsed){
40420 var sh = this.split.el.getHeight();
40423 this.split.el.setLeft(box.x);
40424 this.split.el.setTop(box.y-sh);
40425 this.split.el.setWidth(box.width);
40427 if(this.collapsed){
40428 this.updateBody(box.width, null);
40430 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40434 Roo.bootstrap.layout.East = function(config){
40435 config.region = "east";
40436 config.cursor = "e-resize";
40437 Roo.bootstrap.layout.Split.call(this, config);
40439 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40440 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40441 this.split.el.addClass("roo-layout-split-h");
40445 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40446 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40448 onRender : function(ctr, pos)
40450 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40451 var size = this.config.initialSize || this.config.width;
40452 if(this.el && typeof size != "undefined"){
40453 this.el.setWidth(size);
40458 getBox : function(){
40459 if(this.collapsed){
40460 return this.collapsedEl.getBox();
40462 var box = this.el.getBox();
40464 var sw = this.split.el.getWidth();
40471 updateBox : function(box){
40472 if(this.split && !this.collapsed){
40473 var sw = this.split.el.getWidth();
40475 this.split.el.setLeft(box.x);
40476 this.split.el.setTop(box.y);
40477 this.split.el.setHeight(box.height);
40480 if(this.collapsed){
40481 this.updateBody(null, box.height);
40483 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40487 Roo.bootstrap.layout.West = function(config){
40488 config.region = "west";
40489 config.cursor = "w-resize";
40491 Roo.bootstrap.layout.Split.call(this, config);
40493 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40494 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40495 this.split.el.addClass("roo-layout-split-h");
40499 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40500 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40502 onRender: function(ctr, pos)
40504 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40505 var size = this.config.initialSize || this.config.width;
40506 if(typeof size != "undefined"){
40507 this.el.setWidth(size);
40511 getBox : function(){
40512 if(this.collapsed){
40513 return this.collapsedEl.getBox();
40515 var box = this.el.getBox();
40516 if (box.width == 0) {
40517 box.width = this.config.width; // kludge?
40520 box.width += this.split.el.getWidth();
40525 updateBox : function(box){
40526 if(this.split && !this.collapsed){
40527 var sw = this.split.el.getWidth();
40529 this.split.el.setLeft(box.x+box.width);
40530 this.split.el.setTop(box.y);
40531 this.split.el.setHeight(box.height);
40533 if(this.collapsed){
40534 this.updateBody(null, box.height);
40536 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40538 });Roo.namespace("Roo.bootstrap.panel");/*
40540 * Ext JS Library 1.1.1
40541 * Copyright(c) 2006-2007, Ext JS, LLC.
40543 * Originally Released Under LGPL - original licence link has changed is not relivant.
40546 * <script type="text/javascript">
40549 * @class Roo.ContentPanel
40550 * @extends Roo.util.Observable
40551 * A basic ContentPanel element.
40552 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40553 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40554 * @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
40555 * @cfg {Boolean} closable True if the panel can be closed/removed
40556 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40557 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40558 * @cfg {Toolbar} toolbar A toolbar for this panel
40559 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40560 * @cfg {String} title The title for this panel
40561 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40562 * @cfg {String} url Calls {@link #setUrl} with this value
40563 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40564 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40565 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40566 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40567 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40568 * @cfg {Boolean} badges render the badges
40569 * @cfg {String} cls extra classes to use
40570 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40573 * Create a new ContentPanel.
40574 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40575 * @param {String/Object} config A string to set only the title or a config object
40576 * @param {String} content (optional) Set the HTML content for this panel
40577 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40579 Roo.bootstrap.panel.Content = function( config){
40581 this.tpl = config.tpl || false;
40583 var el = config.el;
40584 var content = config.content;
40586 if(config.autoCreate){ // xtype is available if this is called from factory
40589 this.el = Roo.get(el);
40590 if(!this.el && config && config.autoCreate){
40591 if(typeof config.autoCreate == "object"){
40592 if(!config.autoCreate.id){
40593 config.autoCreate.id = config.id||el;
40595 this.el = Roo.DomHelper.append(document.body,
40596 config.autoCreate, true);
40600 cls: (config.cls || '') +
40601 (config.background ? ' bg-' + config.background : '') +
40602 " roo-layout-inactive-content",
40605 if (config.iframe) {
40609 style : 'border: 0px',
40610 src : 'about:blank'
40616 elcfg.html = config.html;
40620 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40621 if (config.iframe) {
40622 this.iframeEl = this.el.select('iframe',true).first();
40627 this.closable = false;
40628 this.loaded = false;
40629 this.active = false;
40632 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40634 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40636 this.wrapEl = this.el; //this.el.wrap();
40638 if (config.toolbar.items) {
40639 ti = config.toolbar.items ;
40640 delete config.toolbar.items ;
40644 this.toolbar.render(this.wrapEl, 'before');
40645 for(var i =0;i < ti.length;i++) {
40646 // Roo.log(['add child', items[i]]);
40647 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40649 this.toolbar.items = nitems;
40650 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40651 delete config.toolbar;
40655 // xtype created footer. - not sure if will work as we normally have to render first..
40656 if (this.footer && !this.footer.el && this.footer.xtype) {
40657 if (!this.wrapEl) {
40658 this.wrapEl = this.el.wrap();
40661 this.footer.container = this.wrapEl.createChild();
40663 this.footer = Roo.factory(this.footer, Roo);
40668 if(typeof config == "string"){
40669 this.title = config;
40671 Roo.apply(this, config);
40675 this.resizeEl = Roo.get(this.resizeEl, true);
40677 this.resizeEl = this.el;
40679 // handle view.xtype
40687 * Fires when this panel is activated.
40688 * @param {Roo.ContentPanel} this
40692 * @event deactivate
40693 * Fires when this panel is activated.
40694 * @param {Roo.ContentPanel} this
40696 "deactivate" : true,
40700 * Fires when this panel is resized if fitToFrame is true.
40701 * @param {Roo.ContentPanel} this
40702 * @param {Number} width The width after any component adjustments
40703 * @param {Number} height The height after any component adjustments
40709 * Fires when this tab is created
40710 * @param {Roo.ContentPanel} this
40716 * Fires when this content is scrolled
40717 * @param {Roo.ContentPanel} this
40718 * @param {Event} scrollEvent
40729 if(this.autoScroll && !this.iframe){
40730 this.resizeEl.setStyle("overflow", "auto");
40731 this.resizeEl.on('scroll', this.onScroll, this);
40733 // fix randome scrolling
40734 //this.el.on('scroll', function() {
40735 // Roo.log('fix random scolling');
40736 // this.scrollTo('top',0);
40739 content = content || this.content;
40741 this.setContent(content);
40743 if(config && config.url){
40744 this.setUrl(this.url, this.params, this.loadOnce);
40749 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40751 if (this.view && typeof(this.view.xtype) != 'undefined') {
40752 this.view.el = this.el.appendChild(document.createElement("div"));
40753 this.view = Roo.factory(this.view);
40754 this.view.render && this.view.render(false, '');
40758 this.fireEvent('render', this);
40761 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40771 /* Resize Element - use this to work out scroll etc. */
40774 setRegion : function(region){
40775 this.region = region;
40776 this.setActiveClass(region && !this.background);
40780 setActiveClass: function(state)
40783 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40784 this.el.setStyle('position','relative');
40786 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40787 this.el.setStyle('position', 'absolute');
40792 * Returns the toolbar for this Panel if one was configured.
40793 * @return {Roo.Toolbar}
40795 getToolbar : function(){
40796 return this.toolbar;
40799 setActiveState : function(active)
40801 this.active = active;
40802 this.setActiveClass(active);
40804 if(this.fireEvent("deactivate", this) === false){
40809 this.fireEvent("activate", this);
40813 * Updates this panel's element (not for iframe)
40814 * @param {String} content The new content
40815 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40817 setContent : function(content, loadScripts){
40822 this.el.update(content, loadScripts);
40825 ignoreResize : function(w, h){
40826 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40829 this.lastSize = {width: w, height: h};
40834 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40835 * @return {Roo.UpdateManager} The UpdateManager
40837 getUpdateManager : function(){
40841 return this.el.getUpdateManager();
40844 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40845 * Does not work with IFRAME contents
40846 * @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:
40849 url: "your-url.php",
40850 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40851 callback: yourFunction,
40852 scope: yourObject, //(optional scope)
40855 text: "Loading...",
40861 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40862 * 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.
40863 * @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}
40864 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40865 * @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.
40866 * @return {Roo.ContentPanel} this
40874 var um = this.el.getUpdateManager();
40875 um.update.apply(um, arguments);
40881 * 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.
40882 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40883 * @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)
40884 * @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)
40885 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40887 setUrl : function(url, params, loadOnce){
40889 this.iframeEl.dom.src = url;
40893 if(this.refreshDelegate){
40894 this.removeListener("activate", this.refreshDelegate);
40896 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40897 this.on("activate", this.refreshDelegate);
40898 return this.el.getUpdateManager();
40901 _handleRefresh : function(url, params, loadOnce){
40902 if(!loadOnce || !this.loaded){
40903 var updater = this.el.getUpdateManager();
40904 updater.update(url, params, this._setLoaded.createDelegate(this));
40908 _setLoaded : function(){
40909 this.loaded = true;
40913 * Returns this panel's id
40916 getId : function(){
40921 * Returns this panel's element - used by regiosn to add.
40922 * @return {Roo.Element}
40924 getEl : function(){
40925 return this.wrapEl || this.el;
40930 adjustForComponents : function(width, height)
40932 //Roo.log('adjustForComponents ');
40933 if(this.resizeEl != this.el){
40934 width -= this.el.getFrameWidth('lr');
40935 height -= this.el.getFrameWidth('tb');
40938 var te = this.toolbar.getEl();
40939 te.setWidth(width);
40940 height -= te.getHeight();
40943 var te = this.footer.getEl();
40944 te.setWidth(width);
40945 height -= te.getHeight();
40949 if(this.adjustments){
40950 width += this.adjustments[0];
40951 height += this.adjustments[1];
40953 return {"width": width, "height": height};
40956 setSize : function(width, height){
40957 if(this.fitToFrame && !this.ignoreResize(width, height)){
40958 if(this.fitContainer && this.resizeEl != this.el){
40959 this.el.setSize(width, height);
40961 var size = this.adjustForComponents(width, height);
40963 this.iframeEl.setSize(width,height);
40966 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40967 this.fireEvent('resize', this, size.width, size.height);
40974 * Returns this panel's title
40977 getTitle : function(){
40979 if (typeof(this.title) != 'object') {
40984 for (var k in this.title) {
40985 if (!this.title.hasOwnProperty(k)) {
40989 if (k.indexOf('-') >= 0) {
40990 var s = k.split('-');
40991 for (var i = 0; i<s.length; i++) {
40992 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40995 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41002 * Set this panel's title
41003 * @param {String} title
41005 setTitle : function(title){
41006 this.title = title;
41008 this.region.updatePanelTitle(this, title);
41013 * Returns true is this panel was configured to be closable
41014 * @return {Boolean}
41016 isClosable : function(){
41017 return this.closable;
41020 beforeSlide : function(){
41022 this.resizeEl.clip();
41025 afterSlide : function(){
41027 this.resizeEl.unclip();
41031 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41032 * Will fail silently if the {@link #setUrl} method has not been called.
41033 * This does not activate the panel, just updates its content.
41035 refresh : function(){
41036 if(this.refreshDelegate){
41037 this.loaded = false;
41038 this.refreshDelegate();
41043 * Destroys this panel
41045 destroy : function(){
41046 this.el.removeAllListeners();
41047 var tempEl = document.createElement("span");
41048 tempEl.appendChild(this.el.dom);
41049 tempEl.innerHTML = "";
41055 * form - if the content panel contains a form - this is a reference to it.
41056 * @type {Roo.form.Form}
41060 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41061 * This contains a reference to it.
41067 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41077 * @param {Object} cfg Xtype definition of item to add.
41081 getChildContainer: function () {
41082 return this.getEl();
41086 onScroll : function(e)
41088 this.fireEvent('scroll', this, e);
41093 var ret = new Roo.factory(cfg);
41098 if (cfg.xtype.match(/^Form$/)) {
41101 //if (this.footer) {
41102 // el = this.footer.container.insertSibling(false, 'before');
41104 el = this.el.createChild();
41107 this.form = new Roo.form.Form(cfg);
41110 if ( this.form.allItems.length) {
41111 this.form.render(el.dom);
41115 // should only have one of theses..
41116 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41117 // views.. should not be just added - used named prop 'view''
41119 cfg.el = this.el.appendChild(document.createElement("div"));
41122 var ret = new Roo.factory(cfg);
41124 ret.render && ret.render(false, ''); // render blank..
41134 * @class Roo.bootstrap.panel.Grid
41135 * @extends Roo.bootstrap.panel.Content
41137 * Create a new GridPanel.
41138 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41139 * @param {Object} config A the config object
41145 Roo.bootstrap.panel.Grid = function(config)
41149 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41150 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41152 config.el = this.wrapper;
41153 //this.el = this.wrapper;
41155 if (config.container) {
41156 // ctor'ed from a Border/panel.grid
41159 this.wrapper.setStyle("overflow", "hidden");
41160 this.wrapper.addClass('roo-grid-container');
41165 if(config.toolbar){
41166 var tool_el = this.wrapper.createChild();
41167 this.toolbar = Roo.factory(config.toolbar);
41169 if (config.toolbar.items) {
41170 ti = config.toolbar.items ;
41171 delete config.toolbar.items ;
41175 this.toolbar.render(tool_el);
41176 for(var i =0;i < ti.length;i++) {
41177 // Roo.log(['add child', items[i]]);
41178 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41180 this.toolbar.items = nitems;
41182 delete config.toolbar;
41185 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41186 config.grid.scrollBody = true;;
41187 config.grid.monitorWindowResize = false; // turn off autosizing
41188 config.grid.autoHeight = false;
41189 config.grid.autoWidth = false;
41191 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41193 if (config.background) {
41194 // render grid on panel activation (if panel background)
41195 this.on('activate', function(gp) {
41196 if (!gp.grid.rendered) {
41197 gp.grid.render(this.wrapper);
41198 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41203 this.grid.render(this.wrapper);
41204 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41207 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41208 // ??? needed ??? config.el = this.wrapper;
41213 // xtype created footer. - not sure if will work as we normally have to render first..
41214 if (this.footer && !this.footer.el && this.footer.xtype) {
41216 var ctr = this.grid.getView().getFooterPanel(true);
41217 this.footer.dataSource = this.grid.dataSource;
41218 this.footer = Roo.factory(this.footer, Roo);
41219 this.footer.render(ctr);
41229 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41230 getId : function(){
41231 return this.grid.id;
41235 * Returns the grid for this panel
41236 * @return {Roo.bootstrap.Table}
41238 getGrid : function(){
41242 setSize : function(width, height){
41243 if(!this.ignoreResize(width, height)){
41244 var grid = this.grid;
41245 var size = this.adjustForComponents(width, height);
41246 // tfoot is not a footer?
41249 var gridel = grid.getGridEl();
41250 gridel.setSize(size.width, size.height);
41252 var tbd = grid.getGridEl().select('tbody', true).first();
41253 var thd = grid.getGridEl().select('thead',true).first();
41254 var tbf= grid.getGridEl().select('tfoot', true).first();
41257 size.height -= tbf.getHeight();
41260 size.height -= thd.getHeight();
41263 tbd.setSize(size.width, size.height );
41264 // this is for the account management tab -seems to work there.
41265 var thd = grid.getGridEl().select('thead',true).first();
41267 // tbd.setSize(size.width, size.height - thd.getHeight());
41276 beforeSlide : function(){
41277 this.grid.getView().scroller.clip();
41280 afterSlide : function(){
41281 this.grid.getView().scroller.unclip();
41284 destroy : function(){
41285 this.grid.destroy();
41287 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41292 * @class Roo.bootstrap.panel.Nest
41293 * @extends Roo.bootstrap.panel.Content
41295 * Create a new Panel, that can contain a layout.Border.
41298 * @param {Roo.BorderLayout} layout The layout for this panel
41299 * @param {String/Object} config A string to set only the title or a config object
41301 Roo.bootstrap.panel.Nest = function(config)
41303 // construct with only one argument..
41304 /* FIXME - implement nicer consturctors
41305 if (layout.layout) {
41307 layout = config.layout;
41308 delete config.layout;
41310 if (layout.xtype && !layout.getEl) {
41311 // then layout needs constructing..
41312 layout = Roo.factory(layout, Roo);
41316 config.el = config.layout.getEl();
41318 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41320 config.layout.monitorWindowResize = false; // turn off autosizing
41321 this.layout = config.layout;
41322 this.layout.getEl().addClass("roo-layout-nested-layout");
41323 this.layout.parent = this;
41330 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41332 setSize : function(width, height){
41333 if(!this.ignoreResize(width, height)){
41334 var size = this.adjustForComponents(width, height);
41335 var el = this.layout.getEl();
41336 if (size.height < 1) {
41337 el.setWidth(size.width);
41339 el.setSize(size.width, size.height);
41341 var touch = el.dom.offsetWidth;
41342 this.layout.layout();
41343 // ie requires a double layout on the first pass
41344 if(Roo.isIE && !this.initialized){
41345 this.initialized = true;
41346 this.layout.layout();
41351 // activate all subpanels if not currently active..
41353 setActiveState : function(active){
41354 this.active = active;
41355 this.setActiveClass(active);
41358 this.fireEvent("deactivate", this);
41362 this.fireEvent("activate", this);
41363 // not sure if this should happen before or after..
41364 if (!this.layout) {
41365 return; // should not happen..
41368 for (var r in this.layout.regions) {
41369 reg = this.layout.getRegion(r);
41370 if (reg.getActivePanel()) {
41371 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41372 reg.setActivePanel(reg.getActivePanel());
41375 if (!reg.panels.length) {
41378 reg.showPanel(reg.getPanel(0));
41387 * Returns the nested BorderLayout for this panel
41388 * @return {Roo.BorderLayout}
41390 getLayout : function(){
41391 return this.layout;
41395 * Adds a xtype elements to the layout of the nested panel
41399 xtype : 'ContentPanel',
41406 xtype : 'NestedLayoutPanel',
41412 items : [ ... list of content panels or nested layout panels.. ]
41416 * @param {Object} cfg Xtype definition of item to add.
41418 addxtype : function(cfg) {
41419 return this.layout.addxtype(cfg);
41424 * Ext JS Library 1.1.1
41425 * Copyright(c) 2006-2007, Ext JS, LLC.
41427 * Originally Released Under LGPL - original licence link has changed is not relivant.
41430 * <script type="text/javascript">
41433 * @class Roo.TabPanel
41434 * @extends Roo.util.Observable
41435 * A lightweight tab container.
41439 // basic tabs 1, built from existing content
41440 var tabs = new Roo.TabPanel("tabs1");
41441 tabs.addTab("script", "View Script");
41442 tabs.addTab("markup", "View Markup");
41443 tabs.activate("script");
41445 // more advanced tabs, built from javascript
41446 var jtabs = new Roo.TabPanel("jtabs");
41447 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41449 // set up the UpdateManager
41450 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41451 var updater = tab2.getUpdateManager();
41452 updater.setDefaultUrl("ajax1.htm");
41453 tab2.on('activate', updater.refresh, updater, true);
41455 // Use setUrl for Ajax loading
41456 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41457 tab3.setUrl("ajax2.htm", null, true);
41460 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41463 jtabs.activate("jtabs-1");
41466 * Create a new TabPanel.
41467 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41468 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41470 Roo.bootstrap.panel.Tabs = function(config){
41472 * The container element for this TabPanel.
41473 * @type Roo.Element
41475 this.el = Roo.get(config.el);
41478 if(typeof config == "boolean"){
41479 this.tabPosition = config ? "bottom" : "top";
41481 Roo.apply(this, config);
41485 if(this.tabPosition == "bottom"){
41486 // if tabs are at the bottom = create the body first.
41487 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41488 this.el.addClass("roo-tabs-bottom");
41490 // next create the tabs holders
41492 if (this.tabPosition == "west"){
41494 var reg = this.region; // fake it..
41496 if (!reg.mgr.parent) {
41499 reg = reg.mgr.parent.region;
41501 Roo.log("got nest?");
41503 if (reg.mgr.getRegion('west')) {
41504 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41505 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41506 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41507 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41508 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41516 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41517 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41518 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41519 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41524 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41527 // finally - if tabs are at the top, then create the body last..
41528 if(this.tabPosition != "bottom"){
41529 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41530 * @type Roo.Element
41532 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41533 this.el.addClass("roo-tabs-top");
41537 this.bodyEl.setStyle("position", "relative");
41539 this.active = null;
41540 this.activateDelegate = this.activate.createDelegate(this);
41545 * Fires when the active tab changes
41546 * @param {Roo.TabPanel} this
41547 * @param {Roo.TabPanelItem} activePanel The new active tab
41551 * @event beforetabchange
41552 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41553 * @param {Roo.TabPanel} this
41554 * @param {Object} e Set cancel to true on this object to cancel the tab change
41555 * @param {Roo.TabPanelItem} tab The tab being changed to
41557 "beforetabchange" : true
41560 Roo.EventManager.onWindowResize(this.onResize, this);
41561 this.cpad = this.el.getPadding("lr");
41562 this.hiddenCount = 0;
41565 // toolbar on the tabbar support...
41566 if (this.toolbar) {
41567 alert("no toolbar support yet");
41568 this.toolbar = false;
41570 var tcfg = this.toolbar;
41571 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41572 this.toolbar = new Roo.Toolbar(tcfg);
41573 if (Roo.isSafari) {
41574 var tbl = tcfg.container.child('table', true);
41575 tbl.setAttribute('width', '100%');
41583 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41586 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41588 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41590 tabPosition : "top",
41592 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41594 currentTabWidth : 0,
41596 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41600 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41604 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41606 preferredTabWidth : 175,
41608 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41610 resizeTabs : false,
41612 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41614 monitorResize : true,
41616 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41618 toolbar : false, // set by caller..
41620 region : false, /// set by caller
41622 disableTooltips : true, // not used yet...
41625 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41626 * @param {String} id The id of the div to use <b>or create</b>
41627 * @param {String} text The text for the tab
41628 * @param {String} content (optional) Content to put in the TabPanelItem body
41629 * @param {Boolean} closable (optional) True to create a close icon on the tab
41630 * @return {Roo.TabPanelItem} The created TabPanelItem
41632 addTab : function(id, text, content, closable, tpl)
41634 var item = new Roo.bootstrap.panel.TabItem({
41638 closable : closable,
41641 this.addTabItem(item);
41643 item.setContent(content);
41649 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41650 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41651 * @return {Roo.TabPanelItem}
41653 getTab : function(id){
41654 return this.items[id];
41658 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41659 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41661 hideTab : function(id){
41662 var t = this.items[id];
41665 this.hiddenCount++;
41666 this.autoSizeTabs();
41671 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41672 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41674 unhideTab : function(id){
41675 var t = this.items[id];
41677 t.setHidden(false);
41678 this.hiddenCount--;
41679 this.autoSizeTabs();
41684 * Adds an existing {@link Roo.TabPanelItem}.
41685 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41687 addTabItem : function(item)
41689 this.items[item.id] = item;
41690 this.items.push(item);
41691 this.autoSizeTabs();
41692 // if(this.resizeTabs){
41693 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41694 // this.autoSizeTabs();
41696 // item.autoSize();
41701 * Removes a {@link Roo.TabPanelItem}.
41702 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41704 removeTab : function(id){
41705 var items = this.items;
41706 var tab = items[id];
41707 if(!tab) { return; }
41708 var index = items.indexOf(tab);
41709 if(this.active == tab && items.length > 1){
41710 var newTab = this.getNextAvailable(index);
41715 this.stripEl.dom.removeChild(tab.pnode.dom);
41716 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41717 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41719 items.splice(index, 1);
41720 delete this.items[tab.id];
41721 tab.fireEvent("close", tab);
41722 tab.purgeListeners();
41723 this.autoSizeTabs();
41726 getNextAvailable : function(start){
41727 var items = this.items;
41729 // look for a next tab that will slide over to
41730 // replace the one being removed
41731 while(index < items.length){
41732 var item = items[++index];
41733 if(item && !item.isHidden()){
41737 // if one isn't found select the previous tab (on the left)
41740 var item = items[--index];
41741 if(item && !item.isHidden()){
41749 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41750 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41752 disableTab : function(id){
41753 var tab = this.items[id];
41754 if(tab && this.active != tab){
41760 * Enables a {@link Roo.TabPanelItem} that is disabled.
41761 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41763 enableTab : function(id){
41764 var tab = this.items[id];
41769 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41770 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41771 * @return {Roo.TabPanelItem} The TabPanelItem.
41773 activate : function(id)
41775 //Roo.log('activite:' + id);
41777 var tab = this.items[id];
41781 if(tab == this.active || tab.disabled){
41785 this.fireEvent("beforetabchange", this, e, tab);
41786 if(e.cancel !== true && !tab.disabled){
41788 this.active.hide();
41790 this.active = this.items[id];
41791 this.active.show();
41792 this.fireEvent("tabchange", this, this.active);
41798 * Gets the active {@link Roo.TabPanelItem}.
41799 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41801 getActiveTab : function(){
41802 return this.active;
41806 * Updates the tab body element to fit the height of the container element
41807 * for overflow scrolling
41808 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41810 syncHeight : function(targetHeight){
41811 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41812 var bm = this.bodyEl.getMargins();
41813 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41814 this.bodyEl.setHeight(newHeight);
41818 onResize : function(){
41819 if(this.monitorResize){
41820 this.autoSizeTabs();
41825 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41827 beginUpdate : function(){
41828 this.updating = true;
41832 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41834 endUpdate : function(){
41835 this.updating = false;
41836 this.autoSizeTabs();
41840 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41842 autoSizeTabs : function()
41844 var count = this.items.length;
41845 var vcount = count - this.hiddenCount;
41848 this.stripEl.hide();
41850 this.stripEl.show();
41853 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41858 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41859 var availWidth = Math.floor(w / vcount);
41860 var b = this.stripBody;
41861 if(b.getWidth() > w){
41862 var tabs = this.items;
41863 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41864 if(availWidth < this.minTabWidth){
41865 /*if(!this.sleft){ // incomplete scrolling code
41866 this.createScrollButtons();
41869 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41872 if(this.currentTabWidth < this.preferredTabWidth){
41873 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41879 * Returns the number of tabs in this TabPanel.
41882 getCount : function(){
41883 return this.items.length;
41887 * Resizes all the tabs to the passed width
41888 * @param {Number} The new width
41890 setTabWidth : function(width){
41891 this.currentTabWidth = width;
41892 for(var i = 0, len = this.items.length; i < len; i++) {
41893 if(!this.items[i].isHidden()) {
41894 this.items[i].setWidth(width);
41900 * Destroys this TabPanel
41901 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41903 destroy : function(removeEl){
41904 Roo.EventManager.removeResizeListener(this.onResize, this);
41905 for(var i = 0, len = this.items.length; i < len; i++){
41906 this.items[i].purgeListeners();
41908 if(removeEl === true){
41909 this.el.update("");
41914 createStrip : function(container)
41916 var strip = document.createElement("nav");
41917 strip.className = Roo.bootstrap.version == 4 ?
41918 "navbar-light bg-light" :
41919 "navbar navbar-default"; //"x-tabs-wrap";
41920 container.appendChild(strip);
41924 createStripList : function(strip)
41926 // div wrapper for retard IE
41927 // returns the "tr" element.
41928 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41929 //'<div class="x-tabs-strip-wrap">'+
41930 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41931 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41932 return strip.firstChild; //.firstChild.firstChild.firstChild;
41934 createBody : function(container)
41936 var body = document.createElement("div");
41937 Roo.id(body, "tab-body");
41938 //Roo.fly(body).addClass("x-tabs-body");
41939 Roo.fly(body).addClass("tab-content");
41940 container.appendChild(body);
41943 createItemBody :function(bodyEl, id){
41944 var body = Roo.getDom(id);
41946 body = document.createElement("div");
41949 //Roo.fly(body).addClass("x-tabs-item-body");
41950 Roo.fly(body).addClass("tab-pane");
41951 bodyEl.insertBefore(body, bodyEl.firstChild);
41955 createStripElements : function(stripEl, text, closable, tpl)
41957 var td = document.createElement("li"); // was td..
41958 td.className = 'nav-item';
41960 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41963 stripEl.appendChild(td);
41965 td.className = "x-tabs-closable";
41966 if(!this.closeTpl){
41967 this.closeTpl = new Roo.Template(
41968 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41969 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41970 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41973 var el = this.closeTpl.overwrite(td, {"text": text});
41974 var close = el.getElementsByTagName("div")[0];
41975 var inner = el.getElementsByTagName("em")[0];
41976 return {"el": el, "close": close, "inner": inner};
41979 // not sure what this is..
41980 // if(!this.tabTpl){
41981 //this.tabTpl = new Roo.Template(
41982 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41983 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41985 // this.tabTpl = new Roo.Template(
41986 // '<a href="#">' +
41987 // '<span unselectable="on"' +
41988 // (this.disableTooltips ? '' : ' title="{text}"') +
41989 // ' >{text}</span></a>'
41995 var template = tpl || this.tabTpl || false;
41998 template = new Roo.Template(
41999 Roo.bootstrap.version == 4 ?
42001 '<a class="nav-link" href="#" unselectable="on"' +
42002 (this.disableTooltips ? '' : ' title="{text}"') +
42005 '<a class="nav-link" href="#">' +
42006 '<span unselectable="on"' +
42007 (this.disableTooltips ? '' : ' title="{text}"') +
42008 ' >{text}</span></a>'
42013 switch (typeof(template)) {
42017 template = new Roo.Template(template);
42023 var el = template.overwrite(td, {"text": text});
42025 var inner = el.getElementsByTagName("span")[0];
42027 return {"el": el, "inner": inner};
42035 * @class Roo.TabPanelItem
42036 * @extends Roo.util.Observable
42037 * Represents an individual item (tab plus body) in a TabPanel.
42038 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42039 * @param {String} id The id of this TabPanelItem
42040 * @param {String} text The text for the tab of this TabPanelItem
42041 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42043 Roo.bootstrap.panel.TabItem = function(config){
42045 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42046 * @type Roo.TabPanel
42048 this.tabPanel = config.panel;
42050 * The id for this TabPanelItem
42053 this.id = config.id;
42055 this.disabled = false;
42057 this.text = config.text;
42059 this.loaded = false;
42060 this.closable = config.closable;
42063 * The body element for this TabPanelItem.
42064 * @type Roo.Element
42066 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42067 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42068 this.bodyEl.setStyle("display", "block");
42069 this.bodyEl.setStyle("zoom", "1");
42070 //this.hideAction();
42072 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42074 this.el = Roo.get(els.el);
42075 this.inner = Roo.get(els.inner, true);
42076 this.textEl = Roo.bootstrap.version == 4 ?
42077 this.el : Roo.get(this.el.dom.firstChild, true);
42079 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42080 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42083 // this.el.on("mousedown", this.onTabMouseDown, this);
42084 this.el.on("click", this.onTabClick, this);
42086 if(config.closable){
42087 var c = Roo.get(els.close, true);
42088 c.dom.title = this.closeText;
42089 c.addClassOnOver("close-over");
42090 c.on("click", this.closeClick, this);
42096 * Fires when this tab becomes the active tab.
42097 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42098 * @param {Roo.TabPanelItem} this
42102 * @event beforeclose
42103 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42104 * @param {Roo.TabPanelItem} this
42105 * @param {Object} e Set cancel to true on this object to cancel the close.
42107 "beforeclose": true,
42110 * Fires when this tab is closed.
42111 * @param {Roo.TabPanelItem} this
42115 * @event deactivate
42116 * Fires when this tab is no longer the active tab.
42117 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42118 * @param {Roo.TabPanelItem} this
42120 "deactivate" : true
42122 this.hidden = false;
42124 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42127 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42129 purgeListeners : function(){
42130 Roo.util.Observable.prototype.purgeListeners.call(this);
42131 this.el.removeAllListeners();
42134 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42137 this.status_node.addClass("active");
42140 this.tabPanel.stripWrap.repaint();
42142 this.fireEvent("activate", this.tabPanel, this);
42146 * Returns true if this tab is the active tab.
42147 * @return {Boolean}
42149 isActive : function(){
42150 return this.tabPanel.getActiveTab() == this;
42154 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42157 this.status_node.removeClass("active");
42159 this.fireEvent("deactivate", this.tabPanel, this);
42162 hideAction : function(){
42163 this.bodyEl.hide();
42164 this.bodyEl.setStyle("position", "absolute");
42165 this.bodyEl.setLeft("-20000px");
42166 this.bodyEl.setTop("-20000px");
42169 showAction : function(){
42170 this.bodyEl.setStyle("position", "relative");
42171 this.bodyEl.setTop("");
42172 this.bodyEl.setLeft("");
42173 this.bodyEl.show();
42177 * Set the tooltip for the tab.
42178 * @param {String} tooltip The tab's tooltip
42180 setTooltip : function(text){
42181 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42182 this.textEl.dom.qtip = text;
42183 this.textEl.dom.removeAttribute('title');
42185 this.textEl.dom.title = text;
42189 onTabClick : function(e){
42190 e.preventDefault();
42191 this.tabPanel.activate(this.id);
42194 onTabMouseDown : function(e){
42195 e.preventDefault();
42196 this.tabPanel.activate(this.id);
42199 getWidth : function(){
42200 return this.inner.getWidth();
42203 setWidth : function(width){
42204 var iwidth = width - this.linode.getPadding("lr");
42205 this.inner.setWidth(iwidth);
42206 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42207 this.linode.setWidth(width);
42211 * Show or hide the tab
42212 * @param {Boolean} hidden True to hide or false to show.
42214 setHidden : function(hidden){
42215 this.hidden = hidden;
42216 this.linode.setStyle("display", hidden ? "none" : "");
42220 * Returns true if this tab is "hidden"
42221 * @return {Boolean}
42223 isHidden : function(){
42224 return this.hidden;
42228 * Returns the text for this tab
42231 getText : function(){
42235 autoSize : function(){
42236 //this.el.beginMeasure();
42237 this.textEl.setWidth(1);
42239 * #2804 [new] Tabs in Roojs
42240 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42242 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42243 //this.el.endMeasure();
42247 * Sets the text for the tab (Note: this also sets the tooltip text)
42248 * @param {String} text The tab's text and tooltip
42250 setText : function(text){
42252 this.textEl.update(text);
42253 this.setTooltip(text);
42254 //if(!this.tabPanel.resizeTabs){
42255 // this.autoSize();
42259 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42261 activate : function(){
42262 this.tabPanel.activate(this.id);
42266 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42268 disable : function(){
42269 if(this.tabPanel.active != this){
42270 this.disabled = true;
42271 this.status_node.addClass("disabled");
42276 * Enables this TabPanelItem if it was previously disabled.
42278 enable : function(){
42279 this.disabled = false;
42280 this.status_node.removeClass("disabled");
42284 * Sets the content for this TabPanelItem.
42285 * @param {String} content The content
42286 * @param {Boolean} loadScripts true to look for and load scripts
42288 setContent : function(content, loadScripts){
42289 this.bodyEl.update(content, loadScripts);
42293 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42294 * @return {Roo.UpdateManager} The UpdateManager
42296 getUpdateManager : function(){
42297 return this.bodyEl.getUpdateManager();
42301 * Set a URL to be used to load the content for this TabPanelItem.
42302 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42303 * @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)
42304 * @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)
42305 * @return {Roo.UpdateManager} The UpdateManager
42307 setUrl : function(url, params, loadOnce){
42308 if(this.refreshDelegate){
42309 this.un('activate', this.refreshDelegate);
42311 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42312 this.on("activate", this.refreshDelegate);
42313 return this.bodyEl.getUpdateManager();
42317 _handleRefresh : function(url, params, loadOnce){
42318 if(!loadOnce || !this.loaded){
42319 var updater = this.bodyEl.getUpdateManager();
42320 updater.update(url, params, this._setLoaded.createDelegate(this));
42325 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42326 * Will fail silently if the setUrl method has not been called.
42327 * This does not activate the panel, just updates its content.
42329 refresh : function(){
42330 if(this.refreshDelegate){
42331 this.loaded = false;
42332 this.refreshDelegate();
42337 _setLoaded : function(){
42338 this.loaded = true;
42342 closeClick : function(e){
42345 this.fireEvent("beforeclose", this, o);
42346 if(o.cancel !== true){
42347 this.tabPanel.removeTab(this.id);
42351 * The text displayed in the tooltip for the close icon.
42354 closeText : "Close this tab"
42357 * This script refer to:
42358 * Title: International Telephone Input
42359 * Author: Jack O'Connor
42360 * Code version: v12.1.12
42361 * Availability: https://github.com/jackocnr/intl-tel-input.git
42364 Roo.bootstrap.PhoneInputData = function() {
42367 "Afghanistan (افغانستان)",
42372 "Albania (Shqipëri)",
42377 "Algeria (الجزائر)",
42402 "Antigua and Barbuda",
42412 "Armenia (Հայաստան)",
42428 "Austria (Österreich)",
42433 "Azerbaijan (Azərbaycan)",
42443 "Bahrain (البحرين)",
42448 "Bangladesh (বাংলাদেশ)",
42458 "Belarus (Беларусь)",
42463 "Belgium (België)",
42493 "Bosnia and Herzegovina (Босна и Херцеговина)",
42508 "British Indian Ocean Territory",
42513 "British Virgin Islands",
42523 "Bulgaria (България)",
42533 "Burundi (Uburundi)",
42538 "Cambodia (កម្ពុជា)",
42543 "Cameroon (Cameroun)",
42552 ["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"]
42555 "Cape Verde (Kabu Verdi)",
42560 "Caribbean Netherlands",
42571 "Central African Republic (République centrafricaine)",
42591 "Christmas Island",
42597 "Cocos (Keeling) Islands",
42608 "Comoros (جزر القمر)",
42613 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42618 "Congo (Republic) (Congo-Brazzaville)",
42638 "Croatia (Hrvatska)",
42659 "Czech Republic (Česká republika)",
42664 "Denmark (Danmark)",
42679 "Dominican Republic (República Dominicana)",
42683 ["809", "829", "849"]
42701 "Equatorial Guinea (Guinea Ecuatorial)",
42721 "Falkland Islands (Islas Malvinas)",
42726 "Faroe Islands (Føroyar)",
42747 "French Guiana (Guyane française)",
42752 "French Polynesia (Polynésie française)",
42767 "Georgia (საქართველო)",
42772 "Germany (Deutschland)",
42792 "Greenland (Kalaallit Nunaat)",
42829 "Guinea-Bissau (Guiné Bissau)",
42854 "Hungary (Magyarország)",
42859 "Iceland (Ísland)",
42879 "Iraq (العراق)",
42895 "Israel (ישראל)",
42922 "Jordan (الأردن)",
42927 "Kazakhstan (Казахстан)",
42948 "Kuwait (الكويت)",
42953 "Kyrgyzstan (Кыргызстан)",
42963 "Latvia (Latvija)",
42968 "Lebanon (لبنان)",
42983 "Libya (ليبيا)",
42993 "Lithuania (Lietuva)",
43008 "Macedonia (FYROM) (Македонија)",
43013 "Madagascar (Madagasikara)",
43043 "Marshall Islands",
43053 "Mauritania (موريتانيا)",
43058 "Mauritius (Moris)",
43079 "Moldova (Republica Moldova)",
43089 "Mongolia (Монгол)",
43094 "Montenegro (Crna Gora)",
43104 "Morocco (المغرب)",
43110 "Mozambique (Moçambique)",
43115 "Myanmar (Burma) (မြန်မာ)",
43120 "Namibia (Namibië)",
43135 "Netherlands (Nederland)",
43140 "New Caledonia (Nouvelle-Calédonie)",
43175 "North Korea (조선 민주주의 인민 공화국)",
43180 "Northern Mariana Islands",
43196 "Pakistan (پاکستان)",
43206 "Palestine (فلسطين)",
43216 "Papua New Guinea",
43258 "Réunion (La Réunion)",
43264 "Romania (România)",
43280 "Saint Barthélemy",
43291 "Saint Kitts and Nevis",
43301 "Saint Martin (Saint-Martin (partie française))",
43307 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43312 "Saint Vincent and the Grenadines",
43327 "São Tomé and Príncipe (São Tomé e Príncipe)",
43332 "Saudi Arabia (المملكة العربية السعودية)",
43337 "Senegal (Sénégal)",
43367 "Slovakia (Slovensko)",
43372 "Slovenia (Slovenija)",
43382 "Somalia (Soomaaliya)",
43392 "South Korea (대한민국)",
43397 "South Sudan (جنوب السودان)",
43407 "Sri Lanka (ශ්රී ලංකාව)",
43412 "Sudan (السودان)",
43422 "Svalbard and Jan Mayen",
43433 "Sweden (Sverige)",
43438 "Switzerland (Schweiz)",
43443 "Syria (سوريا)",
43488 "Trinidad and Tobago",
43493 "Tunisia (تونس)",
43498 "Turkey (Türkiye)",
43508 "Turks and Caicos Islands",
43518 "U.S. Virgin Islands",
43528 "Ukraine (Україна)",
43533 "United Arab Emirates (الإمارات العربية المتحدة)",
43555 "Uzbekistan (Oʻzbekiston)",
43565 "Vatican City (Città del Vaticano)",
43576 "Vietnam (Việt Nam)",
43581 "Wallis and Futuna (Wallis-et-Futuna)",
43586 "Western Sahara (الصحراء الغربية)",
43592 "Yemen (اليمن)",
43616 * This script refer to:
43617 * Title: International Telephone Input
43618 * Author: Jack O'Connor
43619 * Code version: v12.1.12
43620 * Availability: https://github.com/jackocnr/intl-tel-input.git
43624 * @class Roo.bootstrap.PhoneInput
43625 * @extends Roo.bootstrap.TriggerField
43626 * An input with International dial-code selection
43628 * @cfg {String} defaultDialCode default '+852'
43629 * @cfg {Array} preferedCountries default []
43632 * Create a new PhoneInput.
43633 * @param {Object} config Configuration options
43636 Roo.bootstrap.PhoneInput = function(config) {
43637 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43640 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43642 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43644 listWidth: undefined,
43646 selectedClass: 'active',
43648 invalidClass : "has-warning",
43650 validClass: 'has-success',
43652 allowed: '0123456789',
43657 * @cfg {String} defaultDialCode The default dial code when initializing the input
43659 defaultDialCode: '+852',
43662 * @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
43664 preferedCountries: false,
43666 getAutoCreate : function()
43668 var data = Roo.bootstrap.PhoneInputData();
43669 var align = this.labelAlign || this.parentLabelAlign();
43672 this.allCountries = [];
43673 this.dialCodeMapping = [];
43675 for (var i = 0; i < data.length; i++) {
43677 this.allCountries[i] = {
43681 priority: c[3] || 0,
43682 areaCodes: c[4] || null
43684 this.dialCodeMapping[c[2]] = {
43687 priority: c[3] || 0,
43688 areaCodes: c[4] || null
43700 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43701 maxlength: this.max_length,
43702 cls : 'form-control tel-input',
43703 autocomplete: 'new-password'
43706 var hiddenInput = {
43709 cls: 'hidden-tel-input'
43713 hiddenInput.name = this.name;
43716 if (this.disabled) {
43717 input.disabled = true;
43720 var flag_container = {
43737 cls: this.hasFeedback ? 'has-feedback' : '',
43743 cls: 'dial-code-holder',
43750 cls: 'roo-select2-container input-group',
43757 if (this.fieldLabel.length) {
43760 tooltip: 'This field is required'
43766 cls: 'control-label',
43772 html: this.fieldLabel
43775 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43781 if(this.indicatorpos == 'right') {
43782 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43789 if(align == 'left') {
43797 if(this.labelWidth > 12){
43798 label.style = "width: " + this.labelWidth + 'px';
43800 if(this.labelWidth < 13 && this.labelmd == 0){
43801 this.labelmd = this.labelWidth;
43803 if(this.labellg > 0){
43804 label.cls += ' col-lg-' + this.labellg;
43805 input.cls += ' col-lg-' + (12 - this.labellg);
43807 if(this.labelmd > 0){
43808 label.cls += ' col-md-' + this.labelmd;
43809 container.cls += ' col-md-' + (12 - this.labelmd);
43811 if(this.labelsm > 0){
43812 label.cls += ' col-sm-' + this.labelsm;
43813 container.cls += ' col-sm-' + (12 - this.labelsm);
43815 if(this.labelxs > 0){
43816 label.cls += ' col-xs-' + this.labelxs;
43817 container.cls += ' col-xs-' + (12 - this.labelxs);
43827 var settings = this;
43829 ['xs','sm','md','lg'].map(function(size){
43830 if (settings[size]) {
43831 cfg.cls += ' col-' + size + '-' + settings[size];
43835 this.store = new Roo.data.Store({
43836 proxy : new Roo.data.MemoryProxy({}),
43837 reader : new Roo.data.JsonReader({
43848 'name' : 'dialCode',
43852 'name' : 'priority',
43856 'name' : 'areaCodes',
43863 if(!this.preferedCountries) {
43864 this.preferedCountries = [
43871 var p = this.preferedCountries.reverse();
43874 for (var i = 0; i < p.length; i++) {
43875 for (var j = 0; j < this.allCountries.length; j++) {
43876 if(this.allCountries[j].iso2 == p[i]) {
43877 var t = this.allCountries[j];
43878 this.allCountries.splice(j,1);
43879 this.allCountries.unshift(t);
43885 this.store.proxy.data = {
43887 data: this.allCountries
43893 initEvents : function()
43896 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43898 this.indicator = this.indicatorEl();
43899 this.flag = this.flagEl();
43900 this.dialCodeHolder = this.dialCodeHolderEl();
43902 this.trigger = this.el.select('div.flag-box',true).first();
43903 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43908 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43909 _this.list.setWidth(lw);
43912 this.list.on('mouseover', this.onViewOver, this);
43913 this.list.on('mousemove', this.onViewMove, this);
43914 this.inputEl().on("keyup", this.onKeyUp, this);
43915 this.inputEl().on("keypress", this.onKeyPress, this);
43917 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43919 this.view = new Roo.View(this.list, this.tpl, {
43920 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43923 this.view.on('click', this.onViewClick, this);
43924 this.setValue(this.defaultDialCode);
43927 onTriggerClick : function(e)
43929 Roo.log('trigger click');
43934 if(this.isExpanded()){
43936 this.hasFocus = false;
43938 this.store.load({});
43939 this.hasFocus = true;
43944 isExpanded : function()
43946 return this.list.isVisible();
43949 collapse : function()
43951 if(!this.isExpanded()){
43955 Roo.get(document).un('mousedown', this.collapseIf, this);
43956 Roo.get(document).un('mousewheel', this.collapseIf, this);
43957 this.fireEvent('collapse', this);
43961 expand : function()
43965 if(this.isExpanded() || !this.hasFocus){
43969 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43970 this.list.setWidth(lw);
43973 this.restrictHeight();
43975 Roo.get(document).on('mousedown', this.collapseIf, this);
43976 Roo.get(document).on('mousewheel', this.collapseIf, this);
43978 this.fireEvent('expand', this);
43981 restrictHeight : function()
43983 this.list.alignTo(this.inputEl(), this.listAlign);
43984 this.list.alignTo(this.inputEl(), this.listAlign);
43987 onViewOver : function(e, t)
43989 if(this.inKeyMode){
43992 var item = this.view.findItemFromChild(t);
43995 var index = this.view.indexOf(item);
43996 this.select(index, false);
44001 onViewClick : function(view, doFocus, el, e)
44003 var index = this.view.getSelectedIndexes()[0];
44005 var r = this.store.getAt(index);
44008 this.onSelect(r, index);
44010 if(doFocus !== false && !this.blockFocus){
44011 this.inputEl().focus();
44015 onViewMove : function(e, t)
44017 this.inKeyMode = false;
44020 select : function(index, scrollIntoView)
44022 this.selectedIndex = index;
44023 this.view.select(index);
44024 if(scrollIntoView !== false){
44025 var el = this.view.getNode(index);
44027 this.list.scrollChildIntoView(el, false);
44032 createList : function()
44034 this.list = Roo.get(document.body).createChild({
44036 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44037 style: 'display:none'
44040 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44043 collapseIf : function(e)
44045 var in_combo = e.within(this.el);
44046 var in_list = e.within(this.list);
44047 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44049 if (in_combo || in_list || is_list) {
44055 onSelect : function(record, index)
44057 if(this.fireEvent('beforeselect', this, record, index) !== false){
44059 this.setFlagClass(record.data.iso2);
44060 this.setDialCode(record.data.dialCode);
44061 this.hasFocus = false;
44063 this.fireEvent('select', this, record, index);
44067 flagEl : function()
44069 var flag = this.el.select('div.flag',true).first();
44076 dialCodeHolderEl : function()
44078 var d = this.el.select('input.dial-code-holder',true).first();
44085 setDialCode : function(v)
44087 this.dialCodeHolder.dom.value = '+'+v;
44090 setFlagClass : function(n)
44092 this.flag.dom.className = 'flag '+n;
44095 getValue : function()
44097 var v = this.inputEl().getValue();
44098 if(this.dialCodeHolder) {
44099 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44104 setValue : function(v)
44106 var d = this.getDialCode(v);
44108 //invalid dial code
44109 if(v.length == 0 || !d || d.length == 0) {
44111 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44112 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44118 this.setFlagClass(this.dialCodeMapping[d].iso2);
44119 this.setDialCode(d);
44120 this.inputEl().dom.value = v.replace('+'+d,'');
44121 this.hiddenEl().dom.value = this.getValue();
44126 getDialCode : function(v)
44130 if (v.length == 0) {
44131 return this.dialCodeHolder.dom.value;
44135 if (v.charAt(0) != "+") {
44138 var numericChars = "";
44139 for (var i = 1; i < v.length; i++) {
44140 var c = v.charAt(i);
44143 if (this.dialCodeMapping[numericChars]) {
44144 dialCode = v.substr(1, i);
44146 if (numericChars.length == 4) {
44156 this.setValue(this.defaultDialCode);
44160 hiddenEl : function()
44162 return this.el.select('input.hidden-tel-input',true).first();
44165 // after setting val
44166 onKeyUp : function(e){
44167 this.setValue(this.getValue());
44170 onKeyPress : function(e){
44171 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44178 * @class Roo.bootstrap.MoneyField
44179 * @extends Roo.bootstrap.ComboBox
44180 * Bootstrap MoneyField class
44183 * Create a new MoneyField.
44184 * @param {Object} config Configuration options
44187 Roo.bootstrap.MoneyField = function(config) {
44189 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44193 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44196 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44198 allowDecimals : true,
44200 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44202 decimalSeparator : ".",
44204 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44206 decimalPrecision : 0,
44208 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44210 allowNegative : true,
44212 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44216 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44218 minValue : Number.NEGATIVE_INFINITY,
44220 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44222 maxValue : Number.MAX_VALUE,
44224 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44226 minText : "The minimum value for this field is {0}",
44228 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44230 maxText : "The maximum value for this field is {0}",
44232 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44233 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44235 nanText : "{0} is not a valid number",
44237 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44241 * @cfg {String} defaults currency of the MoneyField
44242 * value should be in lkey
44244 defaultCurrency : false,
44246 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44248 thousandsDelimiter : false,
44250 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44261 getAutoCreate : function()
44263 var align = this.labelAlign || this.parentLabelAlign();
44275 cls : 'form-control roo-money-amount-input',
44276 autocomplete: 'new-password'
44279 var hiddenInput = {
44283 cls: 'hidden-number-input'
44286 if(this.max_length) {
44287 input.maxlength = this.max_length;
44291 hiddenInput.name = this.name;
44294 if (this.disabled) {
44295 input.disabled = true;
44298 var clg = 12 - this.inputlg;
44299 var cmd = 12 - this.inputmd;
44300 var csm = 12 - this.inputsm;
44301 var cxs = 12 - this.inputxs;
44305 cls : 'row roo-money-field',
44309 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44313 cls: 'roo-select2-container input-group',
44317 cls : 'form-control roo-money-currency-input',
44318 autocomplete: 'new-password',
44320 name : this.currencyName
44324 cls : 'input-group-addon',
44338 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44342 cls: this.hasFeedback ? 'has-feedback' : '',
44353 if (this.fieldLabel.length) {
44356 tooltip: 'This field is required'
44362 cls: 'control-label',
44368 html: this.fieldLabel
44371 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44377 if(this.indicatorpos == 'right') {
44378 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44385 if(align == 'left') {
44393 if(this.labelWidth > 12){
44394 label.style = "width: " + this.labelWidth + 'px';
44396 if(this.labelWidth < 13 && this.labelmd == 0){
44397 this.labelmd = this.labelWidth;
44399 if(this.labellg > 0){
44400 label.cls += ' col-lg-' + this.labellg;
44401 input.cls += ' col-lg-' + (12 - this.labellg);
44403 if(this.labelmd > 0){
44404 label.cls += ' col-md-' + this.labelmd;
44405 container.cls += ' col-md-' + (12 - this.labelmd);
44407 if(this.labelsm > 0){
44408 label.cls += ' col-sm-' + this.labelsm;
44409 container.cls += ' col-sm-' + (12 - this.labelsm);
44411 if(this.labelxs > 0){
44412 label.cls += ' col-xs-' + this.labelxs;
44413 container.cls += ' col-xs-' + (12 - this.labelxs);
44424 var settings = this;
44426 ['xs','sm','md','lg'].map(function(size){
44427 if (settings[size]) {
44428 cfg.cls += ' col-' + size + '-' + settings[size];
44435 initEvents : function()
44437 this.indicator = this.indicatorEl();
44439 this.initCurrencyEvent();
44441 this.initNumberEvent();
44444 initCurrencyEvent : function()
44447 throw "can not find store for combo";
44450 this.store = Roo.factory(this.store, Roo.data);
44451 this.store.parent = this;
44455 this.triggerEl = this.el.select('.input-group-addon', true).first();
44457 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44462 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44463 _this.list.setWidth(lw);
44466 this.list.on('mouseover', this.onViewOver, this);
44467 this.list.on('mousemove', this.onViewMove, this);
44468 this.list.on('scroll', this.onViewScroll, this);
44471 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44474 this.view = new Roo.View(this.list, this.tpl, {
44475 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44478 this.view.on('click', this.onViewClick, this);
44480 this.store.on('beforeload', this.onBeforeLoad, this);
44481 this.store.on('load', this.onLoad, this);
44482 this.store.on('loadexception', this.onLoadException, this);
44484 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44485 "up" : function(e){
44486 this.inKeyMode = true;
44490 "down" : function(e){
44491 if(!this.isExpanded()){
44492 this.onTriggerClick();
44494 this.inKeyMode = true;
44499 "enter" : function(e){
44502 if(this.fireEvent("specialkey", this, e)){
44503 this.onViewClick(false);
44509 "esc" : function(e){
44513 "tab" : function(e){
44516 if(this.fireEvent("specialkey", this, e)){
44517 this.onViewClick(false);
44525 doRelay : function(foo, bar, hname){
44526 if(hname == 'down' || this.scope.isExpanded()){
44527 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44535 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44539 initNumberEvent : function(e)
44541 this.inputEl().on("keydown" , this.fireKey, this);
44542 this.inputEl().on("focus", this.onFocus, this);
44543 this.inputEl().on("blur", this.onBlur, this);
44545 this.inputEl().relayEvent('keyup', this);
44547 if(this.indicator){
44548 this.indicator.addClass('invisible');
44551 this.originalValue = this.getValue();
44553 if(this.validationEvent == 'keyup'){
44554 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44555 this.inputEl().on('keyup', this.filterValidation, this);
44557 else if(this.validationEvent !== false){
44558 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44561 if(this.selectOnFocus){
44562 this.on("focus", this.preFocus, this);
44565 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44566 this.inputEl().on("keypress", this.filterKeys, this);
44568 this.inputEl().relayEvent('keypress', this);
44571 var allowed = "0123456789";
44573 if(this.allowDecimals){
44574 allowed += this.decimalSeparator;
44577 if(this.allowNegative){
44581 if(this.thousandsDelimiter) {
44585 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44587 var keyPress = function(e){
44589 var k = e.getKey();
44591 var c = e.getCharCode();
44594 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44595 allowed.indexOf(String.fromCharCode(c)) === -1
44601 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44605 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44610 this.inputEl().on("keypress", keyPress, this);
44614 onTriggerClick : function(e)
44621 this.loadNext = false;
44623 if(this.isExpanded()){
44628 this.hasFocus = true;
44630 if(this.triggerAction == 'all') {
44631 this.doQuery(this.allQuery, true);
44635 this.doQuery(this.getRawValue());
44638 getCurrency : function()
44640 var v = this.currencyEl().getValue();
44645 restrictHeight : function()
44647 this.list.alignTo(this.currencyEl(), this.listAlign);
44648 this.list.alignTo(this.currencyEl(), this.listAlign);
44651 onViewClick : function(view, doFocus, el, e)
44653 var index = this.view.getSelectedIndexes()[0];
44655 var r = this.store.getAt(index);
44658 this.onSelect(r, index);
44662 onSelect : function(record, index){
44664 if(this.fireEvent('beforeselect', this, record, index) !== false){
44666 this.setFromCurrencyData(index > -1 ? record.data : false);
44670 this.fireEvent('select', this, record, index);
44674 setFromCurrencyData : function(o)
44678 this.lastCurrency = o;
44680 if (this.currencyField) {
44681 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44683 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44686 this.lastSelectionText = currency;
44688 //setting default currency
44689 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44690 this.setCurrency(this.defaultCurrency);
44694 this.setCurrency(currency);
44697 setFromData : function(o)
44701 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44703 this.setFromCurrencyData(c);
44708 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44710 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44713 this.setValue(value);
44717 setCurrency : function(v)
44719 this.currencyValue = v;
44722 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44727 setValue : function(v)
44729 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44735 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44737 this.inputEl().dom.value = (v == '') ? '' :
44738 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44740 if(!this.allowZero && v === '0') {
44741 this.hiddenEl().dom.value = '';
44742 this.inputEl().dom.value = '';
44749 getRawValue : function()
44751 var v = this.inputEl().getValue();
44756 getValue : function()
44758 return this.fixPrecision(this.parseValue(this.getRawValue()));
44761 parseValue : function(value)
44763 if(this.thousandsDelimiter) {
44765 r = new RegExp(",", "g");
44766 value = value.replace(r, "");
44769 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44770 return isNaN(value) ? '' : value;
44774 fixPrecision : function(value)
44776 if(this.thousandsDelimiter) {
44778 r = new RegExp(",", "g");
44779 value = value.replace(r, "");
44782 var nan = isNaN(value);
44784 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44785 return nan ? '' : value;
44787 return parseFloat(value).toFixed(this.decimalPrecision);
44790 decimalPrecisionFcn : function(v)
44792 return Math.floor(v);
44795 validateValue : function(value)
44797 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44801 var num = this.parseValue(value);
44804 this.markInvalid(String.format(this.nanText, value));
44808 if(num < this.minValue){
44809 this.markInvalid(String.format(this.minText, this.minValue));
44813 if(num > this.maxValue){
44814 this.markInvalid(String.format(this.maxText, this.maxValue));
44821 validate : function()
44823 if(this.disabled || this.allowBlank){
44828 var currency = this.getCurrency();
44830 if(this.validateValue(this.getRawValue()) && currency.length){
44835 this.markInvalid();
44839 getName: function()
44844 beforeBlur : function()
44850 var v = this.parseValue(this.getRawValue());
44857 onBlur : function()
44861 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44862 //this.el.removeClass(this.focusClass);
44865 this.hasFocus = false;
44867 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44871 var v = this.getValue();
44873 if(String(v) !== String(this.startValue)){
44874 this.fireEvent('change', this, v, this.startValue);
44877 this.fireEvent("blur", this);
44880 inputEl : function()
44882 return this.el.select('.roo-money-amount-input', true).first();
44885 currencyEl : function()
44887 return this.el.select('.roo-money-currency-input', true).first();
44890 hiddenEl : function()
44892 return this.el.select('input.hidden-number-input',true).first();
44896 * @class Roo.bootstrap.BezierSignature
44897 * @extends Roo.bootstrap.Component
44898 * Bootstrap BezierSignature class
44899 * This script refer to:
44900 * Title: Signature Pad
44902 * Availability: https://github.com/szimek/signature_pad
44905 * Create a new BezierSignature
44906 * @param {Object} config The config object
44909 Roo.bootstrap.BezierSignature = function(config){
44910 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44916 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44923 mouse_btn_down: true,
44926 * @cfg {int} canvas height
44928 canvas_height: '200px',
44931 * @cfg {float|function} Radius of a single dot.
44936 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44941 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44946 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44951 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44956 * @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.
44958 bg_color: 'rgba(0, 0, 0, 0)',
44961 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44963 dot_color: 'black',
44966 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44968 velocity_filter_weight: 0.7,
44971 * @cfg {function} Callback when stroke begin.
44976 * @cfg {function} Callback when stroke end.
44980 getAutoCreate : function()
44982 var cls = 'roo-signature column';
44985 cls += ' ' + this.cls;
44995 for(var i = 0; i < col_sizes.length; i++) {
44996 if(this[col_sizes[i]]) {
44997 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45007 cls: 'roo-signature-body',
45011 cls: 'roo-signature-body-canvas',
45012 height: this.canvas_height,
45013 width: this.canvas_width
45020 style: 'display: none'
45028 initEvents: function()
45030 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45032 var canvas = this.canvasEl();
45034 // mouse && touch event swapping...
45035 canvas.dom.style.touchAction = 'none';
45036 canvas.dom.style.msTouchAction = 'none';
45038 this.mouse_btn_down = false;
45039 canvas.on('mousedown', this._handleMouseDown, this);
45040 canvas.on('mousemove', this._handleMouseMove, this);
45041 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45043 if (window.PointerEvent) {
45044 canvas.on('pointerdown', this._handleMouseDown, this);
45045 canvas.on('pointermove', this._handleMouseMove, this);
45046 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45049 if ('ontouchstart' in window) {
45050 canvas.on('touchstart', this._handleTouchStart, this);
45051 canvas.on('touchmove', this._handleTouchMove, this);
45052 canvas.on('touchend', this._handleTouchEnd, this);
45055 Roo.EventManager.onWindowResize(this.resize, this, true);
45057 // file input event
45058 this.fileEl().on('change', this.uploadImage, this);
45065 resize: function(){
45067 var canvas = this.canvasEl().dom;
45068 var ctx = this.canvasElCtx();
45069 var img_data = false;
45071 if(canvas.width > 0) {
45072 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45074 // setting canvas width will clean img data
45077 var style = window.getComputedStyle ?
45078 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45080 var padding_left = parseInt(style.paddingLeft) || 0;
45081 var padding_right = parseInt(style.paddingRight) || 0;
45083 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45086 ctx.putImageData(img_data, 0, 0);
45090 _handleMouseDown: function(e)
45092 if (e.browserEvent.which === 1) {
45093 this.mouse_btn_down = true;
45094 this.strokeBegin(e);
45098 _handleMouseMove: function (e)
45100 if (this.mouse_btn_down) {
45101 this.strokeMoveUpdate(e);
45105 _handleMouseUp: function (e)
45107 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45108 this.mouse_btn_down = false;
45113 _handleTouchStart: function (e) {
45115 e.preventDefault();
45116 if (e.browserEvent.targetTouches.length === 1) {
45117 // var touch = e.browserEvent.changedTouches[0];
45118 // this.strokeBegin(touch);
45120 this.strokeBegin(e); // assume e catching the correct xy...
45124 _handleTouchMove: function (e) {
45125 e.preventDefault();
45126 // var touch = event.targetTouches[0];
45127 // _this._strokeMoveUpdate(touch);
45128 this.strokeMoveUpdate(e);
45131 _handleTouchEnd: function (e) {
45132 var wasCanvasTouched = e.target === this.canvasEl().dom;
45133 if (wasCanvasTouched) {
45134 e.preventDefault();
45135 // var touch = event.changedTouches[0];
45136 // _this._strokeEnd(touch);
45141 reset: function () {
45142 this._lastPoints = [];
45143 this._lastVelocity = 0;
45144 this._lastWidth = (this.min_width + this.max_width) / 2;
45145 this.canvasElCtx().fillStyle = this.dot_color;
45148 strokeMoveUpdate: function(e)
45150 this.strokeUpdate(e);
45152 if (this.throttle) {
45153 this.throttleStroke(this.strokeUpdate, this.throttle);
45156 this.strokeUpdate(e);
45160 strokeBegin: function(e)
45162 var newPointGroup = {
45163 color: this.dot_color,
45167 if (typeof this.onBegin === 'function') {
45171 this.curve_data.push(newPointGroup);
45173 this.strokeUpdate(e);
45176 strokeUpdate: function(e)
45178 var rect = this.canvasEl().dom.getBoundingClientRect();
45179 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45180 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45181 var lastPoints = lastPointGroup.points;
45182 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45183 var isLastPointTooClose = lastPoint
45184 ? point.distanceTo(lastPoint) <= this.min_distance
45186 var color = lastPointGroup.color;
45187 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45188 var curve = this.addPoint(point);
45190 this.drawDot({color: color, point: point});
45193 this.drawCurve({color: color, curve: curve});
45203 strokeEnd: function(e)
45205 this.strokeUpdate(e);
45206 if (typeof this.onEnd === 'function') {
45211 addPoint: function (point) {
45212 var _lastPoints = this._lastPoints;
45213 _lastPoints.push(point);
45214 if (_lastPoints.length > 2) {
45215 if (_lastPoints.length === 3) {
45216 _lastPoints.unshift(_lastPoints[0]);
45218 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45219 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45220 _lastPoints.shift();
45226 calculateCurveWidths: function (startPoint, endPoint) {
45227 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45228 (1 - this.velocity_filter_weight) * this._lastVelocity;
45230 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45233 start: this._lastWidth
45236 this._lastVelocity = velocity;
45237 this._lastWidth = newWidth;
45241 drawDot: function (_a) {
45242 var color = _a.color, point = _a.point;
45243 var ctx = this.canvasElCtx();
45244 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45246 this.drawCurveSegment(point.x, point.y, width);
45248 ctx.fillStyle = color;
45252 drawCurve: function (_a) {
45253 var color = _a.color, curve = _a.curve;
45254 var ctx = this.canvasElCtx();
45255 var widthDelta = curve.endWidth - curve.startWidth;
45256 var drawSteps = Math.floor(curve.length()) * 2;
45258 ctx.fillStyle = color;
45259 for (var i = 0; i < drawSteps; i += 1) {
45260 var t = i / drawSteps;
45266 var x = uuu * curve.startPoint.x;
45267 x += 3 * uu * t * curve.control1.x;
45268 x += 3 * u * tt * curve.control2.x;
45269 x += ttt * curve.endPoint.x;
45270 var y = uuu * curve.startPoint.y;
45271 y += 3 * uu * t * curve.control1.y;
45272 y += 3 * u * tt * curve.control2.y;
45273 y += ttt * curve.endPoint.y;
45274 var width = curve.startWidth + ttt * widthDelta;
45275 this.drawCurveSegment(x, y, width);
45281 drawCurveSegment: function (x, y, width) {
45282 var ctx = this.canvasElCtx();
45284 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45285 this.is_empty = false;
45290 var ctx = this.canvasElCtx();
45291 var canvas = this.canvasEl().dom;
45292 ctx.fillStyle = this.bg_color;
45293 ctx.clearRect(0, 0, canvas.width, canvas.height);
45294 ctx.fillRect(0, 0, canvas.width, canvas.height);
45295 this.curve_data = [];
45297 this.is_empty = true;
45302 return this.el.select('input',true).first();
45305 canvasEl: function()
45307 return this.el.select('canvas',true).first();
45310 canvasElCtx: function()
45312 return this.el.select('canvas',true).first().dom.getContext('2d');
45315 getImage: function(type)
45317 if(this.is_empty) {
45322 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45325 drawFromImage: function(img_src)
45327 var img = new Image();
45329 img.onload = function(){
45330 this.canvasElCtx().drawImage(img, 0, 0);
45335 this.is_empty = false;
45338 selectImage: function()
45340 this.fileEl().dom.click();
45343 uploadImage: function(e)
45345 var reader = new FileReader();
45347 reader.onload = function(e){
45348 var img = new Image();
45349 img.onload = function(){
45351 this.canvasElCtx().drawImage(img, 0, 0);
45353 img.src = e.target.result;
45356 reader.readAsDataURL(e.target.files[0]);
45359 // Bezier Point Constructor
45360 Point: (function () {
45361 function Point(x, y, time) {
45364 this.time = time || Date.now();
45366 Point.prototype.distanceTo = function (start) {
45367 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45369 Point.prototype.equals = function (other) {
45370 return this.x === other.x && this.y === other.y && this.time === other.time;
45372 Point.prototype.velocityFrom = function (start) {
45373 return this.time !== start.time
45374 ? this.distanceTo(start) / (this.time - start.time)
45381 // Bezier Constructor
45382 Bezier: (function () {
45383 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45384 this.startPoint = startPoint;
45385 this.control2 = control2;
45386 this.control1 = control1;
45387 this.endPoint = endPoint;
45388 this.startWidth = startWidth;
45389 this.endWidth = endWidth;
45391 Bezier.fromPoints = function (points, widths, scope) {
45392 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45393 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45394 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45396 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45397 var dx1 = s1.x - s2.x;
45398 var dy1 = s1.y - s2.y;
45399 var dx2 = s2.x - s3.x;
45400 var dy2 = s2.y - s3.y;
45401 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45402 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45403 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45404 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45405 var dxm = m1.x - m2.x;
45406 var dym = m1.y - m2.y;
45407 var k = l2 / (l1 + l2);
45408 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45409 var tx = s2.x - cm.x;
45410 var ty = s2.y - cm.y;
45412 c1: new scope.Point(m1.x + tx, m1.y + ty),
45413 c2: new scope.Point(m2.x + tx, m2.y + ty)
45416 Bezier.prototype.length = function () {
45421 for (var i = 0; i <= steps; i += 1) {
45423 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45424 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45426 var xdiff = cx - px;
45427 var ydiff = cy - py;
45428 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45435 Bezier.prototype.point = function (t, start, c1, c2, end) {
45436 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45437 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45438 + (3.0 * c2 * (1.0 - t) * t * t)
45439 + (end * t * t * t);
45444 throttleStroke: function(fn, wait) {
45445 if (wait === void 0) { wait = 250; }
45447 var timeout = null;
45451 var later = function () {
45452 previous = Date.now();
45454 result = fn.apply(storedContext, storedArgs);
45456 storedContext = null;
45460 return function wrapper() {
45462 for (var _i = 0; _i < arguments.length; _i++) {
45463 args[_i] = arguments[_i];
45465 var now = Date.now();
45466 var remaining = wait - (now - previous);
45467 storedContext = this;
45469 if (remaining <= 0 || remaining > wait) {
45471 clearTimeout(timeout);
45475 result = fn.apply(storedContext, storedArgs);
45477 storedContext = null;
45481 else if (!timeout) {
45482 timeout = window.setTimeout(later, remaining);