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
7422 * Abstract base class for grid SelectionModels. It provides the interface that should be
7423 * implemented by descendant classes. This class should not be directly instantiated.
7426 Roo.grid.AbstractSelectionModel = function(){
7427 this.locked = false;
7428 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7431 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7432 /** @ignore Called by the grid automatically. Do not call directly. */
7433 init : function(grid){
7439 * Locks the selections.
7446 * Unlocks the selections.
7448 unlock : function(){
7449 this.locked = false;
7453 * Returns true if the selections are locked.
7456 isLocked : function(){
7461 * Ext JS Library 1.1.1
7462 * Copyright(c) 2006-2007, Ext JS, LLC.
7464 * Originally Released Under LGPL - original licence link has changed is not relivant.
7467 * <script type="text/javascript">
7470 * @extends Roo.grid.AbstractSelectionModel
7471 * @class Roo.grid.RowSelectionModel
7472 * The default SelectionModel used by {@link Roo.grid.Grid}.
7473 * It supports multiple selections and keyboard selection/navigation.
7475 * @param {Object} config
7477 Roo.grid.RowSelectionModel = function(config){
7478 Roo.apply(this, config);
7479 this.selections = new Roo.util.MixedCollection(false, function(o){
7484 this.lastActive = false;
7488 * @event selectionchange
7489 * Fires when the selection changes
7490 * @param {SelectionModel} this
7492 "selectionchange" : true,
7494 * @event afterselectionchange
7495 * Fires after the selection changes (eg. by key press or clicking)
7496 * @param {SelectionModel} this
7498 "afterselectionchange" : true,
7500 * @event beforerowselect
7501 * Fires when a row is selected being selected, return false to cancel.
7502 * @param {SelectionModel} this
7503 * @param {Number} rowIndex The selected index
7504 * @param {Boolean} keepExisting False if other selections will be cleared
7506 "beforerowselect" : true,
7509 * Fires when a row is selected.
7510 * @param {SelectionModel} this
7511 * @param {Number} rowIndex The selected index
7512 * @param {Roo.data.Record} r The record
7516 * @event rowdeselect
7517 * Fires when a row is deselected.
7518 * @param {SelectionModel} this
7519 * @param {Number} rowIndex The selected index
7521 "rowdeselect" : true
7523 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7524 this.locked = false;
7527 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7529 * @cfg {Boolean} singleSelect
7530 * True to allow selection of only one row at a time (defaults to false)
7532 singleSelect : false,
7535 initEvents : function(){
7537 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7538 this.grid.on("mousedown", this.handleMouseDown, this);
7539 }else{ // allow click to work like normal
7540 this.grid.on("rowclick", this.handleDragableRowClick, this);
7542 // bootstrap does not have a view..
7543 var view = this.grid.view ? this.grid.view : this.grid;
7544 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7547 this.selectPrevious(e.shiftKey);
7548 }else if(this.last !== false && this.lastActive !== false){
7549 var last = this.last;
7550 this.selectRange(this.last, this.lastActive-1);
7551 view.focusRow(this.lastActive);
7556 this.selectFirstRow();
7558 this.fireEvent("afterselectionchange", this);
7560 "down" : function(e){
7562 this.selectNext(e.shiftKey);
7563 }else if(this.last !== false && this.lastActive !== false){
7564 var last = this.last;
7565 this.selectRange(this.last, this.lastActive+1);
7566 view.focusRow(this.lastActive);
7571 this.selectFirstRow();
7573 this.fireEvent("afterselectionchange", this);
7579 view.on("refresh", this.onRefresh, this);
7580 view.on("rowupdated", this.onRowUpdated, this);
7581 view.on("rowremoved", this.onRemove, this);
7585 onRefresh : function(){
7586 var ds = this.grid.ds, i, v = this.grid.view;
7587 var s = this.selections;
7589 if((i = ds.indexOfId(r.id)) != -1){
7591 s.add(ds.getAt(i)); // updating the selection relate data
7599 onRemove : function(v, index, r){
7600 this.selections.remove(r);
7604 onRowUpdated : function(v, index, r){
7605 if(this.isSelected(r)){
7606 v.onRowSelect(index);
7612 * @param {Array} records The records to select
7613 * @param {Boolean} keepExisting (optional) True to keep existing selections
7615 selectRecords : function(records, keepExisting){
7617 this.clearSelections();
7619 var ds = this.grid.ds;
7620 for(var i = 0, len = records.length; i < len; i++){
7621 this.selectRow(ds.indexOf(records[i]), true);
7626 * Gets the number of selected rows.
7629 getCount : function(){
7630 return this.selections.length;
7634 * Selects the first row in the grid.
7636 selectFirstRow : function(){
7641 * Select the last row.
7642 * @param {Boolean} keepExisting (optional) True to keep existing selections
7644 selectLastRow : function(keepExisting){
7645 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7649 * Selects the row immediately following the last selected row.
7650 * @param {Boolean} keepExisting (optional) True to keep existing selections
7652 selectNext : function(keepExisting){
7653 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7654 this.selectRow(this.last+1, keepExisting);
7655 var view = this.grid.view ? this.grid.view : this.grid;
7656 view.focusRow(this.last);
7661 * Selects the row that precedes the last selected row.
7662 * @param {Boolean} keepExisting (optional) True to keep existing selections
7664 selectPrevious : function(keepExisting){
7666 this.selectRow(this.last-1, keepExisting);
7667 var view = this.grid.view ? this.grid.view : this.grid;
7668 view.focusRow(this.last);
7673 * Returns the selected records
7674 * @return {Array} Array of selected records
7676 getSelections : function(){
7677 return [].concat(this.selections.items);
7681 * Returns the first selected record.
7684 getSelected : function(){
7685 return this.selections.itemAt(0);
7690 * Clears all selections.
7692 clearSelections : function(fast){
7697 var ds = this.grid.ds;
7698 var s = this.selections;
7700 this.deselectRow(ds.indexOfId(r.id));
7704 this.selections.clear();
7713 selectAll : function(){
7717 this.selections.clear();
7718 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7719 this.selectRow(i, true);
7724 * Returns True if there is a selection.
7727 hasSelection : function(){
7728 return this.selections.length > 0;
7732 * Returns True if the specified row is selected.
7733 * @param {Number/Record} record The record or index of the record to check
7736 isSelected : function(index){
7737 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7738 return (r && this.selections.key(r.id) ? true : false);
7742 * Returns True if the specified record id is selected.
7743 * @param {String} id The id of record to check
7746 isIdSelected : function(id){
7747 return (this.selections.key(id) ? true : false);
7751 handleMouseDown : function(e, t)
7753 var view = this.grid.view ? this.grid.view : this.grid;
7755 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7758 if(e.shiftKey && this.last !== false){
7759 var last = this.last;
7760 this.selectRange(last, rowIndex, e.ctrlKey);
7761 this.last = last; // reset the last
7762 view.focusRow(rowIndex);
7764 var isSelected = this.isSelected(rowIndex);
7765 if(e.button !== 0 && isSelected){
7766 view.focusRow(rowIndex);
7767 }else if(e.ctrlKey && isSelected){
7768 this.deselectRow(rowIndex);
7769 }else if(!isSelected){
7770 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7771 view.focusRow(rowIndex);
7774 this.fireEvent("afterselectionchange", this);
7777 handleDragableRowClick : function(grid, rowIndex, e)
7779 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7780 this.selectRow(rowIndex, false);
7781 var view = this.grid.view ? this.grid.view : this.grid;
7782 view.focusRow(rowIndex);
7783 this.fireEvent("afterselectionchange", this);
7788 * Selects multiple rows.
7789 * @param {Array} rows Array of the indexes of the row to select
7790 * @param {Boolean} keepExisting (optional) True to keep existing selections
7792 selectRows : function(rows, keepExisting){
7794 this.clearSelections();
7796 for(var i = 0, len = rows.length; i < len; i++){
7797 this.selectRow(rows[i], true);
7802 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7803 * @param {Number} startRow The index of the first row in the range
7804 * @param {Number} endRow The index of the last row in the range
7805 * @param {Boolean} keepExisting (optional) True to retain existing selections
7807 selectRange : function(startRow, endRow, keepExisting){
7812 this.clearSelections();
7814 if(startRow <= endRow){
7815 for(var i = startRow; i <= endRow; i++){
7816 this.selectRow(i, true);
7819 for(var i = startRow; i >= endRow; i--){
7820 this.selectRow(i, true);
7826 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7827 * @param {Number} startRow The index of the first row in the range
7828 * @param {Number} endRow The index of the last row in the range
7830 deselectRange : function(startRow, endRow, preventViewNotify){
7834 for(var i = startRow; i <= endRow; i++){
7835 this.deselectRow(i, preventViewNotify);
7841 * @param {Number} row The index of the row to select
7842 * @param {Boolean} keepExisting (optional) True to keep existing selections
7844 selectRow : function(index, keepExisting, preventViewNotify){
7845 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7848 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7849 if(!keepExisting || this.singleSelect){
7850 this.clearSelections();
7852 var r = this.grid.ds.getAt(index);
7853 this.selections.add(r);
7854 this.last = this.lastActive = index;
7855 if(!preventViewNotify){
7856 var view = this.grid.view ? this.grid.view : this.grid;
7857 view.onRowSelect(index);
7859 this.fireEvent("rowselect", this, index, r);
7860 this.fireEvent("selectionchange", this);
7866 * @param {Number} row The index of the row to deselect
7868 deselectRow : function(index, preventViewNotify){
7872 if(this.last == index){
7875 if(this.lastActive == index){
7876 this.lastActive = false;
7878 var r = this.grid.ds.getAt(index);
7879 this.selections.remove(r);
7880 if(!preventViewNotify){
7881 var view = this.grid.view ? this.grid.view : this.grid;
7882 view.onRowDeselect(index);
7884 this.fireEvent("rowdeselect", this, index);
7885 this.fireEvent("selectionchange", this);
7889 restoreLast : function(){
7891 this.last = this._last;
7896 acceptsNav : function(row, col, cm){
7897 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7901 onEditorKey : function(field, e){
7902 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7907 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7909 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7911 }else if(k == e.ENTER && !e.ctrlKey){
7915 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7917 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7919 }else if(k == e.ESC){
7923 g.startEditing(newCell[0], newCell[1]);
7928 * Ext JS Library 1.1.1
7929 * Copyright(c) 2006-2007, Ext JS, LLC.
7931 * Originally Released Under LGPL - original licence link has changed is not relivant.
7934 * <script type="text/javascript">
7939 * @class Roo.grid.ColumnModel
7940 * @extends Roo.util.Observable
7941 * This is the default implementation of a ColumnModel used by the Grid. It defines
7942 * the columns in the grid.
7945 var colModel = new Roo.grid.ColumnModel([
7946 {header: "Ticker", width: 60, sortable: true, locked: true},
7947 {header: "Company Name", width: 150, sortable: true},
7948 {header: "Market Cap.", width: 100, sortable: true},
7949 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7950 {header: "Employees", width: 100, sortable: true, resizable: false}
7955 * The config options listed for this class are options which may appear in each
7956 * individual column definition.
7957 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7959 * @param {Object} config An Array of column config objects. See this class's
7960 * config objects for details.
7962 Roo.grid.ColumnModel = function(config){
7964 * The config passed into the constructor
7966 this.config = []; //config;
7969 // if no id, create one
7970 // if the column does not have a dataIndex mapping,
7971 // map it to the order it is in the config
7972 for(var i = 0, len = config.length; i < len; i++){
7973 this.addColumn(config[i]);
7978 * The width of columns which have no width specified (defaults to 100)
7981 this.defaultWidth = 100;
7984 * Default sortable of columns which have no sortable specified (defaults to false)
7987 this.defaultSortable = false;
7991 * @event widthchange
7992 * Fires when the width of a column changes.
7993 * @param {ColumnModel} this
7994 * @param {Number} columnIndex The column index
7995 * @param {Number} newWidth The new width
7997 "widthchange": true,
7999 * @event headerchange
8000 * Fires when the text of a header changes.
8001 * @param {ColumnModel} this
8002 * @param {Number} columnIndex The column index
8003 * @param {Number} newText The new header text
8005 "headerchange": true,
8007 * @event hiddenchange
8008 * Fires when a column is hidden or "unhidden".
8009 * @param {ColumnModel} this
8010 * @param {Number} columnIndex The column index
8011 * @param {Boolean} hidden true if hidden, false otherwise
8013 "hiddenchange": true,
8015 * @event columnmoved
8016 * Fires when a column is moved.
8017 * @param {ColumnModel} this
8018 * @param {Number} oldIndex
8019 * @param {Number} newIndex
8021 "columnmoved" : true,
8023 * @event columlockchange
8024 * Fires when a column's locked state is changed
8025 * @param {ColumnModel} this
8026 * @param {Number} colIndex
8027 * @param {Boolean} locked true if locked
8029 "columnlockchange" : true
8031 Roo.grid.ColumnModel.superclass.constructor.call(this);
8033 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8035 * @cfg {String} header The header text to display in the Grid view.
8038 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8041 * @cfg {String} smHeader Header at Bootsrap Small width
8044 * @cfg {String} mdHeader Header at Bootsrap Medium width
8047 * @cfg {String} lgHeader Header at Bootsrap Large width
8050 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8053 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8054 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8055 * specified, the column's index is used as an index into the Record's data Array.
8058 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8059 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8062 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8063 * Defaults to the value of the {@link #defaultSortable} property.
8064 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8067 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8070 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8073 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8076 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8079 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8080 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8081 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8082 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8085 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8088 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8091 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8094 * @cfg {String} cursor (Optional)
8097 * @cfg {String} tooltip (Optional)
8100 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8103 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8106 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8109 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8112 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8115 * Returns the id of the column at the specified index.
8116 * @param {Number} index The column index
8117 * @return {String} the id
8119 getColumnId : function(index){
8120 return this.config[index].id;
8124 * Returns the column for a specified id.
8125 * @param {String} id The column id
8126 * @return {Object} the column
8128 getColumnById : function(id){
8129 return this.lookup[id];
8134 * Returns the column Object for a specified dataIndex.
8135 * @param {String} dataIndex The column dataIndex
8136 * @return {Object|Boolean} the column or false if not found
8138 getColumnByDataIndex: function(dataIndex){
8139 var index = this.findColumnIndex(dataIndex);
8140 return index > -1 ? this.config[index] : false;
8144 * Returns the index for a specified column id.
8145 * @param {String} id The column id
8146 * @return {Number} the index, or -1 if not found
8148 getIndexById : function(id){
8149 for(var i = 0, len = this.config.length; i < len; i++){
8150 if(this.config[i].id == id){
8158 * Returns the index for a specified column dataIndex.
8159 * @param {String} dataIndex The column dataIndex
8160 * @return {Number} the index, or -1 if not found
8163 findColumnIndex : function(dataIndex){
8164 for(var i = 0, len = this.config.length; i < len; i++){
8165 if(this.config[i].dataIndex == dataIndex){
8173 moveColumn : function(oldIndex, newIndex){
8174 var c = this.config[oldIndex];
8175 this.config.splice(oldIndex, 1);
8176 this.config.splice(newIndex, 0, c);
8177 this.dataMap = null;
8178 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8181 isLocked : function(colIndex){
8182 return this.config[colIndex].locked === true;
8185 setLocked : function(colIndex, value, suppressEvent){
8186 if(this.isLocked(colIndex) == value){
8189 this.config[colIndex].locked = value;
8191 this.fireEvent("columnlockchange", this, colIndex, value);
8195 getTotalLockedWidth : function(){
8197 for(var i = 0; i < this.config.length; i++){
8198 if(this.isLocked(i) && !this.isHidden(i)){
8199 this.totalWidth += this.getColumnWidth(i);
8205 getLockedCount : function(){
8206 for(var i = 0, len = this.config.length; i < len; i++){
8207 if(!this.isLocked(i)){
8212 return this.config.length;
8216 * Returns the number of columns.
8219 getColumnCount : function(visibleOnly){
8220 if(visibleOnly === true){
8222 for(var i = 0, len = this.config.length; i < len; i++){
8223 if(!this.isHidden(i)){
8229 return this.config.length;
8233 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8234 * @param {Function} fn
8235 * @param {Object} scope (optional)
8236 * @return {Array} result
8238 getColumnsBy : function(fn, scope){
8240 for(var i = 0, len = this.config.length; i < len; i++){
8241 var c = this.config[i];
8242 if(fn.call(scope||this, c, i) === true){
8250 * Returns true if the specified column is sortable.
8251 * @param {Number} col The column index
8254 isSortable : function(col){
8255 if(typeof this.config[col].sortable == "undefined"){
8256 return this.defaultSortable;
8258 return this.config[col].sortable;
8262 * Returns the rendering (formatting) function defined for the column.
8263 * @param {Number} col The column index.
8264 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8266 getRenderer : function(col){
8267 if(!this.config[col].renderer){
8268 return Roo.grid.ColumnModel.defaultRenderer;
8270 return this.config[col].renderer;
8274 * Sets the rendering (formatting) function for a column.
8275 * @param {Number} col The column index
8276 * @param {Function} fn The function to use to process the cell's raw data
8277 * to return HTML markup for the grid view. The render function is called with
8278 * the following parameters:<ul>
8279 * <li>Data value.</li>
8280 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8281 * <li>css A CSS style string to apply to the table cell.</li>
8282 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8283 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8284 * <li>Row index</li>
8285 * <li>Column index</li>
8286 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8288 setRenderer : function(col, fn){
8289 this.config[col].renderer = fn;
8293 * Returns the width for the specified column.
8294 * @param {Number} col The column index
8295 * @param (optional) {String} gridSize bootstrap width size.
8298 getColumnWidth : function(col, gridSize)
8300 var cfg = this.config[col];
8302 if (typeof(gridSize) == 'undefined') {
8303 return cfg.width * 1 || this.defaultWidth;
8305 if (gridSize === false) { // if we set it..
8306 return cfg.width || false;
8308 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8310 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8311 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8314 return cfg[ sizes[i] ];
8321 * Sets the width for a column.
8322 * @param {Number} col The column index
8323 * @param {Number} width The new width
8325 setColumnWidth : function(col, width, suppressEvent){
8326 this.config[col].width = width;
8327 this.totalWidth = null;
8329 this.fireEvent("widthchange", this, col, width);
8334 * Returns the total width of all columns.
8335 * @param {Boolean} includeHidden True to include hidden column widths
8338 getTotalWidth : function(includeHidden){
8339 if(!this.totalWidth){
8340 this.totalWidth = 0;
8341 for(var i = 0, len = this.config.length; i < len; i++){
8342 if(includeHidden || !this.isHidden(i)){
8343 this.totalWidth += this.getColumnWidth(i);
8347 return this.totalWidth;
8351 * Returns the header for the specified column.
8352 * @param {Number} col The column index
8355 getColumnHeader : function(col){
8356 return this.config[col].header;
8360 * Sets the header for a column.
8361 * @param {Number} col The column index
8362 * @param {String} header The new header
8364 setColumnHeader : function(col, header){
8365 this.config[col].header = header;
8366 this.fireEvent("headerchange", this, col, header);
8370 * Returns the tooltip for the specified column.
8371 * @param {Number} col The column index
8374 getColumnTooltip : function(col){
8375 return this.config[col].tooltip;
8378 * Sets the tooltip for a column.
8379 * @param {Number} col The column index
8380 * @param {String} tooltip The new tooltip
8382 setColumnTooltip : function(col, tooltip){
8383 this.config[col].tooltip = tooltip;
8387 * Returns the dataIndex for the specified column.
8388 * @param {Number} col The column index
8391 getDataIndex : function(col){
8392 return this.config[col].dataIndex;
8396 * Sets the dataIndex for a column.
8397 * @param {Number} col The column index
8398 * @param {Number} dataIndex The new dataIndex
8400 setDataIndex : function(col, dataIndex){
8401 this.config[col].dataIndex = dataIndex;
8407 * Returns true if the cell is editable.
8408 * @param {Number} colIndex The column index
8409 * @param {Number} rowIndex The row index - this is nto actually used..?
8412 isCellEditable : function(colIndex, rowIndex){
8413 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8417 * Returns the editor defined for the cell/column.
8418 * return false or null to disable editing.
8419 * @param {Number} colIndex The column index
8420 * @param {Number} rowIndex The row index
8423 getCellEditor : function(colIndex, rowIndex){
8424 return this.config[colIndex].editor;
8428 * Sets if a column is editable.
8429 * @param {Number} col The column index
8430 * @param {Boolean} editable True if the column is editable
8432 setEditable : function(col, editable){
8433 this.config[col].editable = editable;
8438 * Returns true if the column is hidden.
8439 * @param {Number} colIndex The column index
8442 isHidden : function(colIndex){
8443 return this.config[colIndex].hidden;
8448 * Returns true if the column width cannot be changed
8450 isFixed : function(colIndex){
8451 return this.config[colIndex].fixed;
8455 * Returns true if the column can be resized
8458 isResizable : function(colIndex){
8459 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8462 * Sets if a column is hidden.
8463 * @param {Number} colIndex The column index
8464 * @param {Boolean} hidden True if the column is hidden
8466 setHidden : function(colIndex, hidden){
8467 this.config[colIndex].hidden = hidden;
8468 this.totalWidth = null;
8469 this.fireEvent("hiddenchange", this, colIndex, hidden);
8473 * Sets the editor for a column.
8474 * @param {Number} col The column index
8475 * @param {Object} editor The editor object
8477 setEditor : function(col, editor){
8478 this.config[col].editor = editor;
8481 * Add a column (experimental...) - defaults to adding to the end..
8482 * @param {Object} config
8484 addColumn : function(c)
8487 var i = this.config.length;
8490 if(typeof c.dataIndex == "undefined"){
8493 if(typeof c.renderer == "string"){
8494 c.renderer = Roo.util.Format[c.renderer];
8496 if(typeof c.id == "undefined"){
8499 if(c.editor && c.editor.xtype){
8500 c.editor = Roo.factory(c.editor, Roo.grid);
8502 if(c.editor && c.editor.isFormField){
8503 c.editor = new Roo.grid.GridEditor(c.editor);
8505 this.lookup[c.id] = c;
8510 Roo.grid.ColumnModel.defaultRenderer = function(value)
8512 if(typeof value == "object") {
8515 if(typeof value == "string" && value.length < 1){
8519 return String.format("{0}", value);
8522 // Alias for backwards compatibility
8523 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8526 * Ext JS Library 1.1.1
8527 * Copyright(c) 2006-2007, Ext JS, LLC.
8529 * Originally Released Under LGPL - original licence link has changed is not relivant.
8532 * <script type="text/javascript">
8536 * @class Roo.LoadMask
8537 * A simple utility class for generically masking elements while loading data. If the element being masked has
8538 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8539 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8540 * element's UpdateManager load indicator and will be destroyed after the initial load.
8542 * Create a new LoadMask
8543 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8544 * @param {Object} config The config object
8546 Roo.LoadMask = function(el, config){
8547 this.el = Roo.get(el);
8548 Roo.apply(this, config);
8550 this.store.on('beforeload', this.onBeforeLoad, this);
8551 this.store.on('load', this.onLoad, this);
8552 this.store.on('loadexception', this.onLoadException, this);
8553 this.removeMask = false;
8555 var um = this.el.getUpdateManager();
8556 um.showLoadIndicator = false; // disable the default indicator
8557 um.on('beforeupdate', this.onBeforeLoad, this);
8558 um.on('update', this.onLoad, this);
8559 um.on('failure', this.onLoad, this);
8560 this.removeMask = true;
8564 Roo.LoadMask.prototype = {
8566 * @cfg {Boolean} removeMask
8567 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8568 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8573 * The text to display in a centered loading message box (defaults to 'Loading...')
8577 * @cfg {String} msgCls
8578 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8580 msgCls : 'x-mask-loading',
8583 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8589 * Disables the mask to prevent it from being displayed
8591 disable : function(){
8592 this.disabled = true;
8596 * Enables the mask so that it can be displayed
8598 enable : function(){
8599 this.disabled = false;
8602 onLoadException : function()
8606 if (typeof(arguments[3]) != 'undefined') {
8607 Roo.MessageBox.alert("Error loading",arguments[3]);
8611 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8612 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8619 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8624 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8628 onBeforeLoad : function(){
8630 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8635 destroy : function(){
8637 this.store.un('beforeload', this.onBeforeLoad, this);
8638 this.store.un('load', this.onLoad, this);
8639 this.store.un('loadexception', this.onLoadException, this);
8641 var um = this.el.getUpdateManager();
8642 um.un('beforeupdate', this.onBeforeLoad, this);
8643 um.un('update', this.onLoad, this);
8644 um.un('failure', this.onLoad, this);
8648 * @class Roo.bootstrap.Table
8650 * @extends Roo.bootstrap.Component
8651 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8652 * Similar to Roo.grid.Grid
8654 var table = Roo.factory({
8656 xns : Roo.bootstrap,
8657 autoSizeColumns: true,
8664 sortInfo : { direction : 'ASC', field: 'name' },
8666 xtype : 'HttpProxy',
8669 url : 'https://example.com/some.data.url.json'
8672 xtype : 'JsonReader',
8674 fields : [ 'id', 'name', whatever' ],
8681 xtype : 'ColumnModel',
8685 dataIndex : 'is_in_group',
8688 renderer : function(v, x , r) {
8690 return String.format("{0}", v)
8696 xtype : 'RowSelectionModel',
8697 xns : Roo.bootstrap.Table
8698 // you can add listeners to catch selection change here....
8704 grid.render(Roo.get("some-div"));
8707 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8712 * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8713 * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8714 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8716 * @cfg {String} cls table class
8719 * @cfg {boolean} striped Should the rows be alternative striped
8720 * @cfg {boolean} bordered Add borders to the table
8721 * @cfg {boolean} hover Add hover highlighting
8722 * @cfg {boolean} condensed Format condensed
8723 * @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,
8724 * also adds table-responsive (see bootstrap docs for details)
8725 * @cfg {Boolean} loadMask (true|false) default false
8726 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8727 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8728 * @cfg {Boolean} rowSelection (true|false) default false
8729 * @cfg {Boolean} cellSelection (true|false) default false
8730 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8731 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8732 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8733 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8734 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8735 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8738 * Create a new Table
8739 * @param {Object} config The config object
8742 Roo.bootstrap.Table = function(config)
8744 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8747 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8748 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8749 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8750 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8752 this.view = this; // compat with grid.
8754 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8756 this.sm.grid = this;
8757 this.selModel = Roo.factory(this.sm, Roo.grid);
8758 this.sm = this.selModel;
8759 this.sm.xmodule = this.xmodule || false;
8762 if (this.cm && typeof(this.cm.config) == 'undefined') {
8763 this.colModel = new Roo.grid.ColumnModel(this.cm);
8764 this.cm = this.colModel;
8765 this.cm.xmodule = this.xmodule || false;
8768 this.store= Roo.factory(this.store, Roo.data);
8769 this.ds = this.store;
8770 this.ds.xmodule = this.xmodule || false;
8773 if (this.footer && this.store) {
8774 this.footer.dataSource = this.ds;
8775 this.footer = Roo.factory(this.footer);
8782 * Fires when a cell is clicked
8783 * @param {Roo.bootstrap.Table} this
8784 * @param {Roo.Element} el
8785 * @param {Number} rowIndex
8786 * @param {Number} columnIndex
8787 * @param {Roo.EventObject} e
8791 * @event celldblclick
8792 * Fires when a cell is double clicked
8793 * @param {Roo.bootstrap.Table} this
8794 * @param {Roo.Element} el
8795 * @param {Number} rowIndex
8796 * @param {Number} columnIndex
8797 * @param {Roo.EventObject} e
8799 "celldblclick" : true,
8802 * Fires when a row is clicked
8803 * @param {Roo.bootstrap.Table} this
8804 * @param {Roo.Element} el
8805 * @param {Number} rowIndex
8806 * @param {Roo.EventObject} e
8810 * @event rowdblclick
8811 * Fires when a row is double clicked
8812 * @param {Roo.bootstrap.Table} this
8813 * @param {Roo.Element} el
8814 * @param {Number} rowIndex
8815 * @param {Roo.EventObject} e
8817 "rowdblclick" : true,
8820 * Fires when a mouseover occur
8821 * @param {Roo.bootstrap.Table} this
8822 * @param {Roo.Element} el
8823 * @param {Number} rowIndex
8824 * @param {Number} columnIndex
8825 * @param {Roo.EventObject} e
8830 * Fires when a mouseout occur
8831 * @param {Roo.bootstrap.Table} this
8832 * @param {Roo.Element} el
8833 * @param {Number} rowIndex
8834 * @param {Number} columnIndex
8835 * @param {Roo.EventObject} e
8840 * Fires when a row is rendered, so you can change add a style to it.
8841 * @param {Roo.bootstrap.Table} this
8842 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8846 * @event rowsrendered
8847 * Fires when all the rows have been rendered
8848 * @param {Roo.bootstrap.Table} this
8850 'rowsrendered' : true,
8852 * @event contextmenu
8853 * The raw contextmenu event for the entire grid.
8854 * @param {Roo.EventObject} e
8856 "contextmenu" : true,
8858 * @event rowcontextmenu
8859 * Fires when a row is right clicked
8860 * @param {Roo.bootstrap.Table} this
8861 * @param {Number} rowIndex
8862 * @param {Roo.EventObject} e
8864 "rowcontextmenu" : true,
8866 * @event cellcontextmenu
8867 * Fires when a cell is right clicked
8868 * @param {Roo.bootstrap.Table} this
8869 * @param {Number} rowIndex
8870 * @param {Number} cellIndex
8871 * @param {Roo.EventObject} e
8873 "cellcontextmenu" : true,
8875 * @event headercontextmenu
8876 * Fires when a header is right clicked
8877 * @param {Roo.bootstrap.Table} this
8878 * @param {Number} columnIndex
8879 * @param {Roo.EventObject} e
8881 "headercontextmenu" : true,
8884 * The raw mousedown event for the entire grid.
8885 * @param {Roo.EventObject} e
8892 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8908 enableColumnResize: true,
8910 rowSelection : false,
8911 cellSelection : false,
8914 minColumnWidth : 50,
8916 // Roo.Element - the tbody
8917 bodyEl: false, // <tbody> Roo.Element - thead element
8918 headEl: false, // <thead> Roo.Element - thead element
8919 resizeProxy : false, // proxy element for dragging?
8923 container: false, // used by gridpanel...
8929 auto_hide_footer : false,
8931 view: false, // actually points to this..
8933 getAutoCreate : function()
8935 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8942 // this get's auto added by panel.Grid
8943 if (this.scrollBody) {
8944 cfg.cls += ' table-body-fixed';
8947 cfg.cls += ' table-striped';
8951 cfg.cls += ' table-hover';
8953 if (this.bordered) {
8954 cfg.cls += ' table-bordered';
8956 if (this.condensed) {
8957 cfg.cls += ' table-condensed';
8960 if (this.responsive) {
8961 cfg.cls += ' table-responsive';
8965 cfg.cls+= ' ' +this.cls;
8971 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8974 if(this.store || this.cm){
8975 if(this.headerShow){
8976 cfg.cn.push(this.renderHeader());
8979 cfg.cn.push(this.renderBody());
8981 if(this.footerShow){
8982 cfg.cn.push(this.renderFooter());
8984 // where does this come from?
8985 //cfg.cls+= ' TableGrid';
8988 return { cn : [ cfg ] };
8991 initEvents : function()
8993 if(!this.store || !this.cm){
8996 if (this.selModel) {
8997 this.selModel.initEvents();
9001 //Roo.log('initEvents with ds!!!!');
9003 this.bodyEl = this.el.select('tbody', true).first();
9004 this.headEl = this.el.select('thead', true).first();
9005 this.mainFoot = this.el.select('tfoot', true).first();
9010 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9011 e.on('click', this.sort, this);
9015 // why is this done????? = it breaks dialogs??
9016 //this.parent().el.setStyle('position', 'relative');
9020 this.footer.parentId = this.id;
9021 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9024 this.el.select('tfoot tr td').first().addClass('hide');
9029 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9032 this.store.on('load', this.onLoad, this);
9033 this.store.on('beforeload', this.onBeforeLoad, this);
9034 this.store.on('update', this.onUpdate, this);
9035 this.store.on('add', this.onAdd, this);
9036 this.store.on("clear", this.clear, this);
9038 this.el.on("contextmenu", this.onContextMenu, this);
9041 this.cm.on("headerchange", this.onHeaderChange, this);
9042 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9044 //?? does bodyEl get replaced on render?
9045 this.bodyEl.on("click", this.onClick, this);
9046 this.bodyEl.on("dblclick", this.onDblClick, this);
9047 this.bodyEl.on('scroll', this.onBodyScroll, this);
9049 // guessing mainbody will work - this relays usually caught by selmodel at present.
9050 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9053 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9056 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9057 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9062 // Compatibility with grid - we implement all the view features at present.
9063 getView : function()
9068 initCSS : function()
9072 var cm = this.cm, styles = [];
9073 this.CSS.removeStyleSheet(this.id + '-cssrules');
9074 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9075 // we can honour xs/sm/md/xl as widths...
9076 // we first have to decide what widht we are currently at...
9077 var sz = Roo.getGridSize();
9081 var cols = []; // visable cols.
9083 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9084 var w = cm.getColumnWidth(i, false);
9086 cols.push( { rel : false, abs : 0 });
9090 cols.push( { rel : false, abs : w });
9092 last = i; // not really..
9095 var w = cm.getColumnWidth(i, sz);
9100 cols.push( { rel : w, abs : false });
9103 var avail = this.bodyEl.dom.clientWidth - total_abs;
9105 var unitWidth = Math.floor(avail / total);
9106 var rem = avail - (unitWidth * total);
9108 var hidden, width, pos = 0 , splithide , left;
9109 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9111 hidden = 'display:none;';
9113 width = 'width:0px;';
9115 if(!cm.isHidden(i)){
9119 // we can honour xs/sm/md/xl ?
9120 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9122 hidden = 'display:none;';
9124 // width should return a small number...
9126 w+=rem; // add the remaining with..
9129 left = "left:" + (pos -4) + "px;";
9130 width = "width:" + w+ "px;";
9133 if (this.responsive) {
9136 hidden = cm.isHidden(i) ? 'display:none;' : '';
9137 splithide = 'display: none;';
9140 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9143 splithide = 'display:none;';
9146 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9147 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9152 //Roo.log(styles.join(''));
9153 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9159 onContextMenu : function(e, t)
9161 this.processEvent("contextmenu", e);
9164 processEvent : function(name, e)
9166 if (name != 'touchstart' ) {
9167 this.fireEvent(name, e);
9170 var t = e.getTarget();
9172 var cell = Roo.get(t);
9178 if(cell.findParent('tfoot', false, true)){
9182 if(cell.findParent('thead', false, true)){
9184 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9185 cell = Roo.get(t).findParent('th', false, true);
9187 Roo.log("failed to find th in thead?");
9188 Roo.log(e.getTarget());
9193 var cellIndex = cell.dom.cellIndex;
9195 var ename = name == 'touchstart' ? 'click' : name;
9196 this.fireEvent("header" + ename, this, cellIndex, e);
9201 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9202 cell = Roo.get(t).findParent('td', false, true);
9204 Roo.log("failed to find th in tbody?");
9205 Roo.log(e.getTarget());
9210 var row = cell.findParent('tr', false, true);
9211 var cellIndex = cell.dom.cellIndex;
9212 var rowIndex = row.dom.rowIndex - 1;
9216 this.fireEvent("row" + name, this, rowIndex, e);
9220 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9226 onMouseover : function(e, el)
9228 var cell = Roo.get(el);
9234 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9235 cell = cell.findParent('td', false, true);
9238 var row = cell.findParent('tr', false, true);
9239 var cellIndex = cell.dom.cellIndex;
9240 var rowIndex = row.dom.rowIndex - 1; // start from 0
9242 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9246 onMouseout : function(e, el)
9248 var cell = Roo.get(el);
9254 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9255 cell = cell.findParent('td', false, true);
9258 var row = cell.findParent('tr', false, true);
9259 var cellIndex = cell.dom.cellIndex;
9260 var rowIndex = row.dom.rowIndex - 1; // start from 0
9262 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9266 onClick : function(e, el)
9268 var cell = Roo.get(el);
9270 if(!cell || (!this.cellSelection && !this.rowSelection)){
9274 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9275 cell = cell.findParent('td', false, true);
9278 if(!cell || typeof(cell) == 'undefined'){
9282 var row = cell.findParent('tr', false, true);
9284 if(!row || typeof(row) == 'undefined'){
9288 var cellIndex = cell.dom.cellIndex;
9289 var rowIndex = this.getRowIndex(row);
9291 // why??? - should these not be based on SelectionModel?
9292 //if(this.cellSelection){
9293 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9296 //if(this.rowSelection){
9297 this.fireEvent('rowclick', this, row, rowIndex, e);
9302 onDblClick : function(e,el)
9304 var cell = Roo.get(el);
9306 if(!cell || (!this.cellSelection && !this.rowSelection)){
9310 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9311 cell = cell.findParent('td', false, true);
9314 if(!cell || typeof(cell) == 'undefined'){
9318 var row = cell.findParent('tr', false, true);
9320 if(!row || typeof(row) == 'undefined'){
9324 var cellIndex = cell.dom.cellIndex;
9325 var rowIndex = this.getRowIndex(row);
9327 if(this.cellSelection){
9328 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9331 if(this.rowSelection){
9332 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9335 findRowIndex : function(el)
9337 var cell = Roo.get(el);
9341 var row = cell.findParent('tr', false, true);
9343 if(!row || typeof(row) == 'undefined'){
9346 return this.getRowIndex(row);
9348 sort : function(e,el)
9350 var col = Roo.get(el);
9352 if(!col.hasClass('sortable')){
9356 var sort = col.attr('sort');
9359 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9363 this.store.sortInfo = {field : sort, direction : dir};
9366 Roo.log("calling footer first");
9367 this.footer.onClick('first');
9370 this.store.load({ params : { start : 0 } });
9374 renderHeader : function()
9382 this.totalWidth = 0;
9384 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9386 var config = cm.config[i];
9390 cls : 'x-hcol-' + i,
9393 html: cm.getColumnHeader(i)
9396 var tooltip = cm.getColumnTooltip(i);
9398 c.tooltip = tooltip;
9404 if(typeof(config.sortable) != 'undefined' && config.sortable){
9405 c.cls += ' sortable';
9406 c.html = '<i class="fa"></i>' + c.html;
9409 // could use BS4 hidden-..-down
9411 if(typeof(config.lgHeader) != 'undefined'){
9412 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9415 if(typeof(config.mdHeader) != 'undefined'){
9416 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9419 if(typeof(config.smHeader) != 'undefined'){
9420 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9423 if(typeof(config.xsHeader) != 'undefined'){
9424 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9431 if(typeof(config.tooltip) != 'undefined'){
9432 c.tooltip = config.tooltip;
9435 if(typeof(config.colspan) != 'undefined'){
9436 c.colspan = config.colspan;
9439 // hidden is handled by CSS now
9441 if(typeof(config.dataIndex) != 'undefined'){
9442 c.sort = config.dataIndex;
9447 if(typeof(config.align) != 'undefined' && config.align.length){
9448 c.style += ' text-align:' + config.align + ';';
9451 /* width is done in CSS
9452 *if(typeof(config.width) != 'undefined'){
9453 c.style += ' width:' + config.width + 'px;';
9454 this.totalWidth += config.width;
9456 this.totalWidth += 100; // assume minimum of 100 per column?
9460 if(typeof(config.cls) != 'undefined'){
9461 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9463 // this is the bit that doesnt reall work at all...
9465 if (this.responsive) {
9468 ['xs','sm','md','lg'].map(function(size){
9470 if(typeof(config[size]) == 'undefined'){
9474 if (!config[size]) { // 0 = hidden
9475 // BS 4 '0' is treated as hide that column and below.
9476 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9480 c.cls += ' col-' + size + '-' + config[size] + (
9481 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9489 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9500 renderBody : function()
9510 colspan : this.cm.getColumnCount()
9520 renderFooter : function()
9530 colspan : this.cm.getColumnCount()
9544 // Roo.log('ds onload');
9549 var ds = this.store;
9551 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9552 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9553 if (_this.store.sortInfo) {
9555 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9556 e.select('i', true).addClass(['fa-arrow-up']);
9559 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9560 e.select('i', true).addClass(['fa-arrow-down']);
9565 var tbody = this.bodyEl;
9567 if(ds.getCount() > 0){
9568 ds.data.each(function(d,rowIndex){
9569 var row = this.renderRow(cm, ds, rowIndex);
9571 tbody.createChild(row);
9575 if(row.cellObjects.length){
9576 Roo.each(row.cellObjects, function(r){
9577 _this.renderCellObject(r);
9584 var tfoot = this.el.select('tfoot', true).first();
9586 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9588 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9590 var total = this.ds.getTotalCount();
9592 if(this.footer.pageSize < total){
9593 this.mainFoot.show();
9597 Roo.each(this.el.select('tbody td', true).elements, function(e){
9598 e.on('mouseover', _this.onMouseover, _this);
9601 Roo.each(this.el.select('tbody td', true).elements, function(e){
9602 e.on('mouseout', _this.onMouseout, _this);
9604 this.fireEvent('rowsrendered', this);
9608 this.initCSS(); /// resize cols
9614 onUpdate : function(ds,record)
9616 this.refreshRow(record);
9620 onRemove : function(ds, record, index, isUpdate){
9621 if(isUpdate !== true){
9622 this.fireEvent("beforerowremoved", this, index, record);
9624 var bt = this.bodyEl.dom;
9626 var rows = this.el.select('tbody > tr', true).elements;
9628 if(typeof(rows[index]) != 'undefined'){
9629 bt.removeChild(rows[index].dom);
9632 // if(bt.rows[index]){
9633 // bt.removeChild(bt.rows[index]);
9636 if(isUpdate !== true){
9637 //this.stripeRows(index);
9638 //this.syncRowHeights(index, index);
9640 this.fireEvent("rowremoved", this, index, record);
9644 onAdd : function(ds, records, rowIndex)
9646 //Roo.log('on Add called');
9647 // - note this does not handle multiple adding very well..
9648 var bt = this.bodyEl.dom;
9649 for (var i =0 ; i < records.length;i++) {
9650 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9651 //Roo.log(records[i]);
9652 //Roo.log(this.store.getAt(rowIndex+i));
9653 this.insertRow(this.store, rowIndex + i, false);
9660 refreshRow : function(record){
9661 var ds = this.store, index;
9662 if(typeof record == 'number'){
9664 record = ds.getAt(index);
9666 index = ds.indexOf(record);
9668 return; // should not happen - but seems to
9671 this.insertRow(ds, index, true);
9673 this.onRemove(ds, record, index+1, true);
9675 //this.syncRowHeights(index, index);
9677 this.fireEvent("rowupdated", this, index, record);
9679 // private - called by RowSelection
9680 onRowSelect : function(rowIndex){
9681 var row = this.getRowDom(rowIndex);
9682 row.addClass(['bg-info','info']);
9684 // private - called by RowSelection
9685 onRowDeselect : function(rowIndex)
9690 var row = this.getRowDom(rowIndex);
9691 row.removeClass(['bg-info','info']);
9694 * Focuses the specified row.
9695 * @param {Number} row The row index
9697 focusRow : function(row)
9699 //Roo.log('GridView.focusRow');
9700 var x = this.bodyEl.dom.scrollLeft;
9701 this.focusCell(row, 0, false);
9702 this.bodyEl.dom.scrollLeft = x;
9706 * Focuses the specified cell.
9707 * @param {Number} row The row index
9708 * @param {Number} col The column index
9709 * @param {Boolean} hscroll false to disable horizontal scrolling
9711 focusCell : function(row, col, hscroll)
9713 //Roo.log('GridView.focusCell');
9714 var el = this.ensureVisible(row, col, hscroll);
9715 // not sure what focusEL achives = it's a <a> pos relative
9716 //this.focusEl.alignTo(el, "tl-tl");
9718 // this.focusEl.focus();
9720 // this.focusEl.focus.defer(1, this.focusEl);
9725 * Scrolls the specified cell into view
9726 * @param {Number} row The row index
9727 * @param {Number} col The column index
9728 * @param {Boolean} hscroll false to disable horizontal scrolling
9730 ensureVisible : function(row, col, hscroll)
9732 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9733 //return null; //disable for testing.
9734 if(typeof row != "number"){
9737 if(row < 0 && row >= this.ds.getCount()){
9740 col = (col !== undefined ? col : 0);
9742 while(cm.isHidden(col)){
9746 var el = this.getCellDom(row, col);
9750 var c = this.bodyEl.dom;
9752 var ctop = parseInt(el.offsetTop, 10);
9753 var cleft = parseInt(el.offsetLeft, 10);
9754 var cbot = ctop + el.offsetHeight;
9755 var cright = cleft + el.offsetWidth;
9757 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9758 var ch = 0; //?? header is not withing the area?
9759 var stop = parseInt(c.scrollTop, 10);
9760 var sleft = parseInt(c.scrollLeft, 10);
9761 var sbot = stop + ch;
9762 var sright = sleft + c.clientWidth;
9764 Roo.log('GridView.ensureVisible:' +
9766 ' c.clientHeight:' + c.clientHeight +
9767 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9776 //Roo.log("set scrolltop to ctop DISABLE?");
9777 }else if(cbot > sbot){
9778 //Roo.log("set scrolltop to cbot-ch");
9779 c.scrollTop = cbot-ch;
9782 if(hscroll !== false){
9784 c.scrollLeft = cleft;
9785 }else if(cright > sright){
9786 c.scrollLeft = cright-c.clientWidth;
9794 insertRow : function(dm, rowIndex, isUpdate){
9797 this.fireEvent("beforerowsinserted", this, rowIndex);
9799 //var s = this.getScrollState();
9800 var row = this.renderRow(this.cm, this.store, rowIndex);
9801 // insert before rowIndex..
9802 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9806 if(row.cellObjects.length){
9807 Roo.each(row.cellObjects, function(r){
9808 _this.renderCellObject(r);
9813 this.fireEvent("rowsinserted", this, rowIndex);
9814 //this.syncRowHeights(firstRow, lastRow);
9815 //this.stripeRows(firstRow);
9822 getRowDom : function(rowIndex)
9824 var rows = this.el.select('tbody > tr', true).elements;
9826 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9829 getCellDom : function(rowIndex, colIndex)
9831 var row = this.getRowDom(rowIndex);
9832 if (row === false) {
9835 var cols = row.select('td', true).elements;
9836 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9840 // returns the object tree for a tr..
9843 renderRow : function(cm, ds, rowIndex)
9845 var d = ds.getAt(rowIndex);
9849 cls : 'x-row-' + rowIndex,
9853 var cellObjects = [];
9855 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9856 var config = cm.config[i];
9858 var renderer = cm.getRenderer(i);
9862 if(typeof(renderer) !== 'undefined'){
9863 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9865 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9866 // and are rendered into the cells after the row is rendered - using the id for the element.
9868 if(typeof(value) === 'object'){
9878 rowIndex : rowIndex,
9883 this.fireEvent('rowclass', this, rowcfg);
9887 // this might end up displaying HTML?
9888 // this is too messy... - better to only do it on columsn you know are going to be too long
9889 //tooltip : (typeof(value) === 'object') ? '' : value,
9890 cls : rowcfg.rowClass + ' x-col-' + i,
9892 html: (typeof(value) === 'object') ? '' : value
9899 if(typeof(config.colspan) != 'undefined'){
9900 td.colspan = config.colspan;
9905 if(typeof(config.align) != 'undefined' && config.align.length){
9906 td.style += ' text-align:' + config.align + ';';
9908 if(typeof(config.valign) != 'undefined' && config.valign.length){
9909 td.style += ' vertical-align:' + config.valign + ';';
9912 if(typeof(config.width) != 'undefined'){
9913 td.style += ' width:' + config.width + 'px;';
9917 if(typeof(config.cursor) != 'undefined'){
9918 td.style += ' cursor:' + config.cursor + ';';
9921 if(typeof(config.cls) != 'undefined'){
9922 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9924 if (this.responsive) {
9925 ['xs','sm','md','lg'].map(function(size){
9927 if(typeof(config[size]) == 'undefined'){
9933 if (!config[size]) { // 0 = hidden
9934 // BS 4 '0' is treated as hide that column and below.
9935 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9939 td.cls += ' col-' + size + '-' + config[size] + (
9940 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9950 row.cellObjects = cellObjects;
9958 onBeforeLoad : function()
9967 this.el.select('tbody', true).first().dom.innerHTML = '';
9970 * Show or hide a row.
9971 * @param {Number} rowIndex to show or hide
9972 * @param {Boolean} state hide
9974 setRowVisibility : function(rowIndex, state)
9976 var bt = this.bodyEl.dom;
9978 var rows = this.el.select('tbody > tr', true).elements;
9980 if(typeof(rows[rowIndex]) == 'undefined'){
9983 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9988 getSelectionModel : function(){
9990 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9992 return this.selModel;
9995 * Render the Roo.bootstrap object from renderder
9997 renderCellObject : function(r)
10001 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10003 var t = r.cfg.render(r.container);
10006 Roo.each(r.cfg.cn, function(c){
10008 container: t.getChildContainer(),
10011 _this.renderCellObject(child);
10016 * get the Row Index from a dom element.
10017 * @param {Roo.Element} row The row to look for
10018 * @returns {Number} the row
10020 getRowIndex : function(row)
10024 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10035 * get the header TH element for columnIndex
10036 * @param {Number} columnIndex
10037 * @returns {Roo.Element}
10039 getHeaderIndex: function(colIndex)
10041 var cols = this.headEl.select('th', true).elements;
10042 return cols[colIndex];
10045 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10046 * @param {domElement} cell to look for
10047 * @returns {Number} the column
10049 getCellIndex : function(cell)
10051 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10053 return parseInt(id[1], 10);
10058 * Returns the grid's underlying element = used by panel.Grid
10059 * @return {Element} The element
10061 getGridEl : function(){
10065 * Forces a resize - used by panel.Grid
10066 * @return {Element} The element
10068 autoSize : function()
10070 //var ctr = Roo.get(this.container.dom.parentElement);
10071 var ctr = Roo.get(this.el.dom);
10073 var thd = this.getGridEl().select('thead',true).first();
10074 var tbd = this.getGridEl().select('tbody', true).first();
10075 var tfd = this.getGridEl().select('tfoot', true).first();
10077 var cw = ctr.getWidth();
10078 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10082 tbd.setWidth(ctr.getWidth());
10083 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10084 // this needs fixing for various usage - currently only hydra job advers I think..
10086 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10088 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10091 cw = Math.max(cw, this.totalWidth);
10092 this.getGridEl().select('tbody tr',true).setWidth(cw);
10095 // resize 'expandable coloumn?
10097 return; // we doe not have a view in this design..
10100 onBodyScroll: function()
10102 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10104 this.headEl.setStyle({
10105 'position' : 'relative',
10106 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10112 var scrollHeight = this.bodyEl.dom.scrollHeight;
10114 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10116 var height = this.bodyEl.getHeight();
10118 if(scrollHeight - height == scrollTop) {
10120 var total = this.ds.getTotalCount();
10122 if(this.footer.cursor + this.footer.pageSize < total){
10124 this.footer.ds.load({
10126 start : this.footer.cursor + this.footer.pageSize,
10127 limit : this.footer.pageSize
10136 onColumnSplitterMoved : function(i, diff)
10138 this.userResized = true;
10140 var cm = this.colModel;
10142 var w = this.getHeaderIndex(i).getWidth() + diff;
10145 cm.setColumnWidth(i, w, true);
10147 //var cid = cm.getColumnId(i); << not used in this version?
10148 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10150 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10151 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10152 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10154 //this.updateSplitters();
10155 //this.layout(); << ??
10156 this.fireEvent("columnresize", i, w);
10158 onHeaderChange : function()
10160 var header = this.renderHeader();
10161 var table = this.el.select('table', true).first();
10163 this.headEl.remove();
10164 this.headEl = table.createChild(header, this.bodyEl, false);
10166 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10167 e.on('click', this.sort, this);
10170 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10171 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10176 onHiddenChange : function(colModel, colIndex, hidden)
10179 this.cm.setHidden()
10180 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10181 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10183 this.CSS.updateRule(thSelector, "display", "");
10184 this.CSS.updateRule(tdSelector, "display", "");
10187 this.CSS.updateRule(thSelector, "display", "none");
10188 this.CSS.updateRule(tdSelector, "display", "none");
10191 // onload calls initCSS()
10192 this.onHeaderChange();
10196 setColumnWidth: function(col_index, width)
10198 // width = "md-2 xs-2..."
10199 if(!this.colModel.config[col_index]) {
10203 var w = width.split(" ");
10205 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10207 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10210 for(var j = 0; j < w.length; j++) {
10216 var size_cls = w[j].split("-");
10218 if(!Number.isInteger(size_cls[1] * 1)) {
10222 if(!this.colModel.config[col_index][size_cls[0]]) {
10226 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10230 h_row[0].classList.replace(
10231 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10232 "col-"+size_cls[0]+"-"+size_cls[1]
10235 for(var i = 0; i < rows.length; i++) {
10237 var size_cls = w[j].split("-");
10239 if(!Number.isInteger(size_cls[1] * 1)) {
10243 if(!this.colModel.config[col_index][size_cls[0]]) {
10247 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10251 rows[i].classList.replace(
10252 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10253 "col-"+size_cls[0]+"-"+size_cls[1]
10257 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10262 // currently only used to find the split on drag..
10263 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10268 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10269 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10278 * @class Roo.bootstrap.TableCell
10279 * @extends Roo.bootstrap.Component
10280 * Bootstrap TableCell class
10281 * @cfg {String} html cell contain text
10282 * @cfg {String} cls cell class
10283 * @cfg {String} tag cell tag (td|th) default td
10284 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10285 * @cfg {String} align Aligns the content in a cell
10286 * @cfg {String} axis Categorizes cells
10287 * @cfg {String} bgcolor Specifies the background color of a cell
10288 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10289 * @cfg {Number} colspan Specifies the number of columns a cell should span
10290 * @cfg {String} headers Specifies one or more header cells a cell is related to
10291 * @cfg {Number} height Sets the height of a cell
10292 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10293 * @cfg {Number} rowspan Sets the number of rows a cell should span
10294 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10295 * @cfg {String} valign Vertical aligns the content in a cell
10296 * @cfg {Number} width Specifies the width of a cell
10299 * Create a new TableCell
10300 * @param {Object} config The config object
10303 Roo.bootstrap.TableCell = function(config){
10304 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10307 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10327 getAutoCreate : function(){
10328 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10335 cfg.tag = this.tag;
10348 cfg.align=this.align
10353 if (this.bgcolor) {
10354 cfg.bgcolor=this.bgcolor
10356 if (this.charoff) {
10357 cfg.charoff=this.charoff
10359 if (this.colspan) {
10360 cfg.colspan=this.colspan
10362 if (this.headers) {
10363 cfg.headers=this.headers
10366 cfg.height=this.height
10369 cfg.nowrap=this.nowrap
10371 if (this.rowspan) {
10372 cfg.rowspan=this.rowspan
10375 cfg.scope=this.scope
10378 cfg.valign=this.valign
10381 cfg.width=this.width
10400 * @class Roo.bootstrap.TableRow
10401 * @extends Roo.bootstrap.Component
10402 * Bootstrap TableRow class
10403 * @cfg {String} cls row class
10404 * @cfg {String} align Aligns the content in a table row
10405 * @cfg {String} bgcolor Specifies a background color for a table row
10406 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10407 * @cfg {String} valign Vertical aligns the content in a table row
10410 * Create a new TableRow
10411 * @param {Object} config The config object
10414 Roo.bootstrap.TableRow = function(config){
10415 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10418 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10426 getAutoCreate : function(){
10427 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10434 cfg.cls = this.cls;
10437 cfg.align = this.align;
10440 cfg.bgcolor = this.bgcolor;
10443 cfg.charoff = this.charoff;
10446 cfg.valign = this.valign;
10464 * @class Roo.bootstrap.TableBody
10465 * @extends Roo.bootstrap.Component
10466 * Bootstrap TableBody class
10467 * @cfg {String} cls element class
10468 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10469 * @cfg {String} align Aligns the content inside the element
10470 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10471 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10474 * Create a new TableBody
10475 * @param {Object} config The config object
10478 Roo.bootstrap.TableBody = function(config){
10479 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10482 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10490 getAutoCreate : function(){
10491 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10501 cfg.tag = this.tag;
10505 cfg.align = this.align;
10508 cfg.charoff = this.charoff;
10511 cfg.valign = this.valign;
10518 // initEvents : function()
10521 // if(!this.store){
10525 // this.store = Roo.factory(this.store, Roo.data);
10526 // this.store.on('load', this.onLoad, this);
10528 // this.store.load();
10532 // onLoad: function ()
10534 // this.fireEvent('load', this);
10544 * Ext JS Library 1.1.1
10545 * Copyright(c) 2006-2007, Ext JS, LLC.
10547 * Originally Released Under LGPL - original licence link has changed is not relivant.
10550 * <script type="text/javascript">
10553 // as we use this in bootstrap.
10554 Roo.namespace('Roo.form');
10556 * @class Roo.form.Action
10557 * Internal Class used to handle form actions
10559 * @param {Roo.form.BasicForm} el The form element or its id
10560 * @param {Object} config Configuration options
10565 // define the action interface
10566 Roo.form.Action = function(form, options){
10568 this.options = options || {};
10571 * Client Validation Failed
10574 Roo.form.Action.CLIENT_INVALID = 'client';
10576 * Server Validation Failed
10579 Roo.form.Action.SERVER_INVALID = 'server';
10581 * Connect to Server Failed
10584 Roo.form.Action.CONNECT_FAILURE = 'connect';
10586 * Reading Data from Server Failed
10589 Roo.form.Action.LOAD_FAILURE = 'load';
10591 Roo.form.Action.prototype = {
10593 failureType : undefined,
10594 response : undefined,
10595 result : undefined,
10597 // interface method
10598 run : function(options){
10602 // interface method
10603 success : function(response){
10607 // interface method
10608 handleResponse : function(response){
10612 // default connection failure
10613 failure : function(response){
10615 this.response = response;
10616 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10617 this.form.afterAction(this, false);
10620 processResponse : function(response){
10621 this.response = response;
10622 if(!response.responseText){
10625 this.result = this.handleResponse(response);
10626 return this.result;
10629 // utility functions used internally
10630 getUrl : function(appendParams){
10631 var url = this.options.url || this.form.url || this.form.el.dom.action;
10633 var p = this.getParams();
10635 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10641 getMethod : function(){
10642 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10645 getParams : function(){
10646 var bp = this.form.baseParams;
10647 var p = this.options.params;
10649 if(typeof p == "object"){
10650 p = Roo.urlEncode(Roo.applyIf(p, bp));
10651 }else if(typeof p == 'string' && bp){
10652 p += '&' + Roo.urlEncode(bp);
10655 p = Roo.urlEncode(bp);
10660 createCallback : function(){
10662 success: this.success,
10663 failure: this.failure,
10665 timeout: (this.form.timeout*1000),
10666 upload: this.form.fileUpload ? this.success : undefined
10671 Roo.form.Action.Submit = function(form, options){
10672 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10675 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10678 haveProgress : false,
10679 uploadComplete : false,
10681 // uploadProgress indicator.
10682 uploadProgress : function()
10684 if (!this.form.progressUrl) {
10688 if (!this.haveProgress) {
10689 Roo.MessageBox.progress("Uploading", "Uploading");
10691 if (this.uploadComplete) {
10692 Roo.MessageBox.hide();
10696 this.haveProgress = true;
10698 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10700 var c = new Roo.data.Connection();
10702 url : this.form.progressUrl,
10707 success : function(req){
10708 //console.log(data);
10712 rdata = Roo.decode(req.responseText)
10714 Roo.log("Invalid data from server..");
10718 if (!rdata || !rdata.success) {
10720 Roo.MessageBox.alert(Roo.encode(rdata));
10723 var data = rdata.data;
10725 if (this.uploadComplete) {
10726 Roo.MessageBox.hide();
10731 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10732 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10735 this.uploadProgress.defer(2000,this);
10738 failure: function(data) {
10739 Roo.log('progress url failed ');
10750 // run get Values on the form, so it syncs any secondary forms.
10751 this.form.getValues();
10753 var o = this.options;
10754 var method = this.getMethod();
10755 var isPost = method == 'POST';
10756 if(o.clientValidation === false || this.form.isValid()){
10758 if (this.form.progressUrl) {
10759 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10760 (new Date() * 1) + '' + Math.random());
10765 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10766 form:this.form.el.dom,
10767 url:this.getUrl(!isPost),
10769 params:isPost ? this.getParams() : null,
10770 isUpload: this.form.fileUpload,
10771 formData : this.form.formData
10774 this.uploadProgress();
10776 }else if (o.clientValidation !== false){ // client validation failed
10777 this.failureType = Roo.form.Action.CLIENT_INVALID;
10778 this.form.afterAction(this, false);
10782 success : function(response)
10784 this.uploadComplete= true;
10785 if (this.haveProgress) {
10786 Roo.MessageBox.hide();
10790 var result = this.processResponse(response);
10791 if(result === true || result.success){
10792 this.form.afterAction(this, true);
10796 this.form.markInvalid(result.errors);
10797 this.failureType = Roo.form.Action.SERVER_INVALID;
10799 this.form.afterAction(this, false);
10801 failure : function(response)
10803 this.uploadComplete= true;
10804 if (this.haveProgress) {
10805 Roo.MessageBox.hide();
10808 this.response = response;
10809 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10810 this.form.afterAction(this, false);
10813 handleResponse : function(response){
10814 if(this.form.errorReader){
10815 var rs = this.form.errorReader.read(response);
10818 for(var i = 0, len = rs.records.length; i < len; i++) {
10819 var r = rs.records[i];
10820 errors[i] = r.data;
10823 if(errors.length < 1){
10827 success : rs.success,
10833 ret = Roo.decode(response.responseText);
10837 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10847 Roo.form.Action.Load = function(form, options){
10848 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10849 this.reader = this.form.reader;
10852 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10857 Roo.Ajax.request(Roo.apply(
10858 this.createCallback(), {
10859 method:this.getMethod(),
10860 url:this.getUrl(false),
10861 params:this.getParams()
10865 success : function(response){
10867 var result = this.processResponse(response);
10868 if(result === true || !result.success || !result.data){
10869 this.failureType = Roo.form.Action.LOAD_FAILURE;
10870 this.form.afterAction(this, false);
10873 this.form.clearInvalid();
10874 this.form.setValues(result.data);
10875 this.form.afterAction(this, true);
10878 handleResponse : function(response){
10879 if(this.form.reader){
10880 var rs = this.form.reader.read(response);
10881 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10883 success : rs.success,
10887 return Roo.decode(response.responseText);
10891 Roo.form.Action.ACTION_TYPES = {
10892 'load' : Roo.form.Action.Load,
10893 'submit' : Roo.form.Action.Submit
10902 * @class Roo.bootstrap.Form
10903 * @extends Roo.bootstrap.Component
10904 * Bootstrap Form class
10905 * @cfg {String} method GET | POST (default POST)
10906 * @cfg {String} labelAlign top | left (default top)
10907 * @cfg {String} align left | right - for navbars
10908 * @cfg {Boolean} loadMask load mask when submit (default true)
10912 * Create a new Form
10913 * @param {Object} config The config object
10917 Roo.bootstrap.Form = function(config){
10919 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10921 Roo.bootstrap.Form.popover.apply();
10925 * @event clientvalidation
10926 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10927 * @param {Form} this
10928 * @param {Boolean} valid true if the form has passed client-side validation
10930 clientvalidation: true,
10932 * @event beforeaction
10933 * Fires before any action is performed. Return false to cancel the action.
10934 * @param {Form} this
10935 * @param {Action} action The action to be performed
10937 beforeaction: true,
10939 * @event actionfailed
10940 * Fires when an action fails.
10941 * @param {Form} this
10942 * @param {Action} action The action that failed
10944 actionfailed : true,
10946 * @event actioncomplete
10947 * Fires when an action is completed.
10948 * @param {Form} this
10949 * @param {Action} action The action that completed
10951 actioncomplete : true
10955 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10958 * @cfg {String} method
10959 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10963 * @cfg {String} url
10964 * The URL to use for form actions if one isn't supplied in the action options.
10967 * @cfg {Boolean} fileUpload
10968 * Set to true if this form is a file upload.
10972 * @cfg {Object} baseParams
10973 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10977 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10981 * @cfg {Sting} align (left|right) for navbar forms
10986 activeAction : null,
10989 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10990 * element by passing it or its id or mask the form itself by passing in true.
10993 waitMsgTarget : false,
10998 * @cfg {Boolean} errorMask (true|false) default false
11003 * @cfg {Number} maskOffset Default 100
11008 * @cfg {Boolean} maskBody
11012 getAutoCreate : function(){
11016 method : this.method || 'POST',
11017 id : this.id || Roo.id(),
11020 if (this.parent().xtype.match(/^Nav/)) {
11021 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11025 if (this.labelAlign == 'left' ) {
11026 cfg.cls += ' form-horizontal';
11032 initEvents : function()
11034 this.el.on('submit', this.onSubmit, this);
11035 // this was added as random key presses on the form where triggering form submit.
11036 this.el.on('keypress', function(e) {
11037 if (e.getCharCode() != 13) {
11040 // we might need to allow it for textareas.. and some other items.
11041 // check e.getTarget().
11043 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11047 Roo.log("keypress blocked");
11049 e.preventDefault();
11055 onSubmit : function(e){
11060 * Returns true if client-side validation on the form is successful.
11063 isValid : function(){
11064 var items = this.getItems();
11066 var target = false;
11068 items.each(function(f){
11074 Roo.log('invalid field: ' + f.name);
11078 if(!target && f.el.isVisible(true)){
11084 if(this.errorMask && !valid){
11085 Roo.bootstrap.Form.popover.mask(this, target);
11092 * Returns true if any fields in this form have changed since their original load.
11095 isDirty : function(){
11097 var items = this.getItems();
11098 items.each(function(f){
11108 * Performs a predefined action (submit or load) or custom actions you define on this form.
11109 * @param {String} actionName The name of the action type
11110 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11111 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11112 * accept other config options):
11114 Property Type Description
11115 ---------------- --------------- ----------------------------------------------------------------------------------
11116 url String The url for the action (defaults to the form's url)
11117 method String The form method to use (defaults to the form's method, or POST if not defined)
11118 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11119 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11120 validate the form on the client (defaults to false)
11122 * @return {BasicForm} this
11124 doAction : function(action, options){
11125 if(typeof action == 'string'){
11126 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11128 if(this.fireEvent('beforeaction', this, action) !== false){
11129 this.beforeAction(action);
11130 action.run.defer(100, action);
11136 beforeAction : function(action){
11137 var o = action.options;
11142 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11144 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11147 // not really supported yet.. ??
11149 //if(this.waitMsgTarget === true){
11150 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11151 //}else if(this.waitMsgTarget){
11152 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11153 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11155 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11161 afterAction : function(action, success){
11162 this.activeAction = null;
11163 var o = action.options;
11168 Roo.get(document.body).unmask();
11174 //if(this.waitMsgTarget === true){
11175 // this.el.unmask();
11176 //}else if(this.waitMsgTarget){
11177 // this.waitMsgTarget.unmask();
11179 // Roo.MessageBox.updateProgress(1);
11180 // Roo.MessageBox.hide();
11187 Roo.callback(o.success, o.scope, [this, action]);
11188 this.fireEvent('actioncomplete', this, action);
11192 // failure condition..
11193 // we have a scenario where updates need confirming.
11194 // eg. if a locking scenario exists..
11195 // we look for { errors : { needs_confirm : true }} in the response.
11197 (typeof(action.result) != 'undefined') &&
11198 (typeof(action.result.errors) != 'undefined') &&
11199 (typeof(action.result.errors.needs_confirm) != 'undefined')
11202 Roo.log("not supported yet");
11205 Roo.MessageBox.confirm(
11206 "Change requires confirmation",
11207 action.result.errorMsg,
11212 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11222 Roo.callback(o.failure, o.scope, [this, action]);
11223 // show an error message if no failed handler is set..
11224 if (!this.hasListener('actionfailed')) {
11225 Roo.log("need to add dialog support");
11227 Roo.MessageBox.alert("Error",
11228 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11229 action.result.errorMsg :
11230 "Saving Failed, please check your entries or try again"
11235 this.fireEvent('actionfailed', this, action);
11240 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11241 * @param {String} id The value to search for
11244 findField : function(id){
11245 var items = this.getItems();
11246 var field = items.get(id);
11248 items.each(function(f){
11249 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11256 return field || null;
11259 * Mark fields in this form invalid in bulk.
11260 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11261 * @return {BasicForm} this
11263 markInvalid : function(errors){
11264 if(errors instanceof Array){
11265 for(var i = 0, len = errors.length; i < len; i++){
11266 var fieldError = errors[i];
11267 var f = this.findField(fieldError.id);
11269 f.markInvalid(fieldError.msg);
11275 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11276 field.markInvalid(errors[id]);
11280 //Roo.each(this.childForms || [], function (f) {
11281 // f.markInvalid(errors);
11288 * Set values for fields in this form in bulk.
11289 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11290 * @return {BasicForm} this
11292 setValues : function(values){
11293 if(values instanceof Array){ // array of objects
11294 for(var i = 0, len = values.length; i < len; i++){
11296 var f = this.findField(v.id);
11298 f.setValue(v.value);
11299 if(this.trackResetOnLoad){
11300 f.originalValue = f.getValue();
11304 }else{ // object hash
11307 if(typeof values[id] != 'function' && (field = this.findField(id))){
11309 if (field.setFromData &&
11310 field.valueField &&
11311 field.displayField &&
11312 // combos' with local stores can
11313 // be queried via setValue()
11314 // to set their value..
11315 (field.store && !field.store.isLocal)
11319 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11320 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11321 field.setFromData(sd);
11323 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11325 field.setFromData(values);
11328 field.setValue(values[id]);
11332 if(this.trackResetOnLoad){
11333 field.originalValue = field.getValue();
11339 //Roo.each(this.childForms || [], function (f) {
11340 // f.setValues(values);
11347 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11348 * they are returned as an array.
11349 * @param {Boolean} asString
11352 getValues : function(asString){
11353 //if (this.childForms) {
11354 // copy values from the child forms
11355 // Roo.each(this.childForms, function (f) {
11356 // this.setValues(f.getValues());
11362 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11363 if(asString === true){
11366 return Roo.urlDecode(fs);
11370 * Returns the fields in this form as an object with key/value pairs.
11371 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11374 getFieldValues : function(with_hidden)
11376 var items = this.getItems();
11378 items.each(function(f){
11380 if (!f.getName()) {
11384 var v = f.getValue();
11386 if (f.inputType =='radio') {
11387 if (typeof(ret[f.getName()]) == 'undefined') {
11388 ret[f.getName()] = ''; // empty..
11391 if (!f.el.dom.checked) {
11395 v = f.el.dom.value;
11399 if(f.xtype == 'MoneyField'){
11400 ret[f.currencyName] = f.getCurrency();
11403 // not sure if this supported any more..
11404 if ((typeof(v) == 'object') && f.getRawValue) {
11405 v = f.getRawValue() ; // dates..
11407 // combo boxes where name != hiddenName...
11408 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11409 ret[f.name] = f.getRawValue();
11411 ret[f.getName()] = v;
11418 * Clears all invalid messages in this form.
11419 * @return {BasicForm} this
11421 clearInvalid : function(){
11422 var items = this.getItems();
11424 items.each(function(f){
11432 * Resets this form.
11433 * @return {BasicForm} this
11435 reset : function(){
11436 var items = this.getItems();
11437 items.each(function(f){
11441 Roo.each(this.childForms || [], function (f) {
11449 getItems : function()
11451 var r=new Roo.util.MixedCollection(false, function(o){
11452 return o.id || (o.id = Roo.id());
11454 var iter = function(el) {
11461 Roo.each(el.items,function(e) {
11470 hideFields : function(items)
11472 Roo.each(items, function(i){
11474 var f = this.findField(i);
11485 showFields : function(items)
11487 Roo.each(items, function(i){
11489 var f = this.findField(i);
11502 Roo.apply(Roo.bootstrap.Form, {
11518 intervalID : false,
11524 if(this.isApplied){
11529 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11530 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11531 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11532 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11535 this.maskEl.top.enableDisplayMode("block");
11536 this.maskEl.left.enableDisplayMode("block");
11537 this.maskEl.bottom.enableDisplayMode("block");
11538 this.maskEl.right.enableDisplayMode("block");
11540 this.toolTip = new Roo.bootstrap.Tooltip({
11541 cls : 'roo-form-error-popover',
11543 'left' : ['r-l', [-2,0], 'right'],
11544 'right' : ['l-r', [2,0], 'left'],
11545 'bottom' : ['tl-bl', [0,2], 'top'],
11546 'top' : [ 'bl-tl', [0,-2], 'bottom']
11550 this.toolTip.render(Roo.get(document.body));
11552 this.toolTip.el.enableDisplayMode("block");
11554 Roo.get(document.body).on('click', function(){
11558 Roo.get(document.body).on('touchstart', function(){
11562 this.isApplied = true
11565 mask : function(form, target)
11569 this.target = target;
11571 if(!this.form.errorMask || !target.el){
11575 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11577 Roo.log(scrollable);
11579 var ot = this.target.el.calcOffsetsTo(scrollable);
11581 var scrollTo = ot[1] - this.form.maskOffset;
11583 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11585 scrollable.scrollTo('top', scrollTo);
11587 var box = this.target.el.getBox();
11589 var zIndex = Roo.bootstrap.Modal.zIndex++;
11592 this.maskEl.top.setStyle('position', 'absolute');
11593 this.maskEl.top.setStyle('z-index', zIndex);
11594 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11595 this.maskEl.top.setLeft(0);
11596 this.maskEl.top.setTop(0);
11597 this.maskEl.top.show();
11599 this.maskEl.left.setStyle('position', 'absolute');
11600 this.maskEl.left.setStyle('z-index', zIndex);
11601 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11602 this.maskEl.left.setLeft(0);
11603 this.maskEl.left.setTop(box.y - this.padding);
11604 this.maskEl.left.show();
11606 this.maskEl.bottom.setStyle('position', 'absolute');
11607 this.maskEl.bottom.setStyle('z-index', zIndex);
11608 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11609 this.maskEl.bottom.setLeft(0);
11610 this.maskEl.bottom.setTop(box.bottom + this.padding);
11611 this.maskEl.bottom.show();
11613 this.maskEl.right.setStyle('position', 'absolute');
11614 this.maskEl.right.setStyle('z-index', zIndex);
11615 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11616 this.maskEl.right.setLeft(box.right + this.padding);
11617 this.maskEl.right.setTop(box.y - this.padding);
11618 this.maskEl.right.show();
11620 this.toolTip.bindEl = this.target.el;
11622 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11624 var tip = this.target.blankText;
11626 if(this.target.getValue() !== '' ) {
11628 if (this.target.invalidText.length) {
11629 tip = this.target.invalidText;
11630 } else if (this.target.regexText.length){
11631 tip = this.target.regexText;
11635 this.toolTip.show(tip);
11637 this.intervalID = window.setInterval(function() {
11638 Roo.bootstrap.Form.popover.unmask();
11641 window.onwheel = function(){ return false;};
11643 (function(){ this.isMasked = true; }).defer(500, this);
11647 unmask : function()
11649 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11653 this.maskEl.top.setStyle('position', 'absolute');
11654 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11655 this.maskEl.top.hide();
11657 this.maskEl.left.setStyle('position', 'absolute');
11658 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11659 this.maskEl.left.hide();
11661 this.maskEl.bottom.setStyle('position', 'absolute');
11662 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11663 this.maskEl.bottom.hide();
11665 this.maskEl.right.setStyle('position', 'absolute');
11666 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11667 this.maskEl.right.hide();
11669 this.toolTip.hide();
11671 this.toolTip.el.hide();
11673 window.onwheel = function(){ return true;};
11675 if(this.intervalID){
11676 window.clearInterval(this.intervalID);
11677 this.intervalID = false;
11680 this.isMasked = false;
11690 * Ext JS Library 1.1.1
11691 * Copyright(c) 2006-2007, Ext JS, LLC.
11693 * Originally Released Under LGPL - original licence link has changed is not relivant.
11696 * <script type="text/javascript">
11699 * @class Roo.form.VTypes
11700 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11703 Roo.form.VTypes = function(){
11704 // closure these in so they are only created once.
11705 var alpha = /^[a-zA-Z_]+$/;
11706 var alphanum = /^[a-zA-Z0-9_]+$/;
11707 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11708 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11710 // All these messages and functions are configurable
11713 * The function used to validate email addresses
11714 * @param {String} value The email address
11716 'email' : function(v){
11717 return email.test(v);
11720 * The error text to display when the email validation function returns false
11723 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11725 * The keystroke filter mask to be applied on email input
11728 'emailMask' : /[a-z0-9_\.\-@]/i,
11731 * The function used to validate URLs
11732 * @param {String} value The URL
11734 'url' : function(v){
11735 return url.test(v);
11738 * The error text to display when the url validation function returns false
11741 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11744 * The function used to validate alpha values
11745 * @param {String} value The value
11747 'alpha' : function(v){
11748 return alpha.test(v);
11751 * The error text to display when the alpha validation function returns false
11754 'alphaText' : 'This field should only contain letters and _',
11756 * The keystroke filter mask to be applied on alpha input
11759 'alphaMask' : /[a-z_]/i,
11762 * The function used to validate alphanumeric values
11763 * @param {String} value The value
11765 'alphanum' : function(v){
11766 return alphanum.test(v);
11769 * The error text to display when the alphanumeric validation function returns false
11772 'alphanumText' : 'This field should only contain letters, numbers and _',
11774 * The keystroke filter mask to be applied on alphanumeric input
11777 'alphanumMask' : /[a-z0-9_]/i
11787 * @class Roo.bootstrap.Input
11788 * @extends Roo.bootstrap.Component
11789 * Bootstrap Input class
11790 * @cfg {Boolean} disabled is it disabled
11791 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11792 * @cfg {String} name name of the input
11793 * @cfg {string} fieldLabel - the label associated
11794 * @cfg {string} placeholder - placeholder to put in text.
11795 * @cfg {string} before - input group add on before
11796 * @cfg {string} after - input group add on after
11797 * @cfg {string} size - (lg|sm) or leave empty..
11798 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11799 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11800 * @cfg {Number} md colspan out of 12 for computer-sized screens
11801 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11802 * @cfg {string} value default value of the input
11803 * @cfg {Number} labelWidth set the width of label
11804 * @cfg {Number} labellg set the width of label (1-12)
11805 * @cfg {Number} labelmd set the width of label (1-12)
11806 * @cfg {Number} labelsm set the width of label (1-12)
11807 * @cfg {Number} labelxs set the width of label (1-12)
11808 * @cfg {String} labelAlign (top|left)
11809 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11810 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11811 * @cfg {String} indicatorpos (left|right) default left
11812 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11813 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11814 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11816 * @cfg {String} align (left|center|right) Default left
11817 * @cfg {Boolean} forceFeedback (true|false) Default false
11820 * Create a new Input
11821 * @param {Object} config The config object
11824 Roo.bootstrap.Input = function(config){
11826 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11831 * Fires when this field receives input focus.
11832 * @param {Roo.form.Field} this
11837 * Fires when this field loses input focus.
11838 * @param {Roo.form.Field} this
11842 * @event specialkey
11843 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11844 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11845 * @param {Roo.form.Field} this
11846 * @param {Roo.EventObject} e The event object
11851 * Fires just before the field blurs if the field value has changed.
11852 * @param {Roo.form.Field} this
11853 * @param {Mixed} newValue The new value
11854 * @param {Mixed} oldValue The original value
11859 * Fires after the field has been marked as invalid.
11860 * @param {Roo.form.Field} this
11861 * @param {String} msg The validation message
11866 * Fires after the field has been validated with no errors.
11867 * @param {Roo.form.Field} this
11872 * Fires after the key up
11873 * @param {Roo.form.Field} this
11874 * @param {Roo.EventObject} e The event Object
11879 * Fires after the user pastes into input
11880 * @param {Roo.form.Field} this
11881 * @param {Roo.EventObject} e The event Object
11887 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11889 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11890 automatic validation (defaults to "keyup").
11892 validationEvent : "keyup",
11894 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11896 validateOnBlur : true,
11898 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11900 validationDelay : 250,
11902 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11904 focusClass : "x-form-focus", // not needed???
11908 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11910 invalidClass : "has-warning",
11913 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11915 validClass : "has-success",
11918 * @cfg {Boolean} hasFeedback (true|false) default true
11920 hasFeedback : true,
11923 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11925 invalidFeedbackClass : "glyphicon-warning-sign",
11928 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11930 validFeedbackClass : "glyphicon-ok",
11933 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11935 selectOnFocus : false,
11938 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11942 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11947 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11949 disableKeyFilter : false,
11952 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11956 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11960 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11962 blankText : "Please complete this mandatory field",
11965 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11969 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11971 maxLength : Number.MAX_VALUE,
11973 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11975 minLengthText : "The minimum length for this field is {0}",
11977 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11979 maxLengthText : "The maximum length for this field is {0}",
11983 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11984 * If available, this function will be called only after the basic validators all return true, and will be passed the
11985 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11989 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11990 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11991 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11995 * @cfg {String} regexText -- Depricated - use Invalid Text
12000 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12006 autocomplete: false,
12010 inputType : 'text',
12013 placeholder: false,
12018 preventMark: false,
12019 isFormField : true,
12022 labelAlign : false,
12025 formatedValue : false,
12026 forceFeedback : false,
12028 indicatorpos : 'left',
12038 parentLabelAlign : function()
12041 while (parent.parent()) {
12042 parent = parent.parent();
12043 if (typeof(parent.labelAlign) !='undefined') {
12044 return parent.labelAlign;
12051 getAutoCreate : function()
12053 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12059 if(this.inputType != 'hidden'){
12060 cfg.cls = 'form-group' //input-group
12066 type : this.inputType,
12067 value : this.value,
12068 cls : 'form-control',
12069 placeholder : this.placeholder || '',
12070 autocomplete : this.autocomplete || 'new-password'
12072 if (this.inputType == 'file') {
12073 input.style = 'overflow:hidden'; // why not in CSS?
12076 if(this.capture.length){
12077 input.capture = this.capture;
12080 if(this.accept.length){
12081 input.accept = this.accept + "/*";
12085 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12088 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12089 input.maxLength = this.maxLength;
12092 if (this.disabled) {
12093 input.disabled=true;
12096 if (this.readOnly) {
12097 input.readonly=true;
12101 input.name = this.name;
12105 input.cls += ' input-' + this.size;
12109 ['xs','sm','md','lg'].map(function(size){
12110 if (settings[size]) {
12111 cfg.cls += ' col-' + size + '-' + settings[size];
12115 var inputblock = input;
12119 cls: 'glyphicon form-control-feedback'
12122 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12125 cls : 'has-feedback',
12133 if (this.before || this.after) {
12136 cls : 'input-group',
12140 if (this.before && typeof(this.before) == 'string') {
12142 inputblock.cn.push({
12144 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12148 if (this.before && typeof(this.before) == 'object') {
12149 this.before = Roo.factory(this.before);
12151 inputblock.cn.push({
12153 cls : 'roo-input-before input-group-prepend input-group-' +
12154 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12158 inputblock.cn.push(input);
12160 if (this.after && typeof(this.after) == 'string') {
12161 inputblock.cn.push({
12163 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12167 if (this.after && typeof(this.after) == 'object') {
12168 this.after = Roo.factory(this.after);
12170 inputblock.cn.push({
12172 cls : 'roo-input-after input-group-append input-group-' +
12173 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12177 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12178 inputblock.cls += ' has-feedback';
12179 inputblock.cn.push(feedback);
12184 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12185 tooltip : 'This field is required'
12187 if (this.allowBlank ) {
12188 indicator.style = this.allowBlank ? ' display:none' : '';
12190 if (align ==='left' && this.fieldLabel.length) {
12192 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12199 cls : 'control-label col-form-label',
12200 html : this.fieldLabel
12211 var labelCfg = cfg.cn[1];
12212 var contentCfg = cfg.cn[2];
12214 if(this.indicatorpos == 'right'){
12219 cls : 'control-label col-form-label',
12223 html : this.fieldLabel
12237 labelCfg = cfg.cn[0];
12238 contentCfg = cfg.cn[1];
12242 if(this.labelWidth > 12){
12243 labelCfg.style = "width: " + this.labelWidth + 'px';
12246 if(this.labelWidth < 13 && this.labelmd == 0){
12247 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12250 if(this.labellg > 0){
12251 labelCfg.cls += ' col-lg-' + this.labellg;
12252 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12255 if(this.labelmd > 0){
12256 labelCfg.cls += ' col-md-' + this.labelmd;
12257 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12260 if(this.labelsm > 0){
12261 labelCfg.cls += ' col-sm-' + this.labelsm;
12262 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12265 if(this.labelxs > 0){
12266 labelCfg.cls += ' col-xs-' + this.labelxs;
12267 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12271 } else if ( this.fieldLabel.length) {
12278 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12279 tooltip : 'This field is required',
12280 style : this.allowBlank ? ' display:none' : ''
12284 //cls : 'input-group-addon',
12285 html : this.fieldLabel
12293 if(this.indicatorpos == 'right'){
12298 //cls : 'input-group-addon',
12299 html : this.fieldLabel
12304 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12305 tooltip : 'This field is required',
12306 style : this.allowBlank ? ' display:none' : ''
12326 if (this.parentType === 'Navbar' && this.parent().bar) {
12327 cfg.cls += ' navbar-form';
12330 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12331 // on BS4 we do this only if not form
12332 cfg.cls += ' navbar-form';
12340 * return the real input element.
12342 inputEl: function ()
12344 return this.el.select('input.form-control',true).first();
12347 tooltipEl : function()
12349 return this.inputEl();
12352 indicatorEl : function()
12354 if (Roo.bootstrap.version == 4) {
12355 return false; // not enabled in v4 yet.
12358 var indicator = this.el.select('i.roo-required-indicator',true).first();
12368 setDisabled : function(v)
12370 var i = this.inputEl().dom;
12372 i.removeAttribute('disabled');
12376 i.setAttribute('disabled','true');
12378 initEvents : function()
12381 this.inputEl().on("keydown" , this.fireKey, this);
12382 this.inputEl().on("focus", this.onFocus, this);
12383 this.inputEl().on("blur", this.onBlur, this);
12385 this.inputEl().relayEvent('keyup', this);
12386 this.inputEl().relayEvent('paste', this);
12388 this.indicator = this.indicatorEl();
12390 if(this.indicator){
12391 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12394 // reference to original value for reset
12395 this.originalValue = this.getValue();
12396 //Roo.form.TextField.superclass.initEvents.call(this);
12397 if(this.validationEvent == 'keyup'){
12398 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12399 this.inputEl().on('keyup', this.filterValidation, this);
12401 else if(this.validationEvent !== false){
12402 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12405 if(this.selectOnFocus){
12406 this.on("focus", this.preFocus, this);
12409 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12410 this.inputEl().on("keypress", this.filterKeys, this);
12412 this.inputEl().relayEvent('keypress', this);
12415 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12416 this.el.on("click", this.autoSize, this);
12419 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12420 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12423 if (typeof(this.before) == 'object') {
12424 this.before.render(this.el.select('.roo-input-before',true).first());
12426 if (typeof(this.after) == 'object') {
12427 this.after.render(this.el.select('.roo-input-after',true).first());
12430 this.inputEl().on('change', this.onChange, this);
12433 filterValidation : function(e){
12434 if(!e.isNavKeyPress()){
12435 this.validationTask.delay(this.validationDelay);
12439 * Validates the field value
12440 * @return {Boolean} True if the value is valid, else false
12442 validate : function(){
12443 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12444 if(this.disabled || this.validateValue(this.getRawValue())){
12449 this.markInvalid();
12455 * Validates a value according to the field's validation rules and marks the field as invalid
12456 * if the validation fails
12457 * @param {Mixed} value The value to validate
12458 * @return {Boolean} True if the value is valid, else false
12460 validateValue : function(value)
12462 if(this.getVisibilityEl().hasClass('hidden')){
12466 if(value.length < 1) { // if it's blank
12467 if(this.allowBlank){
12473 if(value.length < this.minLength){
12476 if(value.length > this.maxLength){
12480 var vt = Roo.form.VTypes;
12481 if(!vt[this.vtype](value, this)){
12485 if(typeof this.validator == "function"){
12486 var msg = this.validator(value);
12490 if (typeof(msg) == 'string') {
12491 this.invalidText = msg;
12495 if(this.regex && !this.regex.test(value)){
12503 fireKey : function(e){
12504 //Roo.log('field ' + e.getKey());
12505 if(e.isNavKeyPress()){
12506 this.fireEvent("specialkey", this, e);
12509 focus : function (selectText){
12511 this.inputEl().focus();
12512 if(selectText === true){
12513 this.inputEl().dom.select();
12519 onFocus : function(){
12520 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12521 // this.el.addClass(this.focusClass);
12523 if(!this.hasFocus){
12524 this.hasFocus = true;
12525 this.startValue = this.getValue();
12526 this.fireEvent("focus", this);
12530 beforeBlur : Roo.emptyFn,
12534 onBlur : function(){
12536 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12537 //this.el.removeClass(this.focusClass);
12539 this.hasFocus = false;
12540 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12543 var v = this.getValue();
12544 if(String(v) !== String(this.startValue)){
12545 this.fireEvent('change', this, v, this.startValue);
12547 this.fireEvent("blur", this);
12550 onChange : function(e)
12552 var v = this.getValue();
12553 if(String(v) !== String(this.startValue)){
12554 this.fireEvent('change', this, v, this.startValue);
12560 * Resets the current field value to the originally loaded value and clears any validation messages
12562 reset : function(){
12563 this.setValue(this.originalValue);
12567 * Returns the name of the field
12568 * @return {Mixed} name The name field
12570 getName: function(){
12574 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12575 * @return {Mixed} value The field value
12577 getValue : function(){
12579 var v = this.inputEl().getValue();
12584 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12585 * @return {Mixed} value The field value
12587 getRawValue : function(){
12588 var v = this.inputEl().getValue();
12594 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12595 * @param {Mixed} value The value to set
12597 setRawValue : function(v){
12598 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12601 selectText : function(start, end){
12602 var v = this.getRawValue();
12604 start = start === undefined ? 0 : start;
12605 end = end === undefined ? v.length : end;
12606 var d = this.inputEl().dom;
12607 if(d.setSelectionRange){
12608 d.setSelectionRange(start, end);
12609 }else if(d.createTextRange){
12610 var range = d.createTextRange();
12611 range.moveStart("character", start);
12612 range.moveEnd("character", v.length-end);
12619 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12620 * @param {Mixed} value The value to set
12622 setValue : function(v){
12625 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12631 processValue : function(value){
12632 if(this.stripCharsRe){
12633 var newValue = value.replace(this.stripCharsRe, '');
12634 if(newValue !== value){
12635 this.setRawValue(newValue);
12642 preFocus : function(){
12644 if(this.selectOnFocus){
12645 this.inputEl().dom.select();
12648 filterKeys : function(e){
12649 var k = e.getKey();
12650 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12653 var c = e.getCharCode(), cc = String.fromCharCode(c);
12654 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12657 if(!this.maskRe.test(cc)){
12662 * Clear any invalid styles/messages for this field
12664 clearInvalid : function(){
12666 if(!this.el || this.preventMark){ // not rendered
12671 this.el.removeClass([this.invalidClass, 'is-invalid']);
12673 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12675 var feedback = this.el.select('.form-control-feedback', true).first();
12678 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12683 if(this.indicator){
12684 this.indicator.removeClass('visible');
12685 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12688 this.fireEvent('valid', this);
12692 * Mark this field as valid
12694 markValid : function()
12696 if(!this.el || this.preventMark){ // not rendered...
12700 this.el.removeClass([this.invalidClass, this.validClass]);
12701 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12703 var feedback = this.el.select('.form-control-feedback', true).first();
12706 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12709 if(this.indicator){
12710 this.indicator.removeClass('visible');
12711 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12719 if(this.allowBlank && !this.getRawValue().length){
12722 if (Roo.bootstrap.version == 3) {
12723 this.el.addClass(this.validClass);
12725 this.inputEl().addClass('is-valid');
12728 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12730 var feedback = this.el.select('.form-control-feedback', true).first();
12733 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12734 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12739 this.fireEvent('valid', this);
12743 * Mark this field as invalid
12744 * @param {String} msg The validation message
12746 markInvalid : function(msg)
12748 if(!this.el || this.preventMark){ // not rendered
12752 this.el.removeClass([this.invalidClass, this.validClass]);
12753 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12755 var feedback = this.el.select('.form-control-feedback', true).first();
12758 this.el.select('.form-control-feedback', true).first().removeClass(
12759 [this.invalidFeedbackClass, this.validFeedbackClass]);
12766 if(this.allowBlank && !this.getRawValue().length){
12770 if(this.indicator){
12771 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12772 this.indicator.addClass('visible');
12774 if (Roo.bootstrap.version == 3) {
12775 this.el.addClass(this.invalidClass);
12777 this.inputEl().addClass('is-invalid');
12782 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12784 var feedback = this.el.select('.form-control-feedback', true).first();
12787 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12789 if(this.getValue().length || this.forceFeedback){
12790 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12797 this.fireEvent('invalid', this, msg);
12800 SafariOnKeyDown : function(event)
12802 // this is a workaround for a password hang bug on chrome/ webkit.
12803 if (this.inputEl().dom.type != 'password') {
12807 var isSelectAll = false;
12809 if(this.inputEl().dom.selectionEnd > 0){
12810 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12812 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12813 event.preventDefault();
12818 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12820 event.preventDefault();
12821 // this is very hacky as keydown always get's upper case.
12823 var cc = String.fromCharCode(event.getCharCode());
12824 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12828 adjustWidth : function(tag, w){
12829 tag = tag.toLowerCase();
12830 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12831 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12832 if(tag == 'input'){
12835 if(tag == 'textarea'){
12838 }else if(Roo.isOpera){
12839 if(tag == 'input'){
12842 if(tag == 'textarea'){
12850 setFieldLabel : function(v)
12852 if(!this.rendered){
12856 if(this.indicatorEl()){
12857 var ar = this.el.select('label > span',true);
12859 if (ar.elements.length) {
12860 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12861 this.fieldLabel = v;
12865 var br = this.el.select('label',true);
12867 if(br.elements.length) {
12868 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12869 this.fieldLabel = v;
12873 Roo.log('Cannot Found any of label > span || label in input');
12877 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12878 this.fieldLabel = v;
12893 * @class Roo.bootstrap.TextArea
12894 * @extends Roo.bootstrap.Input
12895 * Bootstrap TextArea class
12896 * @cfg {Number} cols Specifies the visible width of a text area
12897 * @cfg {Number} rows Specifies the visible number of lines in a text area
12898 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12899 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12900 * @cfg {string} html text
12903 * Create a new TextArea
12904 * @param {Object} config The config object
12907 Roo.bootstrap.TextArea = function(config){
12908 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12912 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12922 getAutoCreate : function(){
12924 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12930 if(this.inputType != 'hidden'){
12931 cfg.cls = 'form-group' //input-group
12939 value : this.value || '',
12940 html: this.html || '',
12941 cls : 'form-control',
12942 placeholder : this.placeholder || ''
12946 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12947 input.maxLength = this.maxLength;
12951 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12955 input.cols = this.cols;
12958 if (this.readOnly) {
12959 input.readonly = true;
12963 input.name = this.name;
12967 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12971 ['xs','sm','md','lg'].map(function(size){
12972 if (settings[size]) {
12973 cfg.cls += ' col-' + size + '-' + settings[size];
12977 var inputblock = input;
12979 if(this.hasFeedback && !this.allowBlank){
12983 cls: 'glyphicon form-control-feedback'
12987 cls : 'has-feedback',
12996 if (this.before || this.after) {
12999 cls : 'input-group',
13003 inputblock.cn.push({
13005 cls : 'input-group-addon',
13010 inputblock.cn.push(input);
13012 if(this.hasFeedback && !this.allowBlank){
13013 inputblock.cls += ' has-feedback';
13014 inputblock.cn.push(feedback);
13018 inputblock.cn.push({
13020 cls : 'input-group-addon',
13027 if (align ==='left' && this.fieldLabel.length) {
13032 cls : 'control-label',
13033 html : this.fieldLabel
13044 if(this.labelWidth > 12){
13045 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13048 if(this.labelWidth < 13 && this.labelmd == 0){
13049 this.labelmd = this.labelWidth;
13052 if(this.labellg > 0){
13053 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13054 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13057 if(this.labelmd > 0){
13058 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13059 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13062 if(this.labelsm > 0){
13063 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13064 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13067 if(this.labelxs > 0){
13068 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13069 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13072 } else if ( this.fieldLabel.length) {
13077 //cls : 'input-group-addon',
13078 html : this.fieldLabel
13096 if (this.disabled) {
13097 input.disabled=true;
13104 * return the real textarea element.
13106 inputEl: function ()
13108 return this.el.select('textarea.form-control',true).first();
13112 * Clear any invalid styles/messages for this field
13114 clearInvalid : function()
13117 if(!this.el || this.preventMark){ // not rendered
13121 var label = this.el.select('label', true).first();
13122 var icon = this.el.select('i.fa-star', true).first();
13127 this.el.removeClass( this.validClass);
13128 this.inputEl().removeClass('is-invalid');
13130 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13132 var feedback = this.el.select('.form-control-feedback', true).first();
13135 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13140 this.fireEvent('valid', this);
13144 * Mark this field as valid
13146 markValid : function()
13148 if(!this.el || this.preventMark){ // not rendered
13152 this.el.removeClass([this.invalidClass, this.validClass]);
13153 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13155 var feedback = this.el.select('.form-control-feedback', true).first();
13158 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13161 if(this.disabled || this.allowBlank){
13165 var label = this.el.select('label', true).first();
13166 var icon = this.el.select('i.fa-star', true).first();
13171 if (Roo.bootstrap.version == 3) {
13172 this.el.addClass(this.validClass);
13174 this.inputEl().addClass('is-valid');
13178 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13180 var feedback = this.el.select('.form-control-feedback', true).first();
13183 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13184 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13189 this.fireEvent('valid', this);
13193 * Mark this field as invalid
13194 * @param {String} msg The validation message
13196 markInvalid : function(msg)
13198 if(!this.el || this.preventMark){ // not rendered
13202 this.el.removeClass([this.invalidClass, this.validClass]);
13203 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13205 var feedback = this.el.select('.form-control-feedback', true).first();
13208 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13211 if(this.disabled || this.allowBlank){
13215 var label = this.el.select('label', true).first();
13216 var icon = this.el.select('i.fa-star', true).first();
13218 if(!this.getValue().length && label && !icon){
13219 this.el.createChild({
13221 cls : 'text-danger fa fa-lg fa-star',
13222 tooltip : 'This field is required',
13223 style : 'margin-right:5px;'
13227 if (Roo.bootstrap.version == 3) {
13228 this.el.addClass(this.invalidClass);
13230 this.inputEl().addClass('is-invalid');
13233 // fixme ... this may be depricated need to test..
13234 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13236 var feedback = this.el.select('.form-control-feedback', true).first();
13239 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13241 if(this.getValue().length || this.forceFeedback){
13242 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13249 this.fireEvent('invalid', this, msg);
13257 * trigger field - base class for combo..
13262 * @class Roo.bootstrap.TriggerField
13263 * @extends Roo.bootstrap.Input
13264 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13265 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13266 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13267 * for which you can provide a custom implementation. For example:
13269 var trigger = new Roo.bootstrap.TriggerField();
13270 trigger.onTriggerClick = myTriggerFn;
13271 trigger.applyTo('my-field');
13274 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13275 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13276 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13277 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13278 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13281 * Create a new TriggerField.
13282 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13283 * to the base TextField)
13285 Roo.bootstrap.TriggerField = function(config){
13286 this.mimicing = false;
13287 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13290 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13292 * @cfg {String} triggerClass A CSS class to apply to the trigger
13295 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13300 * @cfg {Boolean} removable (true|false) special filter default false
13304 /** @cfg {Boolean} grow @hide */
13305 /** @cfg {Number} growMin @hide */
13306 /** @cfg {Number} growMax @hide */
13312 autoSize: Roo.emptyFn,
13316 deferHeight : true,
13319 actionMode : 'wrap',
13324 getAutoCreate : function(){
13326 var align = this.labelAlign || this.parentLabelAlign();
13331 cls: 'form-group' //input-group
13338 type : this.inputType,
13339 cls : 'form-control',
13340 autocomplete: 'new-password',
13341 placeholder : this.placeholder || ''
13345 input.name = this.name;
13348 input.cls += ' input-' + this.size;
13351 if (this.disabled) {
13352 input.disabled=true;
13355 var inputblock = input;
13357 if(this.hasFeedback && !this.allowBlank){
13361 cls: 'glyphicon form-control-feedback'
13364 if(this.removable && !this.editable ){
13366 cls : 'has-feedback',
13372 cls : 'roo-combo-removable-btn close'
13379 cls : 'has-feedback',
13388 if(this.removable && !this.editable ){
13390 cls : 'roo-removable',
13396 cls : 'roo-combo-removable-btn close'
13403 if (this.before || this.after) {
13406 cls : 'input-group',
13410 inputblock.cn.push({
13412 cls : 'input-group-addon input-group-prepend input-group-text',
13417 inputblock.cn.push(input);
13419 if(this.hasFeedback && !this.allowBlank){
13420 inputblock.cls += ' has-feedback';
13421 inputblock.cn.push(feedback);
13425 inputblock.cn.push({
13427 cls : 'input-group-addon input-group-append input-group-text',
13436 var ibwrap = inputblock;
13441 cls: 'roo-select2-choices',
13445 cls: 'roo-select2-search-field',
13457 cls: 'roo-select2-container input-group',
13462 cls: 'form-hidden-field'
13468 if(!this.multiple && this.showToggleBtn){
13474 if (this.caret != false) {
13477 cls: 'fa fa-' + this.caret
13484 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13486 Roo.bootstrap.version == 3 ? caret : '',
13489 cls: 'combobox-clear',
13503 combobox.cls += ' roo-select2-container-multi';
13507 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13508 tooltip : 'This field is required'
13510 if (Roo.bootstrap.version == 4) {
13513 style : 'display:none'
13518 if (align ==='left' && this.fieldLabel.length) {
13520 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13527 cls : 'control-label',
13528 html : this.fieldLabel
13540 var labelCfg = cfg.cn[1];
13541 var contentCfg = cfg.cn[2];
13543 if(this.indicatorpos == 'right'){
13548 cls : 'control-label',
13552 html : this.fieldLabel
13566 labelCfg = cfg.cn[0];
13567 contentCfg = cfg.cn[1];
13570 if(this.labelWidth > 12){
13571 labelCfg.style = "width: " + this.labelWidth + 'px';
13574 if(this.labelWidth < 13 && this.labelmd == 0){
13575 this.labelmd = this.labelWidth;
13578 if(this.labellg > 0){
13579 labelCfg.cls += ' col-lg-' + this.labellg;
13580 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13583 if(this.labelmd > 0){
13584 labelCfg.cls += ' col-md-' + this.labelmd;
13585 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13588 if(this.labelsm > 0){
13589 labelCfg.cls += ' col-sm-' + this.labelsm;
13590 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13593 if(this.labelxs > 0){
13594 labelCfg.cls += ' col-xs-' + this.labelxs;
13595 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13598 } else if ( this.fieldLabel.length) {
13599 // Roo.log(" label");
13604 //cls : 'input-group-addon',
13605 html : this.fieldLabel
13613 if(this.indicatorpos == 'right'){
13621 html : this.fieldLabel
13635 // Roo.log(" no label && no align");
13642 ['xs','sm','md','lg'].map(function(size){
13643 if (settings[size]) {
13644 cfg.cls += ' col-' + size + '-' + settings[size];
13655 onResize : function(w, h){
13656 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13657 // if(typeof w == 'number'){
13658 // var x = w - this.trigger.getWidth();
13659 // this.inputEl().setWidth(this.adjustWidth('input', x));
13660 // this.trigger.setStyle('left', x+'px');
13665 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13668 getResizeEl : function(){
13669 return this.inputEl();
13673 getPositionEl : function(){
13674 return this.inputEl();
13678 alignErrorIcon : function(){
13679 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13683 initEvents : function(){
13687 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13688 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13689 if(!this.multiple && this.showToggleBtn){
13690 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13691 if(this.hideTrigger){
13692 this.trigger.setDisplayed(false);
13694 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13698 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13701 if(this.removable && !this.editable && !this.tickable){
13702 var close = this.closeTriggerEl();
13705 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13706 close.on('click', this.removeBtnClick, this, close);
13710 //this.trigger.addClassOnOver('x-form-trigger-over');
13711 //this.trigger.addClassOnClick('x-form-trigger-click');
13714 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13718 closeTriggerEl : function()
13720 var close = this.el.select('.roo-combo-removable-btn', true).first();
13721 return close ? close : false;
13724 removeBtnClick : function(e, h, el)
13726 e.preventDefault();
13728 if(this.fireEvent("remove", this) !== false){
13730 this.fireEvent("afterremove", this)
13734 createList : function()
13736 this.list = Roo.get(document.body).createChild({
13737 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13738 cls: 'typeahead typeahead-long dropdown-menu shadow',
13739 style: 'display:none'
13742 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13747 initTrigger : function(){
13752 onDestroy : function(){
13754 this.trigger.removeAllListeners();
13755 // this.trigger.remove();
13758 // this.wrap.remove();
13760 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13764 onFocus : function(){
13765 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13767 if(!this.mimicing){
13768 this.wrap.addClass('x-trigger-wrap-focus');
13769 this.mimicing = true;
13770 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13771 if(this.monitorTab){
13772 this.el.on("keydown", this.checkTab, this);
13779 checkTab : function(e){
13780 if(e.getKey() == e.TAB){
13781 this.triggerBlur();
13786 onBlur : function(){
13791 mimicBlur : function(e, t){
13793 if(!this.wrap.contains(t) && this.validateBlur()){
13794 this.triggerBlur();
13800 triggerBlur : function(){
13801 this.mimicing = false;
13802 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13803 if(this.monitorTab){
13804 this.el.un("keydown", this.checkTab, this);
13806 //this.wrap.removeClass('x-trigger-wrap-focus');
13807 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13811 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13812 validateBlur : function(e, t){
13817 onDisable : function(){
13818 this.inputEl().dom.disabled = true;
13819 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13821 // this.wrap.addClass('x-item-disabled');
13826 onEnable : function(){
13827 this.inputEl().dom.disabled = false;
13828 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13830 // this.el.removeClass('x-item-disabled');
13835 onShow : function(){
13836 var ae = this.getActionEl();
13839 ae.dom.style.display = '';
13840 ae.dom.style.visibility = 'visible';
13846 onHide : function(){
13847 var ae = this.getActionEl();
13848 ae.dom.style.display = 'none';
13852 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13853 * by an implementing function.
13855 * @param {EventObject} e
13857 onTriggerClick : Roo.emptyFn
13865 * @class Roo.bootstrap.CardUploader
13866 * @extends Roo.bootstrap.Button
13867 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13868 * @cfg {Number} errorTimeout default 3000
13869 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13870 * @cfg {Array} html The button text.
13874 * Create a new CardUploader
13875 * @param {Object} config The config object
13878 Roo.bootstrap.CardUploader = function(config){
13882 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13885 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13893 * When a image is clicked on - and needs to display a slideshow or similar..
13894 * @param {Roo.bootstrap.Card} this
13895 * @param {Object} The image information data
13901 * When a the download link is clicked
13902 * @param {Roo.bootstrap.Card} this
13903 * @param {Object} The image information data contains
13910 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13913 errorTimeout : 3000,
13917 fileCollection : false,
13920 getAutoCreate : function()
13924 cls :'form-group' ,
13929 //cls : 'input-group-addon',
13930 html : this.fieldLabel
13938 value : this.value,
13939 cls : 'd-none form-control'
13944 multiple : 'multiple',
13946 cls : 'd-none roo-card-upload-selector'
13950 cls : 'roo-card-uploader-button-container w-100 mb-2'
13953 cls : 'card-columns roo-card-uploader-container'
13963 getChildContainer : function() /// what children are added to.
13965 return this.containerEl;
13968 getButtonContainer : function() /// what children are added to.
13970 return this.el.select(".roo-card-uploader-button-container").first();
13973 initEvents : function()
13976 Roo.bootstrap.Input.prototype.initEvents.call(this);
13980 xns: Roo.bootstrap,
13983 container_method : 'getButtonContainer' ,
13984 html : this.html, // fix changable?
13987 'click' : function(btn, e) {
13996 this.urlAPI = (window.createObjectURL && window) ||
13997 (window.URL && URL.revokeObjectURL && URL) ||
13998 (window.webkitURL && webkitURL);
14003 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14005 this.selectorEl.on('change', this.onFileSelected, this);
14008 this.images.forEach(function(img) {
14011 this.images = false;
14013 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14019 onClick : function(e)
14021 e.preventDefault();
14023 this.selectorEl.dom.click();
14027 onFileSelected : function(e)
14029 e.preventDefault();
14031 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14035 Roo.each(this.selectorEl.dom.files, function(file){
14036 this.addFile(file);
14045 addFile : function(file)
14048 if(typeof(file) === 'string'){
14049 throw "Add file by name?"; // should not happen
14053 if(!file || !this.urlAPI){
14063 var url = _this.urlAPI.createObjectURL( file);
14066 id : Roo.bootstrap.CardUploader.ID--,
14067 is_uploaded : false,
14071 mimetype : file.type,
14079 * addCard - add an Attachment to the uploader
14080 * @param data - the data about the image to upload
14084 title : "Title of file",
14085 is_uploaded : false,
14086 src : "http://.....",
14087 srcfile : { the File upload object },
14088 mimetype : file.type,
14091 .. any other data...
14097 addCard : function (data)
14099 // hidden input element?
14100 // if the file is not an image...
14101 //then we need to use something other that and header_image
14106 xns : Roo.bootstrap,
14107 xtype : 'CardFooter',
14110 xns : Roo.bootstrap,
14116 xns : Roo.bootstrap,
14118 html : String.format("<small>{0}</small>", data.title),
14119 cls : 'col-10 text-left',
14124 click : function() {
14126 t.fireEvent( "download", t, data );
14132 xns : Roo.bootstrap,
14134 style: 'max-height: 28px; ',
14140 click : function() {
14141 t.removeCard(data.id)
14153 var cn = this.addxtype(
14156 xns : Roo.bootstrap,
14159 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14160 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14161 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14166 initEvents : function() {
14167 Roo.bootstrap.Card.prototype.initEvents.call(this);
14169 this.imgEl = this.el.select('.card-img-top').first();
14171 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14172 this.imgEl.set({ 'pointer' : 'cursor' });
14175 this.getCardFooter().addClass('p-1');
14182 // dont' really need ot update items.
14183 // this.items.push(cn);
14184 this.fileCollection.add(cn);
14186 if (!data.srcfile) {
14187 this.updateInput();
14192 var reader = new FileReader();
14193 reader.addEventListener("load", function() {
14194 data.srcdata = reader.result;
14197 reader.readAsDataURL(data.srcfile);
14202 removeCard : function(id)
14205 var card = this.fileCollection.get(id);
14206 card.data.is_deleted = 1;
14207 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14208 //this.fileCollection.remove(card);
14209 //this.items = this.items.filter(function(e) { return e != card });
14210 // dont' really need ot update items.
14211 card.el.dom.parentNode.removeChild(card.el.dom);
14212 this.updateInput();
14218 this.fileCollection.each(function(card) {
14219 if (card.el.dom && card.el.dom.parentNode) {
14220 card.el.dom.parentNode.removeChild(card.el.dom);
14223 this.fileCollection.clear();
14224 this.updateInput();
14227 updateInput : function()
14230 this.fileCollection.each(function(e) {
14234 this.inputEl().dom.value = JSON.stringify(data);
14244 Roo.bootstrap.CardUploader.ID = -1;/*
14246 * Ext JS Library 1.1.1
14247 * Copyright(c) 2006-2007, Ext JS, LLC.
14249 * Originally Released Under LGPL - original licence link has changed is not relivant.
14252 * <script type="text/javascript">
14257 * @class Roo.data.SortTypes
14259 * Defines the default sorting (casting?) comparison functions used when sorting data.
14261 Roo.data.SortTypes = {
14263 * Default sort that does nothing
14264 * @param {Mixed} s The value being converted
14265 * @return {Mixed} The comparison value
14267 none : function(s){
14272 * The regular expression used to strip tags
14276 stripTagsRE : /<\/?[^>]+>/gi,
14279 * Strips all HTML tags to sort on text only
14280 * @param {Mixed} s The value being converted
14281 * @return {String} The comparison value
14283 asText : function(s){
14284 return String(s).replace(this.stripTagsRE, "");
14288 * Strips all HTML tags to sort on text only - Case insensitive
14289 * @param {Mixed} s The value being converted
14290 * @return {String} The comparison value
14292 asUCText : function(s){
14293 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14297 * Case insensitive string
14298 * @param {Mixed} s The value being converted
14299 * @return {String} The comparison value
14301 asUCString : function(s) {
14302 return String(s).toUpperCase();
14307 * @param {Mixed} s The value being converted
14308 * @return {Number} The comparison value
14310 asDate : function(s) {
14314 if(s instanceof Date){
14315 return s.getTime();
14317 return Date.parse(String(s));
14322 * @param {Mixed} s The value being converted
14323 * @return {Float} The comparison value
14325 asFloat : function(s) {
14326 var val = parseFloat(String(s).replace(/,/g, ""));
14335 * @param {Mixed} s The value being converted
14336 * @return {Number} The comparison value
14338 asInt : function(s) {
14339 var val = parseInt(String(s).replace(/,/g, ""));
14347 * Ext JS Library 1.1.1
14348 * Copyright(c) 2006-2007, Ext JS, LLC.
14350 * Originally Released Under LGPL - original licence link has changed is not relivant.
14353 * <script type="text/javascript">
14357 * @class Roo.data.Record
14358 * Instances of this class encapsulate both record <em>definition</em> information, and record
14359 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14360 * to access Records cached in an {@link Roo.data.Store} object.<br>
14362 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14363 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14366 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14368 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14369 * {@link #create}. The parameters are the same.
14370 * @param {Array} data An associative Array of data values keyed by the field name.
14371 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14372 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14373 * not specified an integer id is generated.
14375 Roo.data.Record = function(data, id){
14376 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14381 * Generate a constructor for a specific record layout.
14382 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14383 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14384 * Each field definition object may contain the following properties: <ul>
14385 * <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,
14386 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14387 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14388 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14389 * is being used, then this is a string containing the javascript expression to reference the data relative to
14390 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14391 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14392 * this may be omitted.</p></li>
14393 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14394 * <ul><li>auto (Default, implies no conversion)</li>
14399 * <li>date</li></ul></p></li>
14400 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14401 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14402 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14403 * by the Reader into an object that will be stored in the Record. It is passed the
14404 * following parameters:<ul>
14405 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14407 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14409 * <br>usage:<br><pre><code>
14410 var TopicRecord = Roo.data.Record.create(
14411 {name: 'title', mapping: 'topic_title'},
14412 {name: 'author', mapping: 'username'},
14413 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14414 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14415 {name: 'lastPoster', mapping: 'user2'},
14416 {name: 'excerpt', mapping: 'post_text'}
14419 var myNewRecord = new TopicRecord({
14420 title: 'Do my job please',
14423 lastPost: new Date(),
14424 lastPoster: 'Animal',
14425 excerpt: 'No way dude!'
14427 myStore.add(myNewRecord);
14432 Roo.data.Record.create = function(o){
14433 var f = function(){
14434 f.superclass.constructor.apply(this, arguments);
14436 Roo.extend(f, Roo.data.Record);
14437 var p = f.prototype;
14438 p.fields = new Roo.util.MixedCollection(false, function(field){
14441 for(var i = 0, len = o.length; i < len; i++){
14442 p.fields.add(new Roo.data.Field(o[i]));
14444 f.getField = function(name){
14445 return p.fields.get(name);
14450 Roo.data.Record.AUTO_ID = 1000;
14451 Roo.data.Record.EDIT = 'edit';
14452 Roo.data.Record.REJECT = 'reject';
14453 Roo.data.Record.COMMIT = 'commit';
14455 Roo.data.Record.prototype = {
14457 * Readonly flag - true if this record has been modified.
14466 join : function(store){
14467 this.store = store;
14471 * Set the named field to the specified value.
14472 * @param {String} name The name of the field to set.
14473 * @param {Object} value The value to set the field to.
14475 set : function(name, value){
14476 if(this.data[name] == value){
14480 if(!this.modified){
14481 this.modified = {};
14483 if(typeof this.modified[name] == 'undefined'){
14484 this.modified[name] = this.data[name];
14486 this.data[name] = value;
14487 if(!this.editing && this.store){
14488 this.store.afterEdit(this);
14493 * Get the value of the named field.
14494 * @param {String} name The name of the field to get the value of.
14495 * @return {Object} The value of the field.
14497 get : function(name){
14498 return this.data[name];
14502 beginEdit : function(){
14503 this.editing = true;
14504 this.modified = {};
14508 cancelEdit : function(){
14509 this.editing = false;
14510 delete this.modified;
14514 endEdit : function(){
14515 this.editing = false;
14516 if(this.dirty && this.store){
14517 this.store.afterEdit(this);
14522 * Usually called by the {@link Roo.data.Store} which owns the Record.
14523 * Rejects all changes made to the Record since either creation, or the last commit operation.
14524 * Modified fields are reverted to their original values.
14526 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14527 * of reject operations.
14529 reject : function(){
14530 var m = this.modified;
14532 if(typeof m[n] != "function"){
14533 this.data[n] = m[n];
14536 this.dirty = false;
14537 delete this.modified;
14538 this.editing = false;
14540 this.store.afterReject(this);
14545 * Usually called by the {@link Roo.data.Store} which owns the Record.
14546 * Commits all changes made to the Record since either creation, or the last commit operation.
14548 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14549 * of commit operations.
14551 commit : function(){
14552 this.dirty = false;
14553 delete this.modified;
14554 this.editing = false;
14556 this.store.afterCommit(this);
14561 hasError : function(){
14562 return this.error != null;
14566 clearError : function(){
14571 * Creates a copy of this record.
14572 * @param {String} id (optional) A new record id if you don't want to use this record's id
14575 copy : function(newId) {
14576 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14580 * Ext JS Library 1.1.1
14581 * Copyright(c) 2006-2007, Ext JS, LLC.
14583 * Originally Released Under LGPL - original licence link has changed is not relivant.
14586 * <script type="text/javascript">
14592 * @class Roo.data.Store
14593 * @extends Roo.util.Observable
14594 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14595 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14597 * 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
14598 * has no knowledge of the format of the data returned by the Proxy.<br>
14600 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14601 * instances from the data object. These records are cached and made available through accessor functions.
14603 * Creates a new Store.
14604 * @param {Object} config A config object containing the objects needed for the Store to access data,
14605 * and read the data into Records.
14607 Roo.data.Store = function(config){
14608 this.data = new Roo.util.MixedCollection(false);
14609 this.data.getKey = function(o){
14612 this.baseParams = {};
14614 this.paramNames = {
14619 "multisort" : "_multisort"
14622 if(config && config.data){
14623 this.inlineData = config.data;
14624 delete config.data;
14627 Roo.apply(this, config);
14629 if(this.reader){ // reader passed
14630 this.reader = Roo.factory(this.reader, Roo.data);
14631 this.reader.xmodule = this.xmodule || false;
14632 if(!this.recordType){
14633 this.recordType = this.reader.recordType;
14635 if(this.reader.onMetaChange){
14636 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14640 if(this.recordType){
14641 this.fields = this.recordType.prototype.fields;
14643 this.modified = [];
14647 * @event datachanged
14648 * Fires when the data cache has changed, and a widget which is using this Store
14649 * as a Record cache should refresh its view.
14650 * @param {Store} this
14652 datachanged : true,
14654 * @event metachange
14655 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14656 * @param {Store} this
14657 * @param {Object} meta The JSON metadata
14662 * Fires when Records have been added to the Store
14663 * @param {Store} this
14664 * @param {Roo.data.Record[]} records The array of Records added
14665 * @param {Number} index The index at which the record(s) were added
14670 * Fires when a Record has been removed from the Store
14671 * @param {Store} this
14672 * @param {Roo.data.Record} record The Record that was removed
14673 * @param {Number} index The index at which the record was removed
14678 * Fires when a Record has been updated
14679 * @param {Store} this
14680 * @param {Roo.data.Record} record The Record that was updated
14681 * @param {String} operation The update operation being performed. Value may be one of:
14683 Roo.data.Record.EDIT
14684 Roo.data.Record.REJECT
14685 Roo.data.Record.COMMIT
14691 * Fires when the data cache has been cleared.
14692 * @param {Store} this
14696 * @event beforeload
14697 * Fires before a request is made for a new data object. If the beforeload handler returns false
14698 * the load action will be canceled.
14699 * @param {Store} this
14700 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14704 * @event beforeloadadd
14705 * Fires after a new set of Records has been loaded.
14706 * @param {Store} this
14707 * @param {Roo.data.Record[]} records The Records that were loaded
14708 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14710 beforeloadadd : true,
14713 * Fires after a new set of Records has been loaded, before they are added to the store.
14714 * @param {Store} this
14715 * @param {Roo.data.Record[]} records The Records that were loaded
14716 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14717 * @params {Object} return from reader
14721 * @event loadexception
14722 * Fires if an exception occurs in the Proxy during loading.
14723 * Called with the signature of the Proxy's "loadexception" event.
14724 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14727 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14728 * @param {Object} load options
14729 * @param {Object} jsonData from your request (normally this contains the Exception)
14731 loadexception : true
14735 this.proxy = Roo.factory(this.proxy, Roo.data);
14736 this.proxy.xmodule = this.xmodule || false;
14737 this.relayEvents(this.proxy, ["loadexception"]);
14739 this.sortToggle = {};
14740 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14742 Roo.data.Store.superclass.constructor.call(this);
14744 if(this.inlineData){
14745 this.loadData(this.inlineData);
14746 delete this.inlineData;
14750 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14752 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14753 * without a remote query - used by combo/forms at present.
14757 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14760 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14763 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14764 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14767 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14768 * on any HTTP request
14771 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14774 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14778 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14779 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14781 remoteSort : false,
14784 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14785 * loaded or when a record is removed. (defaults to false).
14787 pruneModifiedRecords : false,
14790 lastOptions : null,
14793 * Add Records to the Store and fires the add event.
14794 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14796 add : function(records){
14797 records = [].concat(records);
14798 for(var i = 0, len = records.length; i < len; i++){
14799 records[i].join(this);
14801 var index = this.data.length;
14802 this.data.addAll(records);
14803 this.fireEvent("add", this, records, index);
14807 * Remove a Record from the Store and fires the remove event.
14808 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14810 remove : function(record){
14811 var index = this.data.indexOf(record);
14812 this.data.removeAt(index);
14814 if(this.pruneModifiedRecords){
14815 this.modified.remove(record);
14817 this.fireEvent("remove", this, record, index);
14821 * Remove all Records from the Store and fires the clear event.
14823 removeAll : function(){
14825 if(this.pruneModifiedRecords){
14826 this.modified = [];
14828 this.fireEvent("clear", this);
14832 * Inserts Records to the Store at the given index and fires the add event.
14833 * @param {Number} index The start index at which to insert the passed Records.
14834 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14836 insert : function(index, records){
14837 records = [].concat(records);
14838 for(var i = 0, len = records.length; i < len; i++){
14839 this.data.insert(index, records[i]);
14840 records[i].join(this);
14842 this.fireEvent("add", this, records, index);
14846 * Get the index within the cache of the passed Record.
14847 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14848 * @return {Number} The index of the passed Record. Returns -1 if not found.
14850 indexOf : function(record){
14851 return this.data.indexOf(record);
14855 * Get the index within the cache of the Record with the passed id.
14856 * @param {String} id The id of the Record to find.
14857 * @return {Number} The index of the Record. Returns -1 if not found.
14859 indexOfId : function(id){
14860 return this.data.indexOfKey(id);
14864 * Get the Record with the specified id.
14865 * @param {String} id The id of the Record to find.
14866 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14868 getById : function(id){
14869 return this.data.key(id);
14873 * Get the Record at the specified index.
14874 * @param {Number} index The index of the Record to find.
14875 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14877 getAt : function(index){
14878 return this.data.itemAt(index);
14882 * Returns a range of Records between specified indices.
14883 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14884 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14885 * @return {Roo.data.Record[]} An array of Records
14887 getRange : function(start, end){
14888 return this.data.getRange(start, end);
14892 storeOptions : function(o){
14893 o = Roo.apply({}, o);
14896 this.lastOptions = o;
14900 * Loads the Record cache from the configured Proxy using the configured Reader.
14902 * If using remote paging, then the first load call must specify the <em>start</em>
14903 * and <em>limit</em> properties in the options.params property to establish the initial
14904 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14906 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14907 * and this call will return before the new data has been loaded. Perform any post-processing
14908 * in a callback function, or in a "load" event handler.</strong>
14910 * @param {Object} options An object containing properties which control loading options:<ul>
14911 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14912 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14913 * passed the following arguments:<ul>
14914 * <li>r : Roo.data.Record[]</li>
14915 * <li>options: Options object from the load call</li>
14916 * <li>success: Boolean success indicator</li></ul></li>
14917 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14918 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14921 load : function(options){
14922 options = options || {};
14923 if(this.fireEvent("beforeload", this, options) !== false){
14924 this.storeOptions(options);
14925 var p = Roo.apply(options.params || {}, this.baseParams);
14926 // if meta was not loaded from remote source.. try requesting it.
14927 if (!this.reader.metaFromRemote) {
14928 p._requestMeta = 1;
14930 if(this.sortInfo && this.remoteSort){
14931 var pn = this.paramNames;
14932 p[pn["sort"]] = this.sortInfo.field;
14933 p[pn["dir"]] = this.sortInfo.direction;
14935 if (this.multiSort) {
14936 var pn = this.paramNames;
14937 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14940 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14945 * Reloads the Record cache from the configured Proxy using the configured Reader and
14946 * the options from the last load operation performed.
14947 * @param {Object} options (optional) An object containing properties which may override the options
14948 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14949 * the most recently used options are reused).
14951 reload : function(options){
14952 this.load(Roo.applyIf(options||{}, this.lastOptions));
14956 // Called as a callback by the Reader during a load operation.
14957 loadRecords : function(o, options, success){
14958 if(!o || success === false){
14959 if(success !== false){
14960 this.fireEvent("load", this, [], options, o);
14962 if(options.callback){
14963 options.callback.call(options.scope || this, [], options, false);
14967 // if data returned failure - throw an exception.
14968 if (o.success === false) {
14969 // show a message if no listener is registered.
14970 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14971 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14973 // loadmask wil be hooked into this..
14974 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14977 var r = o.records, t = o.totalRecords || r.length;
14979 this.fireEvent("beforeloadadd", this, r, options, o);
14981 if(!options || options.add !== true){
14982 if(this.pruneModifiedRecords){
14983 this.modified = [];
14985 for(var i = 0, len = r.length; i < len; i++){
14989 this.data = this.snapshot;
14990 delete this.snapshot;
14993 this.data.addAll(r);
14994 this.totalLength = t;
14996 this.fireEvent("datachanged", this);
14998 this.totalLength = Math.max(t, this.data.length+r.length);
15002 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15004 var e = new Roo.data.Record({});
15006 e.set(this.parent.displayField, this.parent.emptyTitle);
15007 e.set(this.parent.valueField, '');
15012 this.fireEvent("load", this, r, options, o);
15013 if(options.callback){
15014 options.callback.call(options.scope || this, r, options, true);
15020 * Loads data from a passed data block. A Reader which understands the format of the data
15021 * must have been configured in the constructor.
15022 * @param {Object} data The data block from which to read the Records. The format of the data expected
15023 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15024 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15026 loadData : function(o, append){
15027 var r = this.reader.readRecords(o);
15028 this.loadRecords(r, {add: append}, true);
15032 * using 'cn' the nested child reader read the child array into it's child stores.
15033 * @param {Object} rec The record with a 'children array
15035 loadDataFromChildren : function(rec)
15037 this.loadData(this.reader.toLoadData(rec));
15042 * Gets the number of cached records.
15044 * <em>If using paging, this may not be the total size of the dataset. If the data object
15045 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15046 * the data set size</em>
15048 getCount : function(){
15049 return this.data.length || 0;
15053 * Gets the total number of records in the dataset as returned by the server.
15055 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15056 * the dataset size</em>
15058 getTotalCount : function(){
15059 return this.totalLength || 0;
15063 * Returns the sort state of the Store as an object with two properties:
15065 field {String} The name of the field by which the Records are sorted
15066 direction {String} The sort order, "ASC" or "DESC"
15069 getSortState : function(){
15070 return this.sortInfo;
15074 applySort : function(){
15075 if(this.sortInfo && !this.remoteSort){
15076 var s = this.sortInfo, f = s.field;
15077 var st = this.fields.get(f).sortType;
15078 var fn = function(r1, r2){
15079 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15080 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15082 this.data.sort(s.direction, fn);
15083 if(this.snapshot && this.snapshot != this.data){
15084 this.snapshot.sort(s.direction, fn);
15090 * Sets the default sort column and order to be used by the next load operation.
15091 * @param {String} fieldName The name of the field to sort by.
15092 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15094 setDefaultSort : function(field, dir){
15095 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15099 * Sort the Records.
15100 * If remote sorting is used, the sort is performed on the server, and the cache is
15101 * reloaded. If local sorting is used, the cache is sorted internally.
15102 * @param {String} fieldName The name of the field to sort by.
15103 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15105 sort : function(fieldName, dir){
15106 var f = this.fields.get(fieldName);
15108 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15110 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15111 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15116 this.sortToggle[f.name] = dir;
15117 this.sortInfo = {field: f.name, direction: dir};
15118 if(!this.remoteSort){
15120 this.fireEvent("datachanged", this);
15122 this.load(this.lastOptions);
15127 * Calls the specified function for each of the Records in the cache.
15128 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15129 * Returning <em>false</em> aborts and exits the iteration.
15130 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15132 each : function(fn, scope){
15133 this.data.each(fn, scope);
15137 * Gets all records modified since the last commit. Modified records are persisted across load operations
15138 * (e.g., during paging).
15139 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15141 getModifiedRecords : function(){
15142 return this.modified;
15146 createFilterFn : function(property, value, anyMatch){
15147 if(!value.exec){ // not a regex
15148 value = String(value);
15149 if(value.length == 0){
15152 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15154 return function(r){
15155 return value.test(r.data[property]);
15160 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15161 * @param {String} property A field on your records
15162 * @param {Number} start The record index to start at (defaults to 0)
15163 * @param {Number} end The last record index to include (defaults to length - 1)
15164 * @return {Number} The sum
15166 sum : function(property, start, end){
15167 var rs = this.data.items, v = 0;
15168 start = start || 0;
15169 end = (end || end === 0) ? end : rs.length-1;
15171 for(var i = start; i <= end; i++){
15172 v += (rs[i].data[property] || 0);
15178 * Filter the records by a specified property.
15179 * @param {String} field A field on your records
15180 * @param {String/RegExp} value Either a string that the field
15181 * should start with or a RegExp to test against the field
15182 * @param {Boolean} anyMatch True to match any part not just the beginning
15184 filter : function(property, value, anyMatch){
15185 var fn = this.createFilterFn(property, value, anyMatch);
15186 return fn ? this.filterBy(fn) : this.clearFilter();
15190 * Filter by a function. The specified function will be called with each
15191 * record in this data source. If the function returns true the record is included,
15192 * otherwise it is filtered.
15193 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15194 * @param {Object} scope (optional) The scope of the function (defaults to this)
15196 filterBy : function(fn, scope){
15197 this.snapshot = this.snapshot || this.data;
15198 this.data = this.queryBy(fn, scope||this);
15199 this.fireEvent("datachanged", this);
15203 * Query the records by a specified property.
15204 * @param {String} field A field on your records
15205 * @param {String/RegExp} value Either a string that the field
15206 * should start with or a RegExp to test against the field
15207 * @param {Boolean} anyMatch True to match any part not just the beginning
15208 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15210 query : function(property, value, anyMatch){
15211 var fn = this.createFilterFn(property, value, anyMatch);
15212 return fn ? this.queryBy(fn) : this.data.clone();
15216 * Query by a function. The specified function will be called with each
15217 * record in this data source. If the function returns true the record is included
15219 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15220 * @param {Object} scope (optional) The scope of the function (defaults to this)
15221 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15223 queryBy : function(fn, scope){
15224 var data = this.snapshot || this.data;
15225 return data.filterBy(fn, scope||this);
15229 * Collects unique values for a particular dataIndex from this store.
15230 * @param {String} dataIndex The property to collect
15231 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15232 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15233 * @return {Array} An array of the unique values
15235 collect : function(dataIndex, allowNull, bypassFilter){
15236 var d = (bypassFilter === true && this.snapshot) ?
15237 this.snapshot.items : this.data.items;
15238 var v, sv, r = [], l = {};
15239 for(var i = 0, len = d.length; i < len; i++){
15240 v = d[i].data[dataIndex];
15242 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15251 * Revert to a view of the Record cache with no filtering applied.
15252 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15254 clearFilter : function(suppressEvent){
15255 if(this.snapshot && this.snapshot != this.data){
15256 this.data = this.snapshot;
15257 delete this.snapshot;
15258 if(suppressEvent !== true){
15259 this.fireEvent("datachanged", this);
15265 afterEdit : function(record){
15266 if(this.modified.indexOf(record) == -1){
15267 this.modified.push(record);
15269 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15273 afterReject : function(record){
15274 this.modified.remove(record);
15275 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15279 afterCommit : function(record){
15280 this.modified.remove(record);
15281 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15285 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15286 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15288 commitChanges : function(){
15289 var m = this.modified.slice(0);
15290 this.modified = [];
15291 for(var i = 0, len = m.length; i < len; i++){
15297 * Cancel outstanding changes on all changed records.
15299 rejectChanges : function(){
15300 var m = this.modified.slice(0);
15301 this.modified = [];
15302 for(var i = 0, len = m.length; i < len; i++){
15307 onMetaChange : function(meta, rtype, o){
15308 this.recordType = rtype;
15309 this.fields = rtype.prototype.fields;
15310 delete this.snapshot;
15311 this.sortInfo = meta.sortInfo || this.sortInfo;
15312 this.modified = [];
15313 this.fireEvent('metachange', this, this.reader.meta);
15316 moveIndex : function(data, type)
15318 var index = this.indexOf(data);
15320 var newIndex = index + type;
15324 this.insert(newIndex, data);
15329 * Ext JS Library 1.1.1
15330 * Copyright(c) 2006-2007, Ext JS, LLC.
15332 * Originally Released Under LGPL - original licence link has changed is not relivant.
15335 * <script type="text/javascript">
15339 * @class Roo.data.SimpleStore
15340 * @extends Roo.data.Store
15341 * Small helper class to make creating Stores from Array data easier.
15342 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15343 * @cfg {Array} fields An array of field definition objects, or field name strings.
15344 * @cfg {Object} an existing reader (eg. copied from another store)
15345 * @cfg {Array} data The multi-dimensional array of data
15347 * @param {Object} config
15349 Roo.data.SimpleStore = function(config)
15351 Roo.data.SimpleStore.superclass.constructor.call(this, {
15353 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15356 Roo.data.Record.create(config.fields)
15358 proxy : new Roo.data.MemoryProxy(config.data)
15362 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15364 * Ext JS Library 1.1.1
15365 * Copyright(c) 2006-2007, Ext JS, LLC.
15367 * Originally Released Under LGPL - original licence link has changed is not relivant.
15370 * <script type="text/javascript">
15375 * @extends Roo.data.Store
15376 * @class Roo.data.JsonStore
15377 * Small helper class to make creating Stores for JSON data easier. <br/>
15379 var store = new Roo.data.JsonStore({
15380 url: 'get-images.php',
15382 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15385 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15386 * JsonReader and HttpProxy (unless inline data is provided).</b>
15387 * @cfg {Array} fields An array of field definition objects, or field name strings.
15389 * @param {Object} config
15391 Roo.data.JsonStore = function(c){
15392 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15393 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15394 reader: new Roo.data.JsonReader(c, c.fields)
15397 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15399 * Ext JS Library 1.1.1
15400 * Copyright(c) 2006-2007, Ext JS, LLC.
15402 * Originally Released Under LGPL - original licence link has changed is not relivant.
15405 * <script type="text/javascript">
15409 Roo.data.Field = function(config){
15410 if(typeof config == "string"){
15411 config = {name: config};
15413 Roo.apply(this, config);
15416 this.type = "auto";
15419 var st = Roo.data.SortTypes;
15420 // named sortTypes are supported, here we look them up
15421 if(typeof this.sortType == "string"){
15422 this.sortType = st[this.sortType];
15425 // set default sortType for strings and dates
15426 if(!this.sortType){
15429 this.sortType = st.asUCString;
15432 this.sortType = st.asDate;
15435 this.sortType = st.none;
15440 var stripRe = /[\$,%]/g;
15442 // prebuilt conversion function for this field, instead of
15443 // switching every time we're reading a value
15445 var cv, dateFormat = this.dateFormat;
15450 cv = function(v){ return v; };
15453 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15457 return v !== undefined && v !== null && v !== '' ?
15458 parseInt(String(v).replace(stripRe, ""), 10) : '';
15463 return v !== undefined && v !== null && v !== '' ?
15464 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15469 cv = function(v){ return v === true || v === "true" || v == 1; };
15476 if(v instanceof Date){
15480 if(dateFormat == "timestamp"){
15481 return new Date(v*1000);
15483 return Date.parseDate(v, dateFormat);
15485 var parsed = Date.parse(v);
15486 return parsed ? new Date(parsed) : null;
15495 Roo.data.Field.prototype = {
15503 * Ext JS Library 1.1.1
15504 * Copyright(c) 2006-2007, Ext JS, LLC.
15506 * Originally Released Under LGPL - original licence link has changed is not relivant.
15509 * <script type="text/javascript">
15512 // Base class for reading structured data from a data source. This class is intended to be
15513 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15516 * @class Roo.data.DataReader
15517 * Base class for reading structured data from a data source. This class is intended to be
15518 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15521 Roo.data.DataReader = function(meta, recordType){
15525 this.recordType = recordType instanceof Array ?
15526 Roo.data.Record.create(recordType) : recordType;
15529 Roo.data.DataReader.prototype = {
15532 readerType : 'Data',
15534 * Create an empty record
15535 * @param {Object} data (optional) - overlay some values
15536 * @return {Roo.data.Record} record created.
15538 newRow : function(d) {
15540 this.recordType.prototype.fields.each(function(c) {
15542 case 'int' : da[c.name] = 0; break;
15543 case 'date' : da[c.name] = new Date(); break;
15544 case 'float' : da[c.name] = 0.0; break;
15545 case 'boolean' : da[c.name] = false; break;
15546 default : da[c.name] = ""; break;
15550 return new this.recordType(Roo.apply(da, d));
15556 * Ext JS Library 1.1.1
15557 * Copyright(c) 2006-2007, Ext JS, LLC.
15559 * Originally Released Under LGPL - original licence link has changed is not relivant.
15562 * <script type="text/javascript">
15566 * @class Roo.data.DataProxy
15567 * @extends Roo.data.Observable
15568 * This class is an abstract base class for implementations which provide retrieval of
15569 * unformatted data objects.<br>
15571 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15572 * (of the appropriate type which knows how to parse the data object) to provide a block of
15573 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15575 * Custom implementations must implement the load method as described in
15576 * {@link Roo.data.HttpProxy#load}.
15578 Roo.data.DataProxy = function(){
15581 * @event beforeload
15582 * Fires before a network request is made to retrieve a data object.
15583 * @param {Object} This DataProxy object.
15584 * @param {Object} params The params parameter to the load function.
15589 * Fires before the load method's callback is called.
15590 * @param {Object} This DataProxy object.
15591 * @param {Object} o The data object.
15592 * @param {Object} arg The callback argument object passed to the load function.
15596 * @event loadexception
15597 * Fires if an Exception occurs during data retrieval.
15598 * @param {Object} This DataProxy object.
15599 * @param {Object} o The data object.
15600 * @param {Object} arg The callback argument object passed to the load function.
15601 * @param {Object} e The Exception.
15603 loadexception : true
15605 Roo.data.DataProxy.superclass.constructor.call(this);
15608 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15611 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15615 * Ext JS Library 1.1.1
15616 * Copyright(c) 2006-2007, Ext JS, LLC.
15618 * Originally Released Under LGPL - original licence link has changed is not relivant.
15621 * <script type="text/javascript">
15624 * @class Roo.data.MemoryProxy
15625 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15626 * to the Reader when its load method is called.
15628 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15630 Roo.data.MemoryProxy = function(data){
15634 Roo.data.MemoryProxy.superclass.constructor.call(this);
15638 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15641 * Load data from the requested source (in this case an in-memory
15642 * data object passed to the constructor), read the data object into
15643 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15644 * process that block using the passed callback.
15645 * @param {Object} params This parameter is not used by the MemoryProxy class.
15646 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15647 * object into a block of Roo.data.Records.
15648 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15649 * The function must be passed <ul>
15650 * <li>The Record block object</li>
15651 * <li>The "arg" argument from the load function</li>
15652 * <li>A boolean success indicator</li>
15654 * @param {Object} scope The scope in which to call the callback
15655 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15657 load : function(params, reader, callback, scope, arg){
15658 params = params || {};
15661 result = reader.readRecords(params.data ? params.data :this.data);
15663 this.fireEvent("loadexception", this, arg, null, e);
15664 callback.call(scope, null, arg, false);
15667 callback.call(scope, result, arg, true);
15671 update : function(params, records){
15676 * Ext JS Library 1.1.1
15677 * Copyright(c) 2006-2007, Ext JS, LLC.
15679 * Originally Released Under LGPL - original licence link has changed is not relivant.
15682 * <script type="text/javascript">
15685 * @class Roo.data.HttpProxy
15686 * @extends Roo.data.DataProxy
15687 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15688 * configured to reference a certain URL.<br><br>
15690 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15691 * from which the running page was served.<br><br>
15693 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15695 * Be aware that to enable the browser to parse an XML document, the server must set
15696 * the Content-Type header in the HTTP response to "text/xml".
15698 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15699 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15700 * will be used to make the request.
15702 Roo.data.HttpProxy = function(conn){
15703 Roo.data.HttpProxy.superclass.constructor.call(this);
15704 // is conn a conn config or a real conn?
15706 this.useAjax = !conn || !conn.events;
15710 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15711 // thse are take from connection...
15714 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15717 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15718 * extra parameters to each request made by this object. (defaults to undefined)
15721 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15722 * to each request made by this object. (defaults to undefined)
15725 * @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)
15728 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15731 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15737 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15741 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15742 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15743 * a finer-grained basis than the DataProxy events.
15745 getConnection : function(){
15746 return this.useAjax ? Roo.Ajax : this.conn;
15750 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15751 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15752 * process that block using the passed callback.
15753 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15754 * for the request to the remote server.
15755 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15756 * object into a block of Roo.data.Records.
15757 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15758 * The function must be passed <ul>
15759 * <li>The Record block object</li>
15760 * <li>The "arg" argument from the load function</li>
15761 * <li>A boolean success indicator</li>
15763 * @param {Object} scope The scope in which to call the callback
15764 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15766 load : function(params, reader, callback, scope, arg){
15767 if(this.fireEvent("beforeload", this, params) !== false){
15769 params : params || {},
15771 callback : callback,
15776 callback : this.loadResponse,
15780 Roo.applyIf(o, this.conn);
15781 if(this.activeRequest){
15782 Roo.Ajax.abort(this.activeRequest);
15784 this.activeRequest = Roo.Ajax.request(o);
15786 this.conn.request(o);
15789 callback.call(scope||this, null, arg, false);
15794 loadResponse : function(o, success, response){
15795 delete this.activeRequest;
15797 this.fireEvent("loadexception", this, o, response);
15798 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15803 result = o.reader.read(response);
15805 this.fireEvent("loadexception", this, o, response, e);
15806 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15810 this.fireEvent("load", this, o, o.request.arg);
15811 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15815 update : function(dataSet){
15820 updateResponse : function(dataSet){
15825 * Ext JS Library 1.1.1
15826 * Copyright(c) 2006-2007, Ext JS, LLC.
15828 * Originally Released Under LGPL - original licence link has changed is not relivant.
15831 * <script type="text/javascript">
15835 * @class Roo.data.ScriptTagProxy
15836 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15837 * other than the originating domain of the running page.<br><br>
15839 * <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
15840 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15842 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15843 * source code that is used as the source inside a <script> tag.<br><br>
15845 * In order for the browser to process the returned data, the server must wrap the data object
15846 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15847 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15848 * depending on whether the callback name was passed:
15851 boolean scriptTag = false;
15852 String cb = request.getParameter("callback");
15855 response.setContentType("text/javascript");
15857 response.setContentType("application/x-json");
15859 Writer out = response.getWriter();
15861 out.write(cb + "(");
15863 out.print(dataBlock.toJsonString());
15870 * @param {Object} config A configuration object.
15872 Roo.data.ScriptTagProxy = function(config){
15873 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15874 Roo.apply(this, config);
15875 this.head = document.getElementsByTagName("head")[0];
15878 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15880 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15882 * @cfg {String} url The URL from which to request the data object.
15885 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15889 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15890 * the server the name of the callback function set up by the load call to process the returned data object.
15891 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15892 * javascript output which calls this named function passing the data object as its only parameter.
15894 callbackParam : "callback",
15896 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15897 * name to the request.
15902 * Load data from the configured URL, read the data object into
15903 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15904 * process that block using the passed callback.
15905 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15906 * for the request to the remote server.
15907 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15908 * object into a block of Roo.data.Records.
15909 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15910 * The function must be passed <ul>
15911 * <li>The Record block object</li>
15912 * <li>The "arg" argument from the load function</li>
15913 * <li>A boolean success indicator</li>
15915 * @param {Object} scope The scope in which to call the callback
15916 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15918 load : function(params, reader, callback, scope, arg){
15919 if(this.fireEvent("beforeload", this, params) !== false){
15921 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15923 var url = this.url;
15924 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15926 url += "&_dc=" + (new Date().getTime());
15928 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15931 cb : "stcCallback"+transId,
15932 scriptId : "stcScript"+transId,
15936 callback : callback,
15942 window[trans.cb] = function(o){
15943 conn.handleResponse(o, trans);
15946 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15948 if(this.autoAbort !== false){
15952 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15954 var script = document.createElement("script");
15955 script.setAttribute("src", url);
15956 script.setAttribute("type", "text/javascript");
15957 script.setAttribute("id", trans.scriptId);
15958 this.head.appendChild(script);
15960 this.trans = trans;
15962 callback.call(scope||this, null, arg, false);
15967 isLoading : function(){
15968 return this.trans ? true : false;
15972 * Abort the current server request.
15974 abort : function(){
15975 if(this.isLoading()){
15976 this.destroyTrans(this.trans);
15981 destroyTrans : function(trans, isLoaded){
15982 this.head.removeChild(document.getElementById(trans.scriptId));
15983 clearTimeout(trans.timeoutId);
15985 window[trans.cb] = undefined;
15987 delete window[trans.cb];
15990 // if hasn't been loaded, wait for load to remove it to prevent script error
15991 window[trans.cb] = function(){
15992 window[trans.cb] = undefined;
15994 delete window[trans.cb];
16001 handleResponse : function(o, trans){
16002 this.trans = false;
16003 this.destroyTrans(trans, true);
16006 result = trans.reader.readRecords(o);
16008 this.fireEvent("loadexception", this, o, trans.arg, e);
16009 trans.callback.call(trans.scope||window, null, trans.arg, false);
16012 this.fireEvent("load", this, o, trans.arg);
16013 trans.callback.call(trans.scope||window, result, trans.arg, true);
16017 handleFailure : function(trans){
16018 this.trans = false;
16019 this.destroyTrans(trans, false);
16020 this.fireEvent("loadexception", this, null, trans.arg);
16021 trans.callback.call(trans.scope||window, null, trans.arg, false);
16025 * Ext JS Library 1.1.1
16026 * Copyright(c) 2006-2007, Ext JS, LLC.
16028 * Originally Released Under LGPL - original licence link has changed is not relivant.
16031 * <script type="text/javascript">
16035 * @class Roo.data.JsonReader
16036 * @extends Roo.data.DataReader
16037 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16038 * based on mappings in a provided Roo.data.Record constructor.
16040 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16041 * in the reply previously.
16046 var RecordDef = Roo.data.Record.create([
16047 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16048 {name: 'occupation'} // This field will use "occupation" as the mapping.
16050 var myReader = new Roo.data.JsonReader({
16051 totalProperty: "results", // The property which contains the total dataset size (optional)
16052 root: "rows", // The property which contains an Array of row objects
16053 id: "id" // The property within each row object that provides an ID for the record (optional)
16057 * This would consume a JSON file like this:
16059 { 'results': 2, 'rows': [
16060 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16061 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16064 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16065 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16066 * paged from the remote server.
16067 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16068 * @cfg {String} root name of the property which contains the Array of row objects.
16069 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16070 * @cfg {Array} fields Array of field definition objects
16072 * Create a new JsonReader
16073 * @param {Object} meta Metadata configuration options
16074 * @param {Object} recordType Either an Array of field definition objects,
16075 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16077 Roo.data.JsonReader = function(meta, recordType){
16080 // set some defaults:
16081 Roo.applyIf(meta, {
16082 totalProperty: 'total',
16083 successProperty : 'success',
16088 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16090 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16092 readerType : 'Json',
16095 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16096 * Used by Store query builder to append _requestMeta to params.
16099 metaFromRemote : false,
16101 * This method is only used by a DataProxy which has retrieved data from a remote server.
16102 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16103 * @return {Object} data A data block which is used by an Roo.data.Store object as
16104 * a cache of Roo.data.Records.
16106 read : function(response){
16107 var json = response.responseText;
16109 var o = /* eval:var:o */ eval("("+json+")");
16111 throw {message: "JsonReader.read: Json object not found"};
16117 this.metaFromRemote = true;
16118 this.meta = o.metaData;
16119 this.recordType = Roo.data.Record.create(o.metaData.fields);
16120 this.onMetaChange(this.meta, this.recordType, o);
16122 return this.readRecords(o);
16125 // private function a store will implement
16126 onMetaChange : function(meta, recordType, o){
16133 simpleAccess: function(obj, subsc) {
16140 getJsonAccessor: function(){
16142 return function(expr) {
16144 return(re.test(expr))
16145 ? new Function("obj", "return obj." + expr)
16150 return Roo.emptyFn;
16155 * Create a data block containing Roo.data.Records from an XML document.
16156 * @param {Object} o An object which contains an Array of row objects in the property specified
16157 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16158 * which contains the total size of the dataset.
16159 * @return {Object} data A data block which is used by an Roo.data.Store object as
16160 * a cache of Roo.data.Records.
16162 readRecords : function(o){
16164 * After any data loads, the raw JSON data is available for further custom processing.
16168 var s = this.meta, Record = this.recordType,
16169 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16171 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16173 if(s.totalProperty) {
16174 this.getTotal = this.getJsonAccessor(s.totalProperty);
16176 if(s.successProperty) {
16177 this.getSuccess = this.getJsonAccessor(s.successProperty);
16179 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16181 var g = this.getJsonAccessor(s.id);
16182 this.getId = function(rec) {
16184 return (r === undefined || r === "") ? null : r;
16187 this.getId = function(){return null;};
16190 for(var jj = 0; jj < fl; jj++){
16192 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16193 this.ef[jj] = this.getJsonAccessor(map);
16197 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16198 if(s.totalProperty){
16199 var vt = parseInt(this.getTotal(o), 10);
16204 if(s.successProperty){
16205 var vs = this.getSuccess(o);
16206 if(vs === false || vs === 'false'){
16211 for(var i = 0; i < c; i++){
16214 var id = this.getId(n);
16215 for(var j = 0; j < fl; j++){
16217 var v = this.ef[j](n);
16219 Roo.log('missing convert for ' + f.name);
16223 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16225 var record = new Record(values, id);
16227 records[i] = record;
16233 totalRecords : totalRecords
16236 // used when loading children.. @see loadDataFromChildren
16237 toLoadData: function(rec)
16239 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16240 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16241 return { data : data, total : data.length };
16246 * Ext JS Library 1.1.1
16247 * Copyright(c) 2006-2007, Ext JS, LLC.
16249 * Originally Released Under LGPL - original licence link has changed is not relivant.
16252 * <script type="text/javascript">
16256 * @class Roo.data.ArrayReader
16257 * @extends Roo.data.DataReader
16258 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16259 * Each element of that Array represents a row of data fields. The
16260 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16261 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16265 var RecordDef = Roo.data.Record.create([
16266 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16267 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16269 var myReader = new Roo.data.ArrayReader({
16270 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16274 * This would consume an Array like this:
16276 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16280 * Create a new JsonReader
16281 * @param {Object} meta Metadata configuration options.
16282 * @param {Object|Array} recordType Either an Array of field definition objects
16284 * @cfg {Array} fields Array of field definition objects
16285 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16286 * as specified to {@link Roo.data.Record#create},
16287 * or an {@link Roo.data.Record} object
16290 * created using {@link Roo.data.Record#create}.
16292 Roo.data.ArrayReader = function(meta, recordType)
16294 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16297 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16300 * Create a data block containing Roo.data.Records from an XML document.
16301 * @param {Object} o An Array of row objects which represents the dataset.
16302 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16303 * a cache of Roo.data.Records.
16305 readRecords : function(o)
16307 var sid = this.meta ? this.meta.id : null;
16308 var recordType = this.recordType, fields = recordType.prototype.fields;
16311 for(var i = 0; i < root.length; i++){
16314 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16315 for(var j = 0, jlen = fields.length; j < jlen; j++){
16316 var f = fields.items[j];
16317 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16318 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16320 values[f.name] = v;
16322 var record = new recordType(values, id);
16324 records[records.length] = record;
16328 totalRecords : records.length
16331 // used when loading children.. @see loadDataFromChildren
16332 toLoadData: function(rec)
16334 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16335 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16346 * @class Roo.bootstrap.ComboBox
16347 * @extends Roo.bootstrap.TriggerField
16348 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16349 * @cfg {Boolean} append (true|false) default false
16350 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16351 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16352 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16353 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16354 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16355 * @cfg {Boolean} animate default true
16356 * @cfg {Boolean} emptyResultText only for touch device
16357 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16358 * @cfg {String} emptyTitle default ''
16359 * @cfg {Number} width fixed with? experimental
16361 * Create a new ComboBox.
16362 * @param {Object} config Configuration options
16364 Roo.bootstrap.ComboBox = function(config){
16365 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16369 * Fires when the dropdown list is expanded
16370 * @param {Roo.bootstrap.ComboBox} combo This combo box
16375 * Fires when the dropdown list is collapsed
16376 * @param {Roo.bootstrap.ComboBox} combo This combo box
16380 * @event beforeselect
16381 * Fires before a list item is selected. Return false to cancel the selection.
16382 * @param {Roo.bootstrap.ComboBox} combo This combo box
16383 * @param {Roo.data.Record} record The data record returned from the underlying store
16384 * @param {Number} index The index of the selected item in the dropdown list
16386 'beforeselect' : true,
16389 * Fires when a list item is selected
16390 * @param {Roo.bootstrap.ComboBox} combo This combo box
16391 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16392 * @param {Number} index The index of the selected item in the dropdown list
16396 * @event beforequery
16397 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16398 * The event object passed has these properties:
16399 * @param {Roo.bootstrap.ComboBox} combo This combo box
16400 * @param {String} query The query
16401 * @param {Boolean} forceAll true to force "all" query
16402 * @param {Boolean} cancel true to cancel the query
16403 * @param {Object} e The query event object
16405 'beforequery': true,
16408 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16409 * @param {Roo.bootstrap.ComboBox} combo This combo box
16414 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16415 * @param {Roo.bootstrap.ComboBox} combo This combo box
16416 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16421 * Fires when the remove value from the combobox array
16422 * @param {Roo.bootstrap.ComboBox} combo This combo box
16426 * @event afterremove
16427 * Fires when the remove value from the combobox array
16428 * @param {Roo.bootstrap.ComboBox} combo This combo box
16430 'afterremove' : true,
16432 * @event specialfilter
16433 * Fires when specialfilter
16434 * @param {Roo.bootstrap.ComboBox} combo This combo box
16436 'specialfilter' : true,
16439 * Fires when tick the element
16440 * @param {Roo.bootstrap.ComboBox} combo This combo box
16444 * @event touchviewdisplay
16445 * Fires when touch view require special display (default is using displayField)
16446 * @param {Roo.bootstrap.ComboBox} combo This combo box
16447 * @param {Object} cfg set html .
16449 'touchviewdisplay' : true
16454 this.tickItems = [];
16456 this.selectedIndex = -1;
16457 if(this.mode == 'local'){
16458 if(config.queryDelay === undefined){
16459 this.queryDelay = 10;
16461 if(config.minChars === undefined){
16467 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16470 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16471 * rendering into an Roo.Editor, defaults to false)
16474 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16475 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16478 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16481 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16482 * the dropdown list (defaults to undefined, with no header element)
16486 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16490 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16492 listWidth: undefined,
16494 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16495 * mode = 'remote' or 'text' if mode = 'local')
16497 displayField: undefined,
16500 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16501 * mode = 'remote' or 'value' if mode = 'local').
16502 * Note: use of a valueField requires the user make a selection
16503 * in order for a value to be mapped.
16505 valueField: undefined,
16507 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16512 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16513 * field's data value (defaults to the underlying DOM element's name)
16515 hiddenName: undefined,
16517 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16521 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16523 selectedClass: 'active',
16526 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16530 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16531 * anchor positions (defaults to 'tl-bl')
16533 listAlign: 'tl-bl?',
16535 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16539 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16540 * query specified by the allQuery config option (defaults to 'query')
16542 triggerAction: 'query',
16544 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16545 * (defaults to 4, does not apply if editable = false)
16549 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16550 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16554 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16555 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16559 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16560 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16564 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16565 * when editable = true (defaults to false)
16567 selectOnFocus:false,
16569 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16571 queryParam: 'query',
16573 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16574 * when mode = 'remote' (defaults to 'Loading...')
16576 loadingText: 'Loading...',
16578 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16582 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16586 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16587 * traditional select (defaults to true)
16591 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16595 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16599 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16600 * listWidth has a higher value)
16604 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16605 * allow the user to set arbitrary text into the field (defaults to false)
16607 forceSelection:false,
16609 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16610 * if typeAhead = true (defaults to 250)
16612 typeAheadDelay : 250,
16614 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16615 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16617 valueNotFoundText : undefined,
16619 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16621 blockFocus : false,
16624 * @cfg {Boolean} disableClear Disable showing of clear button.
16626 disableClear : false,
16628 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16630 alwaysQuery : false,
16633 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16638 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16640 invalidClass : "has-warning",
16643 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16645 validClass : "has-success",
16648 * @cfg {Boolean} specialFilter (true|false) special filter default false
16650 specialFilter : false,
16653 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16655 mobileTouchView : true,
16658 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16660 useNativeIOS : false,
16663 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16665 mobile_restrict_height : false,
16667 ios_options : false,
16679 btnPosition : 'right',
16680 triggerList : true,
16681 showToggleBtn : true,
16683 emptyResultText: 'Empty',
16684 triggerText : 'Select',
16688 // element that contains real text value.. (when hidden is used..)
16690 getAutoCreate : function()
16695 * Render classic select for iso
16698 if(Roo.isIOS && this.useNativeIOS){
16699 cfg = this.getAutoCreateNativeIOS();
16707 if(Roo.isTouch && this.mobileTouchView){
16708 cfg = this.getAutoCreateTouchView();
16715 if(!this.tickable){
16716 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16721 * ComboBox with tickable selections
16724 var align = this.labelAlign || this.parentLabelAlign();
16727 cls : 'form-group roo-combobox-tickable' //input-group
16730 var btn_text_select = '';
16731 var btn_text_done = '';
16732 var btn_text_cancel = '';
16734 if (this.btn_text_show) {
16735 btn_text_select = 'Select';
16736 btn_text_done = 'Done';
16737 btn_text_cancel = 'Cancel';
16742 cls : 'tickable-buttons',
16747 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16748 //html : this.triggerText
16749 html: btn_text_select
16755 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16757 html: btn_text_done
16763 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16765 html: btn_text_cancel
16771 buttons.cn.unshift({
16773 cls: 'roo-select2-search-field-input'
16779 Roo.each(buttons.cn, function(c){
16781 c.cls += ' btn-' + _this.size;
16784 if (_this.disabled) {
16791 style : 'display: contents',
16796 cls: 'form-hidden-field'
16800 cls: 'roo-select2-choices',
16804 cls: 'roo-select2-search-field',
16815 cls: 'roo-select2-container input-group roo-select2-container-multi',
16821 // cls: 'typeahead typeahead-long dropdown-menu',
16822 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16827 if(this.hasFeedback && !this.allowBlank){
16831 cls: 'glyphicon form-control-feedback'
16834 combobox.cn.push(feedback);
16841 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16842 tooltip : 'This field is required'
16844 if (Roo.bootstrap.version == 4) {
16847 style : 'display:none'
16850 if (align ==='left' && this.fieldLabel.length) {
16852 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16859 cls : 'control-label col-form-label',
16860 html : this.fieldLabel
16872 var labelCfg = cfg.cn[1];
16873 var contentCfg = cfg.cn[2];
16876 if(this.indicatorpos == 'right'){
16882 cls : 'control-label col-form-label',
16886 html : this.fieldLabel
16902 labelCfg = cfg.cn[0];
16903 contentCfg = cfg.cn[1];
16907 if(this.labelWidth > 12){
16908 labelCfg.style = "width: " + this.labelWidth + 'px';
16910 if(this.width * 1 > 0){
16911 contentCfg.style = "width: " + this.width + 'px';
16913 if(this.labelWidth < 13 && this.labelmd == 0){
16914 this.labelmd = this.labelWidth;
16917 if(this.labellg > 0){
16918 labelCfg.cls += ' col-lg-' + this.labellg;
16919 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16922 if(this.labelmd > 0){
16923 labelCfg.cls += ' col-md-' + this.labelmd;
16924 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16927 if(this.labelsm > 0){
16928 labelCfg.cls += ' col-sm-' + this.labelsm;
16929 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16932 if(this.labelxs > 0){
16933 labelCfg.cls += ' col-xs-' + this.labelxs;
16934 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16938 } else if ( this.fieldLabel.length) {
16939 // Roo.log(" label");
16944 //cls : 'input-group-addon',
16945 html : this.fieldLabel
16950 if(this.indicatorpos == 'right'){
16954 //cls : 'input-group-addon',
16955 html : this.fieldLabel
16965 // Roo.log(" no label && no align");
16972 ['xs','sm','md','lg'].map(function(size){
16973 if (settings[size]) {
16974 cfg.cls += ' col-' + size + '-' + settings[size];
16982 _initEventsCalled : false,
16985 initEvents: function()
16987 if (this._initEventsCalled) { // as we call render... prevent looping...
16990 this._initEventsCalled = true;
16993 throw "can not find store for combo";
16996 this.indicator = this.indicatorEl();
16998 this.store = Roo.factory(this.store, Roo.data);
16999 this.store.parent = this;
17001 // if we are building from html. then this element is so complex, that we can not really
17002 // use the rendered HTML.
17003 // so we have to trash and replace the previous code.
17004 if (Roo.XComponent.build_from_html) {
17005 // remove this element....
17006 var e = this.el.dom, k=0;
17007 while (e ) { e = e.previousSibling; ++k;}
17012 this.rendered = false;
17014 this.render(this.parent().getChildContainer(true), k);
17017 if(Roo.isIOS && this.useNativeIOS){
17018 this.initIOSView();
17026 if(Roo.isTouch && this.mobileTouchView){
17027 this.initTouchView();
17032 this.initTickableEvents();
17036 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17038 if(this.hiddenName){
17040 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17042 this.hiddenField.dom.value =
17043 this.hiddenValue !== undefined ? this.hiddenValue :
17044 this.value !== undefined ? this.value : '';
17046 // prevent input submission
17047 this.el.dom.removeAttribute('name');
17048 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17053 // this.el.dom.setAttribute('autocomplete', 'off');
17056 var cls = 'x-combo-list';
17058 //this.list = new Roo.Layer({
17059 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17065 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17066 _this.list.setWidth(lw);
17069 this.list.on('mouseover', this.onViewOver, this);
17070 this.list.on('mousemove', this.onViewMove, this);
17071 this.list.on('scroll', this.onViewScroll, this);
17074 this.list.swallowEvent('mousewheel');
17075 this.assetHeight = 0;
17078 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17079 this.assetHeight += this.header.getHeight();
17082 this.innerList = this.list.createChild({cls:cls+'-inner'});
17083 this.innerList.on('mouseover', this.onViewOver, this);
17084 this.innerList.on('mousemove', this.onViewMove, this);
17085 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17087 if(this.allowBlank && !this.pageSize && !this.disableClear){
17088 this.footer = this.list.createChild({cls:cls+'-ft'});
17089 this.pageTb = new Roo.Toolbar(this.footer);
17093 this.footer = this.list.createChild({cls:cls+'-ft'});
17094 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17095 {pageSize: this.pageSize});
17099 if (this.pageTb && this.allowBlank && !this.disableClear) {
17101 this.pageTb.add(new Roo.Toolbar.Fill(), {
17102 cls: 'x-btn-icon x-btn-clear',
17104 handler: function()
17107 _this.clearValue();
17108 _this.onSelect(false, -1);
17113 this.assetHeight += this.footer.getHeight();
17118 this.tpl = Roo.bootstrap.version == 4 ?
17119 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17120 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17123 this.view = new Roo.View(this.list, this.tpl, {
17124 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17126 //this.view.wrapEl.setDisplayed(false);
17127 this.view.on('click', this.onViewClick, this);
17130 this.store.on('beforeload', this.onBeforeLoad, this);
17131 this.store.on('load', this.onLoad, this);
17132 this.store.on('loadexception', this.onLoadException, this);
17134 if(this.resizable){
17135 this.resizer = new Roo.Resizable(this.list, {
17136 pinned:true, handles:'se'
17138 this.resizer.on('resize', function(r, w, h){
17139 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17140 this.listWidth = w;
17141 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17142 this.restrictHeight();
17144 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17147 if(!this.editable){
17148 this.editable = true;
17149 this.setEditable(false);
17154 if (typeof(this.events.add.listeners) != 'undefined') {
17156 this.addicon = this.wrap.createChild(
17157 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17159 this.addicon.on('click', function(e) {
17160 this.fireEvent('add', this);
17163 if (typeof(this.events.edit.listeners) != 'undefined') {
17165 this.editicon = this.wrap.createChild(
17166 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17167 if (this.addicon) {
17168 this.editicon.setStyle('margin-left', '40px');
17170 this.editicon.on('click', function(e) {
17172 // we fire even if inothing is selected..
17173 this.fireEvent('edit', this, this.lastData );
17179 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17180 "up" : function(e){
17181 this.inKeyMode = true;
17185 "down" : function(e){
17186 if(!this.isExpanded()){
17187 this.onTriggerClick();
17189 this.inKeyMode = true;
17194 "enter" : function(e){
17195 // this.onViewClick();
17199 if(this.fireEvent("specialkey", this, e)){
17200 this.onViewClick(false);
17206 "esc" : function(e){
17210 "tab" : function(e){
17213 if(this.fireEvent("specialkey", this, e)){
17214 this.onViewClick(false);
17222 doRelay : function(foo, bar, hname){
17223 if(hname == 'down' || this.scope.isExpanded()){
17224 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17233 this.queryDelay = Math.max(this.queryDelay || 10,
17234 this.mode == 'local' ? 10 : 250);
17237 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17239 if(this.typeAhead){
17240 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17242 if(this.editable !== false){
17243 this.inputEl().on("keyup", this.onKeyUp, this);
17245 if(this.forceSelection){
17246 this.inputEl().on('blur', this.doForce, this);
17250 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17251 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17255 initTickableEvents: function()
17259 if(this.hiddenName){
17261 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17263 this.hiddenField.dom.value =
17264 this.hiddenValue !== undefined ? this.hiddenValue :
17265 this.value !== undefined ? this.value : '';
17267 // prevent input submission
17268 this.el.dom.removeAttribute('name');
17269 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17274 // this.list = this.el.select('ul.dropdown-menu',true).first();
17276 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17277 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17278 if(this.triggerList){
17279 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17282 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17283 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17285 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17286 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17288 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17289 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17291 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17292 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17293 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17296 this.cancelBtn.hide();
17301 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17302 _this.list.setWidth(lw);
17305 this.list.on('mouseover', this.onViewOver, this);
17306 this.list.on('mousemove', this.onViewMove, this);
17308 this.list.on('scroll', this.onViewScroll, this);
17311 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17312 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17315 this.view = new Roo.View(this.list, this.tpl, {
17320 selectedClass: this.selectedClass
17323 //this.view.wrapEl.setDisplayed(false);
17324 this.view.on('click', this.onViewClick, this);
17328 this.store.on('beforeload', this.onBeforeLoad, this);
17329 this.store.on('load', this.onLoad, this);
17330 this.store.on('loadexception', this.onLoadException, this);
17333 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17334 "up" : function(e){
17335 this.inKeyMode = true;
17339 "down" : function(e){
17340 this.inKeyMode = true;
17344 "enter" : function(e){
17345 if(this.fireEvent("specialkey", this, e)){
17346 this.onViewClick(false);
17352 "esc" : function(e){
17353 this.onTickableFooterButtonClick(e, false, false);
17356 "tab" : function(e){
17357 this.fireEvent("specialkey", this, e);
17359 this.onTickableFooterButtonClick(e, false, false);
17366 doRelay : function(e, fn, key){
17367 if(this.scope.isExpanded()){
17368 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17377 this.queryDelay = Math.max(this.queryDelay || 10,
17378 this.mode == 'local' ? 10 : 250);
17381 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17383 if(this.typeAhead){
17384 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17387 if(this.editable !== false){
17388 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17391 this.indicator = this.indicatorEl();
17393 if(this.indicator){
17394 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17395 this.indicator.hide();
17400 onDestroy : function(){
17402 this.view.setStore(null);
17403 this.view.el.removeAllListeners();
17404 this.view.el.remove();
17405 this.view.purgeListeners();
17408 this.list.dom.innerHTML = '';
17412 this.store.un('beforeload', this.onBeforeLoad, this);
17413 this.store.un('load', this.onLoad, this);
17414 this.store.un('loadexception', this.onLoadException, this);
17416 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17420 fireKey : function(e){
17421 if(e.isNavKeyPress() && !this.list.isVisible()){
17422 this.fireEvent("specialkey", this, e);
17427 onResize: function(w, h)
17431 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17433 // if(typeof w != 'number'){
17434 // // we do not handle it!?!?
17437 // var tw = this.trigger.getWidth();
17438 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17439 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17441 // this.inputEl().setWidth( this.adjustWidth('input', x));
17443 // //this.trigger.setStyle('left', x+'px');
17445 // if(this.list && this.listWidth === undefined){
17446 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17447 // this.list.setWidth(lw);
17448 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17456 * Allow or prevent the user from directly editing the field text. If false is passed,
17457 * the user will only be able to select from the items defined in the dropdown list. This method
17458 * is the runtime equivalent of setting the 'editable' config option at config time.
17459 * @param {Boolean} value True to allow the user to directly edit the field text
17461 setEditable : function(value){
17462 if(value == this.editable){
17465 this.editable = value;
17467 this.inputEl().dom.setAttribute('readOnly', true);
17468 this.inputEl().on('mousedown', this.onTriggerClick, this);
17469 this.inputEl().addClass('x-combo-noedit');
17471 this.inputEl().dom.removeAttribute('readOnly');
17472 this.inputEl().un('mousedown', this.onTriggerClick, this);
17473 this.inputEl().removeClass('x-combo-noedit');
17479 onBeforeLoad : function(combo,opts){
17480 if(!this.hasFocus){
17484 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17486 this.restrictHeight();
17487 this.selectedIndex = -1;
17491 onLoad : function(){
17493 this.hasQuery = false;
17495 if(!this.hasFocus){
17499 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17500 this.loading.hide();
17503 if(this.store.getCount() > 0){
17506 this.restrictHeight();
17507 if(this.lastQuery == this.allQuery){
17508 if(this.editable && !this.tickable){
17509 this.inputEl().dom.select();
17513 !this.selectByValue(this.value, true) &&
17516 !this.store.lastOptions ||
17517 typeof(this.store.lastOptions.add) == 'undefined' ||
17518 this.store.lastOptions.add != true
17521 this.select(0, true);
17524 if(this.autoFocus){
17527 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17528 this.taTask.delay(this.typeAheadDelay);
17532 this.onEmptyResults();
17538 onLoadException : function()
17540 this.hasQuery = false;
17542 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17543 this.loading.hide();
17546 if(this.tickable && this.editable){
17551 // only causes errors at present
17552 //Roo.log(this.store.reader.jsonData);
17553 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17555 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17561 onTypeAhead : function(){
17562 if(this.store.getCount() > 0){
17563 var r = this.store.getAt(0);
17564 var newValue = r.data[this.displayField];
17565 var len = newValue.length;
17566 var selStart = this.getRawValue().length;
17568 if(selStart != len){
17569 this.setRawValue(newValue);
17570 this.selectText(selStart, newValue.length);
17576 onSelect : function(record, index){
17578 if(this.fireEvent('beforeselect', this, record, index) !== false){
17580 this.setFromData(index > -1 ? record.data : false);
17583 this.fireEvent('select', this, record, index);
17588 * Returns the currently selected field value or empty string if no value is set.
17589 * @return {String} value The selected value
17591 getValue : function()
17593 if(Roo.isIOS && this.useNativeIOS){
17594 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17598 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17601 if(this.valueField){
17602 return typeof this.value != 'undefined' ? this.value : '';
17604 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17608 getRawValue : function()
17610 if(Roo.isIOS && this.useNativeIOS){
17611 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17614 var v = this.inputEl().getValue();
17620 * Clears any text/value currently set in the field
17622 clearValue : function(){
17624 if(this.hiddenField){
17625 this.hiddenField.dom.value = '';
17628 this.setRawValue('');
17629 this.lastSelectionText = '';
17630 this.lastData = false;
17632 var close = this.closeTriggerEl();
17643 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17644 * will be displayed in the field. If the value does not match the data value of an existing item,
17645 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17646 * Otherwise the field will be blank (although the value will still be set).
17647 * @param {String} value The value to match
17649 setValue : function(v)
17651 if(Roo.isIOS && this.useNativeIOS){
17652 this.setIOSValue(v);
17662 if(this.valueField){
17663 var r = this.findRecord(this.valueField, v);
17665 text = r.data[this.displayField];
17666 }else if(this.valueNotFoundText !== undefined){
17667 text = this.valueNotFoundText;
17670 this.lastSelectionText = text;
17671 if(this.hiddenField){
17672 this.hiddenField.dom.value = v;
17674 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17677 var close = this.closeTriggerEl();
17680 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17686 * @property {Object} the last set data for the element
17691 * Sets the value of the field based on a object which is related to the record format for the store.
17692 * @param {Object} value the value to set as. or false on reset?
17694 setFromData : function(o){
17701 var dv = ''; // display value
17702 var vv = ''; // value value..
17704 if (this.displayField) {
17705 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17707 // this is an error condition!!!
17708 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17711 if(this.valueField){
17712 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17715 var close = this.closeTriggerEl();
17718 if(dv.length || vv * 1 > 0){
17720 this.blockFocus=true;
17726 if(this.hiddenField){
17727 this.hiddenField.dom.value = vv;
17729 this.lastSelectionText = dv;
17730 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17734 // no hidden field.. - we store the value in 'value', but still display
17735 // display field!!!!
17736 this.lastSelectionText = dv;
17737 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17744 reset : function(){
17745 // overridden so that last data is reset..
17752 this.setValue(this.originalValue);
17753 //this.clearInvalid();
17754 this.lastData = false;
17756 this.view.clearSelections();
17762 findRecord : function(prop, value){
17764 if(this.store.getCount() > 0){
17765 this.store.each(function(r){
17766 if(r.data[prop] == value){
17776 getName: function()
17778 // returns hidden if it's set..
17779 if (!this.rendered) {return ''};
17780 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17784 onViewMove : function(e, t){
17785 this.inKeyMode = false;
17789 onViewOver : function(e, t){
17790 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17793 var item = this.view.findItemFromChild(t);
17796 var index = this.view.indexOf(item);
17797 this.select(index, false);
17802 onViewClick : function(view, doFocus, el, e)
17804 var index = this.view.getSelectedIndexes()[0];
17806 var r = this.store.getAt(index);
17810 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17817 Roo.each(this.tickItems, function(v,k){
17819 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17821 _this.tickItems.splice(k, 1);
17823 if(typeof(e) == 'undefined' && view == false){
17824 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17836 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17837 this.tickItems.push(r.data);
17840 if(typeof(e) == 'undefined' && view == false){
17841 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17848 this.onSelect(r, index);
17850 if(doFocus !== false && !this.blockFocus){
17851 this.inputEl().focus();
17856 restrictHeight : function(){
17857 //this.innerList.dom.style.height = '';
17858 //var inner = this.innerList.dom;
17859 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17860 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17861 //this.list.beginUpdate();
17862 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17863 this.list.alignTo(this.inputEl(), this.listAlign);
17864 this.list.alignTo(this.inputEl(), this.listAlign);
17865 //this.list.endUpdate();
17869 onEmptyResults : function(){
17871 if(this.tickable && this.editable){
17872 this.hasFocus = false;
17873 this.restrictHeight();
17881 * Returns true if the dropdown list is expanded, else false.
17883 isExpanded : function(){
17884 return this.list.isVisible();
17888 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17889 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17890 * @param {String} value The data value of the item to select
17891 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17892 * selected item if it is not currently in view (defaults to true)
17893 * @return {Boolean} True if the value matched an item in the list, else false
17895 selectByValue : function(v, scrollIntoView){
17896 if(v !== undefined && v !== null){
17897 var r = this.findRecord(this.valueField || this.displayField, v);
17899 this.select(this.store.indexOf(r), scrollIntoView);
17907 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17908 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17909 * @param {Number} index The zero-based index of the list item to select
17910 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17911 * selected item if it is not currently in view (defaults to true)
17913 select : function(index, scrollIntoView){
17914 this.selectedIndex = index;
17915 this.view.select(index);
17916 if(scrollIntoView !== false){
17917 var el = this.view.getNode(index);
17919 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17922 this.list.scrollChildIntoView(el, false);
17928 selectNext : function(){
17929 var ct = this.store.getCount();
17931 if(this.selectedIndex == -1){
17933 }else if(this.selectedIndex < ct-1){
17934 this.select(this.selectedIndex+1);
17940 selectPrev : function(){
17941 var ct = this.store.getCount();
17943 if(this.selectedIndex == -1){
17945 }else if(this.selectedIndex != 0){
17946 this.select(this.selectedIndex-1);
17952 onKeyUp : function(e){
17953 if(this.editable !== false && !e.isSpecialKey()){
17954 this.lastKey = e.getKey();
17955 this.dqTask.delay(this.queryDelay);
17960 validateBlur : function(){
17961 return !this.list || !this.list.isVisible();
17965 initQuery : function(){
17967 var v = this.getRawValue();
17969 if(this.tickable && this.editable){
17970 v = this.tickableInputEl().getValue();
17977 doForce : function(){
17978 if(this.inputEl().dom.value.length > 0){
17979 this.inputEl().dom.value =
17980 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17986 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17987 * query allowing the query action to be canceled if needed.
17988 * @param {String} query The SQL query to execute
17989 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17990 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17991 * saved in the current store (defaults to false)
17993 doQuery : function(q, forceAll){
17995 if(q === undefined || q === null){
18000 forceAll: forceAll,
18004 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18009 forceAll = qe.forceAll;
18010 if(forceAll === true || (q.length >= this.minChars)){
18012 this.hasQuery = true;
18014 if(this.lastQuery != q || this.alwaysQuery){
18015 this.lastQuery = q;
18016 if(this.mode == 'local'){
18017 this.selectedIndex = -1;
18019 this.store.clearFilter();
18022 if(this.specialFilter){
18023 this.fireEvent('specialfilter', this);
18028 this.store.filter(this.displayField, q);
18031 this.store.fireEvent("datachanged", this.store);
18038 this.store.baseParams[this.queryParam] = q;
18040 var options = {params : this.getParams(q)};
18043 options.add = true;
18044 options.params.start = this.page * this.pageSize;
18047 this.store.load(options);
18050 * this code will make the page width larger, at the beginning, the list not align correctly,
18051 * we should expand the list on onLoad
18052 * so command out it
18057 this.selectedIndex = -1;
18062 this.loadNext = false;
18066 getParams : function(q){
18068 //p[this.queryParam] = q;
18072 p.limit = this.pageSize;
18078 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18080 collapse : function(){
18081 if(!this.isExpanded()){
18087 this.hasFocus = false;
18091 this.cancelBtn.hide();
18092 this.trigger.show();
18095 this.tickableInputEl().dom.value = '';
18096 this.tickableInputEl().blur();
18101 Roo.get(document).un('mousedown', this.collapseIf, this);
18102 Roo.get(document).un('mousewheel', this.collapseIf, this);
18103 if (!this.editable) {
18104 Roo.get(document).un('keydown', this.listKeyPress, this);
18106 this.fireEvent('collapse', this);
18112 collapseIf : function(e){
18113 var in_combo = e.within(this.el);
18114 var in_list = e.within(this.list);
18115 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18117 if (in_combo || in_list || is_list) {
18118 //e.stopPropagation();
18123 this.onTickableFooterButtonClick(e, false, false);
18131 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18133 expand : function(){
18135 if(this.isExpanded() || !this.hasFocus){
18139 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18140 this.list.setWidth(lw);
18146 this.restrictHeight();
18150 this.tickItems = Roo.apply([], this.item);
18153 this.cancelBtn.show();
18154 this.trigger.hide();
18157 this.tickableInputEl().focus();
18162 Roo.get(document).on('mousedown', this.collapseIf, this);
18163 Roo.get(document).on('mousewheel', this.collapseIf, this);
18164 if (!this.editable) {
18165 Roo.get(document).on('keydown', this.listKeyPress, this);
18168 this.fireEvent('expand', this);
18172 // Implements the default empty TriggerField.onTriggerClick function
18173 onTriggerClick : function(e)
18175 Roo.log('trigger click');
18177 if(this.disabled || !this.triggerList){
18182 this.loadNext = false;
18184 if(this.isExpanded()){
18186 if (!this.blockFocus) {
18187 this.inputEl().focus();
18191 this.hasFocus = true;
18192 if(this.triggerAction == 'all') {
18193 this.doQuery(this.allQuery, true);
18195 this.doQuery(this.getRawValue());
18197 if (!this.blockFocus) {
18198 this.inputEl().focus();
18203 onTickableTriggerClick : function(e)
18210 this.loadNext = false;
18211 this.hasFocus = true;
18213 if(this.triggerAction == 'all') {
18214 this.doQuery(this.allQuery, true);
18216 this.doQuery(this.getRawValue());
18220 onSearchFieldClick : function(e)
18222 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18223 this.onTickableFooterButtonClick(e, false, false);
18227 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18232 this.loadNext = false;
18233 this.hasFocus = true;
18235 if(this.triggerAction == 'all') {
18236 this.doQuery(this.allQuery, true);
18238 this.doQuery(this.getRawValue());
18242 listKeyPress : function(e)
18244 //Roo.log('listkeypress');
18245 // scroll to first matching element based on key pres..
18246 if (e.isSpecialKey()) {
18249 var k = String.fromCharCode(e.getKey()).toUpperCase();
18252 var csel = this.view.getSelectedNodes();
18253 var cselitem = false;
18255 var ix = this.view.indexOf(csel[0]);
18256 cselitem = this.store.getAt(ix);
18257 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18263 this.store.each(function(v) {
18265 // start at existing selection.
18266 if (cselitem.id == v.id) {
18272 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18273 match = this.store.indexOf(v);
18279 if (match === false) {
18280 return true; // no more action?
18283 this.view.select(match);
18284 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18285 sn.scrollIntoView(sn.dom.parentNode, false);
18288 onViewScroll : function(e, t){
18290 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){
18294 this.hasQuery = true;
18296 this.loading = this.list.select('.loading', true).first();
18298 if(this.loading === null){
18299 this.list.createChild({
18301 cls: 'loading roo-select2-more-results roo-select2-active',
18302 html: 'Loading more results...'
18305 this.loading = this.list.select('.loading', true).first();
18307 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18309 this.loading.hide();
18312 this.loading.show();
18317 this.loadNext = true;
18319 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18324 addItem : function(o)
18326 var dv = ''; // display value
18328 if (this.displayField) {
18329 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18331 // this is an error condition!!!
18332 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18339 var choice = this.choices.createChild({
18341 cls: 'roo-select2-search-choice',
18350 cls: 'roo-select2-search-choice-close fa fa-times',
18355 }, this.searchField);
18357 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18359 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18367 this.inputEl().dom.value = '';
18372 onRemoveItem : function(e, _self, o)
18374 e.preventDefault();
18376 this.lastItem = Roo.apply([], this.item);
18378 var index = this.item.indexOf(o.data) * 1;
18381 Roo.log('not this item?!');
18385 this.item.splice(index, 1);
18390 this.fireEvent('remove', this, e);
18396 syncValue : function()
18398 if(!this.item.length){
18405 Roo.each(this.item, function(i){
18406 if(_this.valueField){
18407 value.push(i[_this.valueField]);
18414 this.value = value.join(',');
18416 if(this.hiddenField){
18417 this.hiddenField.dom.value = this.value;
18420 this.store.fireEvent("datachanged", this.store);
18425 clearItem : function()
18427 if(!this.multiple){
18433 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18441 if(this.tickable && !Roo.isTouch){
18442 this.view.refresh();
18446 inputEl: function ()
18448 if(Roo.isIOS && this.useNativeIOS){
18449 return this.el.select('select.roo-ios-select', true).first();
18452 if(Roo.isTouch && this.mobileTouchView){
18453 return this.el.select('input.form-control',true).first();
18457 return this.searchField;
18460 return this.el.select('input.form-control',true).first();
18463 onTickableFooterButtonClick : function(e, btn, el)
18465 e.preventDefault();
18467 this.lastItem = Roo.apply([], this.item);
18469 if(btn && btn.name == 'cancel'){
18470 this.tickItems = Roo.apply([], this.item);
18479 Roo.each(this.tickItems, function(o){
18487 validate : function()
18489 if(this.getVisibilityEl().hasClass('hidden')){
18493 var v = this.getRawValue();
18496 v = this.getValue();
18499 if(this.disabled || this.allowBlank || v.length){
18504 this.markInvalid();
18508 tickableInputEl : function()
18510 if(!this.tickable || !this.editable){
18511 return this.inputEl();
18514 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18518 getAutoCreateTouchView : function()
18523 cls: 'form-group' //input-group
18529 type : this.inputType,
18530 cls : 'form-control x-combo-noedit',
18531 autocomplete: 'new-password',
18532 placeholder : this.placeholder || '',
18537 input.name = this.name;
18541 input.cls += ' input-' + this.size;
18544 if (this.disabled) {
18545 input.disabled = true;
18549 cls : 'roo-combobox-wrap',
18556 inputblock.cls += ' input-group';
18558 inputblock.cn.unshift({
18560 cls : 'input-group-addon input-group-prepend input-group-text',
18565 if(this.removable && !this.multiple){
18566 inputblock.cls += ' roo-removable';
18568 inputblock.cn.push({
18571 cls : 'roo-combo-removable-btn close'
18575 if(this.hasFeedback && !this.allowBlank){
18577 inputblock.cls += ' has-feedback';
18579 inputblock.cn.push({
18581 cls: 'glyphicon form-control-feedback'
18588 inputblock.cls += (this.before) ? '' : ' input-group';
18590 inputblock.cn.push({
18592 cls : 'input-group-addon input-group-append input-group-text',
18598 var ibwrap = inputblock;
18603 cls: 'roo-select2-choices',
18607 cls: 'roo-select2-search-field',
18620 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18625 cls: 'form-hidden-field'
18631 if(!this.multiple && this.showToggleBtn){
18637 if (this.caret != false) {
18640 cls: 'fa fa-' + this.caret
18647 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18649 Roo.bootstrap.version == 3 ? caret : '',
18652 cls: 'combobox-clear',
18666 combobox.cls += ' roo-select2-container-multi';
18669 var required = this.allowBlank ? {
18671 style: 'display: none'
18674 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18675 tooltip : 'This field is required'
18678 var align = this.labelAlign || this.parentLabelAlign();
18680 if (align ==='left' && this.fieldLabel.length) {
18686 cls : 'control-label col-form-label',
18687 html : this.fieldLabel
18691 cls : 'roo-combobox-wrap ',
18698 var labelCfg = cfg.cn[1];
18699 var contentCfg = cfg.cn[2];
18702 if(this.indicatorpos == 'right'){
18707 cls : 'control-label col-form-label',
18711 html : this.fieldLabel
18717 cls : "roo-combobox-wrap ",
18725 labelCfg = cfg.cn[0];
18726 contentCfg = cfg.cn[1];
18731 if(this.labelWidth > 12){
18732 labelCfg.style = "width: " + this.labelWidth + 'px';
18735 if(this.labelWidth < 13 && this.labelmd == 0){
18736 this.labelmd = this.labelWidth;
18739 if(this.labellg > 0){
18740 labelCfg.cls += ' col-lg-' + this.labellg;
18741 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18744 if(this.labelmd > 0){
18745 labelCfg.cls += ' col-md-' + this.labelmd;
18746 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18749 if(this.labelsm > 0){
18750 labelCfg.cls += ' col-sm-' + this.labelsm;
18751 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18754 if(this.labelxs > 0){
18755 labelCfg.cls += ' col-xs-' + this.labelxs;
18756 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18760 } else if ( this.fieldLabel.length) {
18765 cls : 'control-label',
18766 html : this.fieldLabel
18777 if(this.indicatorpos == 'right'){
18781 cls : 'control-label',
18782 html : this.fieldLabel,
18800 var settings = this;
18802 ['xs','sm','md','lg'].map(function(size){
18803 if (settings[size]) {
18804 cfg.cls += ' col-' + size + '-' + settings[size];
18811 initTouchView : function()
18813 this.renderTouchView();
18815 this.touchViewEl.on('scroll', function(){
18816 this.el.dom.scrollTop = 0;
18819 this.originalValue = this.getValue();
18821 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18823 this.inputEl().on("click", this.showTouchView, this);
18824 if (this.triggerEl) {
18825 this.triggerEl.on("click", this.showTouchView, this);
18829 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18830 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18832 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18834 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18835 this.store.on('load', this.onTouchViewLoad, this);
18836 this.store.on('loadexception', this.onTouchViewLoadException, this);
18838 if(this.hiddenName){
18840 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18842 this.hiddenField.dom.value =
18843 this.hiddenValue !== undefined ? this.hiddenValue :
18844 this.value !== undefined ? this.value : '';
18846 this.el.dom.removeAttribute('name');
18847 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18851 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18852 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18855 if(this.removable && !this.multiple){
18856 var close = this.closeTriggerEl();
18858 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18859 close.on('click', this.removeBtnClick, this, close);
18863 * fix the bug in Safari iOS8
18865 this.inputEl().on("focus", function(e){
18866 document.activeElement.blur();
18869 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18876 renderTouchView : function()
18878 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18879 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18881 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18882 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18884 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18885 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18886 this.touchViewBodyEl.setStyle('overflow', 'auto');
18888 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18889 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18891 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18892 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18896 showTouchView : function()
18902 this.touchViewHeaderEl.hide();
18904 if(this.modalTitle.length){
18905 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18906 this.touchViewHeaderEl.show();
18909 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18910 this.touchViewEl.show();
18912 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18914 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18915 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18917 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18919 if(this.modalTitle.length){
18920 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18923 this.touchViewBodyEl.setHeight(bodyHeight);
18927 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18929 this.touchViewEl.addClass(['in','show']);
18932 if(this._touchViewMask){
18933 Roo.get(document.body).addClass("x-body-masked");
18934 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18935 this._touchViewMask.setStyle('z-index', 10000);
18936 this._touchViewMask.addClass('show');
18939 this.doTouchViewQuery();
18943 hideTouchView : function()
18945 this.touchViewEl.removeClass(['in','show']);
18949 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18951 this.touchViewEl.setStyle('display', 'none');
18954 if(this._touchViewMask){
18955 this._touchViewMask.removeClass('show');
18956 Roo.get(document.body).removeClass("x-body-masked");
18960 setTouchViewValue : function()
18967 Roo.each(this.tickItems, function(o){
18972 this.hideTouchView();
18975 doTouchViewQuery : function()
18984 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18988 if(!this.alwaysQuery || this.mode == 'local'){
18989 this.onTouchViewLoad();
18996 onTouchViewBeforeLoad : function(combo,opts)
19002 onTouchViewLoad : function()
19004 if(this.store.getCount() < 1){
19005 this.onTouchViewEmptyResults();
19009 this.clearTouchView();
19011 var rawValue = this.getRawValue();
19013 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19015 this.tickItems = [];
19017 this.store.data.each(function(d, rowIndex){
19018 var row = this.touchViewListGroup.createChild(template);
19020 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19021 row.addClass(d.data.cls);
19024 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19027 html : d.data[this.displayField]
19030 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19031 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19034 row.removeClass('selected');
19035 if(!this.multiple && this.valueField &&
19036 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19039 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19040 row.addClass('selected');
19043 if(this.multiple && this.valueField &&
19044 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19048 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19049 this.tickItems.push(d.data);
19052 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19056 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19058 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19060 if(this.modalTitle.length){
19061 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19064 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19066 if(this.mobile_restrict_height && listHeight < bodyHeight){
19067 this.touchViewBodyEl.setHeight(listHeight);
19072 if(firstChecked && listHeight > bodyHeight){
19073 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19078 onTouchViewLoadException : function()
19080 this.hideTouchView();
19083 onTouchViewEmptyResults : function()
19085 this.clearTouchView();
19087 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19089 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19093 clearTouchView : function()
19095 this.touchViewListGroup.dom.innerHTML = '';
19098 onTouchViewClick : function(e, el, o)
19100 e.preventDefault();
19103 var rowIndex = o.rowIndex;
19105 var r = this.store.getAt(rowIndex);
19107 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19109 if(!this.multiple){
19110 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19111 c.dom.removeAttribute('checked');
19114 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19116 this.setFromData(r.data);
19118 var close = this.closeTriggerEl();
19124 this.hideTouchView();
19126 this.fireEvent('select', this, r, rowIndex);
19131 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19132 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19133 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19137 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19138 this.addItem(r.data);
19139 this.tickItems.push(r.data);
19143 getAutoCreateNativeIOS : function()
19146 cls: 'form-group' //input-group,
19151 cls : 'roo-ios-select'
19155 combobox.name = this.name;
19158 if (this.disabled) {
19159 combobox.disabled = true;
19162 var settings = this;
19164 ['xs','sm','md','lg'].map(function(size){
19165 if (settings[size]) {
19166 cfg.cls += ' col-' + size + '-' + settings[size];
19176 initIOSView : function()
19178 this.store.on('load', this.onIOSViewLoad, this);
19183 onIOSViewLoad : function()
19185 if(this.store.getCount() < 1){
19189 this.clearIOSView();
19191 if(this.allowBlank) {
19193 var default_text = '-- SELECT --';
19195 if(this.placeholder.length){
19196 default_text = this.placeholder;
19199 if(this.emptyTitle.length){
19200 default_text += ' - ' + this.emptyTitle + ' -';
19203 var opt = this.inputEl().createChild({
19206 html : default_text
19210 o[this.valueField] = 0;
19211 o[this.displayField] = default_text;
19213 this.ios_options.push({
19220 this.store.data.each(function(d, rowIndex){
19224 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19225 html = d.data[this.displayField];
19230 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19231 value = d.data[this.valueField];
19240 if(this.value == d.data[this.valueField]){
19241 option['selected'] = true;
19244 var opt = this.inputEl().createChild(option);
19246 this.ios_options.push({
19253 this.inputEl().on('change', function(){
19254 this.fireEvent('select', this);
19259 clearIOSView: function()
19261 this.inputEl().dom.innerHTML = '';
19263 this.ios_options = [];
19266 setIOSValue: function(v)
19270 if(!this.ios_options){
19274 Roo.each(this.ios_options, function(opts){
19276 opts.el.dom.removeAttribute('selected');
19278 if(opts.data[this.valueField] != v){
19282 opts.el.dom.setAttribute('selected', true);
19288 * @cfg {Boolean} grow
19292 * @cfg {Number} growMin
19296 * @cfg {Number} growMax
19305 Roo.apply(Roo.bootstrap.ComboBox, {
19309 cls: 'modal-header',
19331 cls: 'list-group-item',
19335 cls: 'roo-combobox-list-group-item-value'
19339 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19353 listItemCheckbox : {
19355 cls: 'list-group-item',
19359 cls: 'roo-combobox-list-group-item-value'
19363 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19379 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19384 cls: 'modal-footer',
19392 cls: 'col-xs-6 text-left',
19395 cls: 'btn btn-danger roo-touch-view-cancel',
19401 cls: 'col-xs-6 text-right',
19404 cls: 'btn btn-success roo-touch-view-ok',
19415 Roo.apply(Roo.bootstrap.ComboBox, {
19417 touchViewTemplate : {
19419 cls: 'modal fade roo-combobox-touch-view',
19423 cls: 'modal-dialog',
19424 style : 'position:fixed', // we have to fix position....
19428 cls: 'modal-content',
19430 Roo.bootstrap.ComboBox.header,
19431 Roo.bootstrap.ComboBox.body,
19432 Roo.bootstrap.ComboBox.footer
19441 * Ext JS Library 1.1.1
19442 * Copyright(c) 2006-2007, Ext JS, LLC.
19444 * Originally Released Under LGPL - original licence link has changed is not relivant.
19447 * <script type="text/javascript">
19452 * @extends Roo.util.Observable
19453 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19454 * This class also supports single and multi selection modes. <br>
19455 * Create a data model bound view:
19457 var store = new Roo.data.Store(...);
19459 var view = new Roo.View({
19461 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19463 singleSelect: true,
19464 selectedClass: "ydataview-selected",
19468 // listen for node click?
19469 view.on("click", function(vw, index, node, e){
19470 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19474 dataModel.load("foobar.xml");
19476 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19478 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19479 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19481 * Note: old style constructor is still suported (container, template, config)
19484 * Create a new View
19485 * @param {Object} config The config object
19488 Roo.View = function(config, depreciated_tpl, depreciated_config){
19490 this.parent = false;
19492 if (typeof(depreciated_tpl) == 'undefined') {
19493 // new way.. - universal constructor.
19494 Roo.apply(this, config);
19495 this.el = Roo.get(this.el);
19498 this.el = Roo.get(config);
19499 this.tpl = depreciated_tpl;
19500 Roo.apply(this, depreciated_config);
19502 this.wrapEl = this.el.wrap().wrap();
19503 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19506 if(typeof(this.tpl) == "string"){
19507 this.tpl = new Roo.Template(this.tpl);
19509 // support xtype ctors..
19510 this.tpl = new Roo.factory(this.tpl, Roo);
19514 this.tpl.compile();
19519 * @event beforeclick
19520 * Fires before a click is processed. Returns false to cancel the default action.
19521 * @param {Roo.View} this
19522 * @param {Number} index The index of the target node
19523 * @param {HTMLElement} node The target node
19524 * @param {Roo.EventObject} e The raw event object
19526 "beforeclick" : true,
19529 * Fires when a template node is clicked.
19530 * @param {Roo.View} this
19531 * @param {Number} index The index of the target node
19532 * @param {HTMLElement} node The target node
19533 * @param {Roo.EventObject} e The raw event object
19538 * Fires when a template node is double clicked.
19539 * @param {Roo.View} this
19540 * @param {Number} index The index of the target node
19541 * @param {HTMLElement} node The target node
19542 * @param {Roo.EventObject} e The raw event object
19546 * @event contextmenu
19547 * Fires when a template node is right clicked.
19548 * @param {Roo.View} this
19549 * @param {Number} index The index of the target node
19550 * @param {HTMLElement} node The target node
19551 * @param {Roo.EventObject} e The raw event object
19553 "contextmenu" : true,
19555 * @event selectionchange
19556 * Fires when the selected nodes change.
19557 * @param {Roo.View} this
19558 * @param {Array} selections Array of the selected nodes
19560 "selectionchange" : true,
19563 * @event beforeselect
19564 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19565 * @param {Roo.View} this
19566 * @param {HTMLElement} node The node to be selected
19567 * @param {Array} selections Array of currently selected nodes
19569 "beforeselect" : true,
19571 * @event preparedata
19572 * Fires on every row to render, to allow you to change the data.
19573 * @param {Roo.View} this
19574 * @param {Object} data to be rendered (change this)
19576 "preparedata" : true
19584 "click": this.onClick,
19585 "dblclick": this.onDblClick,
19586 "contextmenu": this.onContextMenu,
19590 this.selections = [];
19592 this.cmp = new Roo.CompositeElementLite([]);
19594 this.store = Roo.factory(this.store, Roo.data);
19595 this.setStore(this.store, true);
19598 if ( this.footer && this.footer.xtype) {
19600 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19602 this.footer.dataSource = this.store;
19603 this.footer.container = fctr;
19604 this.footer = Roo.factory(this.footer, Roo);
19605 fctr.insertFirst(this.el);
19607 // this is a bit insane - as the paging toolbar seems to detach the el..
19608 // dom.parentNode.parentNode.parentNode
19609 // they get detached?
19613 Roo.View.superclass.constructor.call(this);
19618 Roo.extend(Roo.View, Roo.util.Observable, {
19621 * @cfg {Roo.data.Store} store Data store to load data from.
19626 * @cfg {String|Roo.Element} el The container element.
19631 * @cfg {String|Roo.Template} tpl The template used by this View
19635 * @cfg {String} dataName the named area of the template to use as the data area
19636 * Works with domtemplates roo-name="name"
19640 * @cfg {String} selectedClass The css class to add to selected nodes
19642 selectedClass : "x-view-selected",
19644 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19649 * @cfg {String} text to display on mask (default Loading)
19653 * @cfg {Boolean} multiSelect Allow multiple selection
19655 multiSelect : false,
19657 * @cfg {Boolean} singleSelect Allow single selection
19659 singleSelect: false,
19662 * @cfg {Boolean} toggleSelect - selecting
19664 toggleSelect : false,
19667 * @cfg {Boolean} tickable - selecting
19672 * Returns the element this view is bound to.
19673 * @return {Roo.Element}
19675 getEl : function(){
19676 return this.wrapEl;
19682 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19684 refresh : function(){
19685 //Roo.log('refresh');
19688 // if we are using something like 'domtemplate', then
19689 // the what gets used is:
19690 // t.applySubtemplate(NAME, data, wrapping data..)
19691 // the outer template then get' applied with
19692 // the store 'extra data'
19693 // and the body get's added to the
19694 // roo-name="data" node?
19695 // <span class='roo-tpl-{name}'></span> ?????
19699 this.clearSelections();
19700 this.el.update("");
19702 var records = this.store.getRange();
19703 if(records.length < 1) {
19705 // is this valid?? = should it render a template??
19707 this.el.update(this.emptyText);
19711 if (this.dataName) {
19712 this.el.update(t.apply(this.store.meta)); //????
19713 el = this.el.child('.roo-tpl-' + this.dataName);
19716 for(var i = 0, len = records.length; i < len; i++){
19717 var data = this.prepareData(records[i].data, i, records[i]);
19718 this.fireEvent("preparedata", this, data, i, records[i]);
19720 var d = Roo.apply({}, data);
19723 Roo.apply(d, {'roo-id' : Roo.id()});
19727 Roo.each(this.parent.item, function(item){
19728 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19731 Roo.apply(d, {'roo-data-checked' : 'checked'});
19735 html[html.length] = Roo.util.Format.trim(
19737 t.applySubtemplate(this.dataName, d, this.store.meta) :
19744 el.update(html.join(""));
19745 this.nodes = el.dom.childNodes;
19746 this.updateIndexes(0);
19751 * Function to override to reformat the data that is sent to
19752 * the template for each node.
19753 * DEPRICATED - use the preparedata event handler.
19754 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19755 * a JSON object for an UpdateManager bound view).
19757 prepareData : function(data, index, record)
19759 this.fireEvent("preparedata", this, data, index, record);
19763 onUpdate : function(ds, record){
19764 // Roo.log('on update');
19765 this.clearSelections();
19766 var index = this.store.indexOf(record);
19767 var n = this.nodes[index];
19768 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19769 n.parentNode.removeChild(n);
19770 this.updateIndexes(index, index);
19776 onAdd : function(ds, records, index)
19778 //Roo.log(['on Add', ds, records, index] );
19779 this.clearSelections();
19780 if(this.nodes.length == 0){
19784 var n = this.nodes[index];
19785 for(var i = 0, len = records.length; i < len; i++){
19786 var d = this.prepareData(records[i].data, i, records[i]);
19788 this.tpl.insertBefore(n, d);
19791 this.tpl.append(this.el, d);
19794 this.updateIndexes(index);
19797 onRemove : function(ds, record, index){
19798 // Roo.log('onRemove');
19799 this.clearSelections();
19800 var el = this.dataName ?
19801 this.el.child('.roo-tpl-' + this.dataName) :
19804 el.dom.removeChild(this.nodes[index]);
19805 this.updateIndexes(index);
19809 * Refresh an individual node.
19810 * @param {Number} index
19812 refreshNode : function(index){
19813 this.onUpdate(this.store, this.store.getAt(index));
19816 updateIndexes : function(startIndex, endIndex){
19817 var ns = this.nodes;
19818 startIndex = startIndex || 0;
19819 endIndex = endIndex || ns.length - 1;
19820 for(var i = startIndex; i <= endIndex; i++){
19821 ns[i].nodeIndex = i;
19826 * Changes the data store this view uses and refresh the view.
19827 * @param {Store} store
19829 setStore : function(store, initial){
19830 if(!initial && this.store){
19831 this.store.un("datachanged", this.refresh);
19832 this.store.un("add", this.onAdd);
19833 this.store.un("remove", this.onRemove);
19834 this.store.un("update", this.onUpdate);
19835 this.store.un("clear", this.refresh);
19836 this.store.un("beforeload", this.onBeforeLoad);
19837 this.store.un("load", this.onLoad);
19838 this.store.un("loadexception", this.onLoad);
19842 store.on("datachanged", this.refresh, this);
19843 store.on("add", this.onAdd, this);
19844 store.on("remove", this.onRemove, this);
19845 store.on("update", this.onUpdate, this);
19846 store.on("clear", this.refresh, this);
19847 store.on("beforeload", this.onBeforeLoad, this);
19848 store.on("load", this.onLoad, this);
19849 store.on("loadexception", this.onLoad, this);
19857 * onbeforeLoad - masks the loading area.
19860 onBeforeLoad : function(store,opts)
19862 //Roo.log('onBeforeLoad');
19864 this.el.update("");
19866 this.el.mask(this.mask ? this.mask : "Loading" );
19868 onLoad : function ()
19875 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19876 * @param {HTMLElement} node
19877 * @return {HTMLElement} The template node
19879 findItemFromChild : function(node){
19880 var el = this.dataName ?
19881 this.el.child('.roo-tpl-' + this.dataName,true) :
19884 if(!node || node.parentNode == el){
19887 var p = node.parentNode;
19888 while(p && p != el){
19889 if(p.parentNode == el){
19898 onClick : function(e){
19899 var item = this.findItemFromChild(e.getTarget());
19901 var index = this.indexOf(item);
19902 if(this.onItemClick(item, index, e) !== false){
19903 this.fireEvent("click", this, index, item, e);
19906 this.clearSelections();
19911 onContextMenu : function(e){
19912 var item = this.findItemFromChild(e.getTarget());
19914 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19919 onDblClick : function(e){
19920 var item = this.findItemFromChild(e.getTarget());
19922 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19926 onItemClick : function(item, index, e)
19928 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19931 if (this.toggleSelect) {
19932 var m = this.isSelected(item) ? 'unselect' : 'select';
19935 _t[m](item, true, false);
19938 if(this.multiSelect || this.singleSelect){
19939 if(this.multiSelect && e.shiftKey && this.lastSelection){
19940 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19942 this.select(item, this.multiSelect && e.ctrlKey);
19943 this.lastSelection = item;
19946 if(!this.tickable){
19947 e.preventDefault();
19955 * Get the number of selected nodes.
19958 getSelectionCount : function(){
19959 return this.selections.length;
19963 * Get the currently selected nodes.
19964 * @return {Array} An array of HTMLElements
19966 getSelectedNodes : function(){
19967 return this.selections;
19971 * Get the indexes of the selected nodes.
19974 getSelectedIndexes : function(){
19975 var indexes = [], s = this.selections;
19976 for(var i = 0, len = s.length; i < len; i++){
19977 indexes.push(s[i].nodeIndex);
19983 * Clear all selections
19984 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19986 clearSelections : function(suppressEvent){
19987 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19988 this.cmp.elements = this.selections;
19989 this.cmp.removeClass(this.selectedClass);
19990 this.selections = [];
19991 if(!suppressEvent){
19992 this.fireEvent("selectionchange", this, this.selections);
19998 * Returns true if the passed node is selected
19999 * @param {HTMLElement/Number} node The node or node index
20000 * @return {Boolean}
20002 isSelected : function(node){
20003 var s = this.selections;
20007 node = this.getNode(node);
20008 return s.indexOf(node) !== -1;
20013 * @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
20014 * @param {Boolean} keepExisting (optional) true to keep existing selections
20015 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20017 select : function(nodeInfo, keepExisting, suppressEvent){
20018 if(nodeInfo instanceof Array){
20020 this.clearSelections(true);
20022 for(var i = 0, len = nodeInfo.length; i < len; i++){
20023 this.select(nodeInfo[i], true, true);
20027 var node = this.getNode(nodeInfo);
20028 if(!node || this.isSelected(node)){
20029 return; // already selected.
20032 this.clearSelections(true);
20035 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20036 Roo.fly(node).addClass(this.selectedClass);
20037 this.selections.push(node);
20038 if(!suppressEvent){
20039 this.fireEvent("selectionchange", this, this.selections);
20047 * @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
20048 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20049 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20051 unselect : function(nodeInfo, keepExisting, suppressEvent)
20053 if(nodeInfo instanceof Array){
20054 Roo.each(this.selections, function(s) {
20055 this.unselect(s, nodeInfo);
20059 var node = this.getNode(nodeInfo);
20060 if(!node || !this.isSelected(node)){
20061 //Roo.log("not selected");
20062 return; // not selected.
20066 Roo.each(this.selections, function(s) {
20068 Roo.fly(node).removeClass(this.selectedClass);
20075 this.selections= ns;
20076 this.fireEvent("selectionchange", this, this.selections);
20080 * Gets a template node.
20081 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20082 * @return {HTMLElement} The node or null if it wasn't found
20084 getNode : function(nodeInfo){
20085 if(typeof nodeInfo == "string"){
20086 return document.getElementById(nodeInfo);
20087 }else if(typeof nodeInfo == "number"){
20088 return this.nodes[nodeInfo];
20094 * Gets a range template nodes.
20095 * @param {Number} startIndex
20096 * @param {Number} endIndex
20097 * @return {Array} An array of nodes
20099 getNodes : function(start, end){
20100 var ns = this.nodes;
20101 start = start || 0;
20102 end = typeof end == "undefined" ? ns.length - 1 : end;
20105 for(var i = start; i <= end; i++){
20109 for(var i = start; i >= end; i--){
20117 * Finds the index of the passed node
20118 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20119 * @return {Number} The index of the node or -1
20121 indexOf : function(node){
20122 node = this.getNode(node);
20123 if(typeof node.nodeIndex == "number"){
20124 return node.nodeIndex;
20126 var ns = this.nodes;
20127 for(var i = 0, len = ns.length; i < len; i++){
20138 * based on jquery fullcalendar
20142 Roo.bootstrap = Roo.bootstrap || {};
20144 * @class Roo.bootstrap.Calendar
20145 * @extends Roo.bootstrap.Component
20146 * Bootstrap Calendar class
20147 * @cfg {Boolean} loadMask (true|false) default false
20148 * @cfg {Object} header generate the user specific header of the calendar, default false
20151 * Create a new Container
20152 * @param {Object} config The config object
20157 Roo.bootstrap.Calendar = function(config){
20158 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20162 * Fires when a date is selected
20163 * @param {DatePicker} this
20164 * @param {Date} date The selected date
20168 * @event monthchange
20169 * Fires when the displayed month changes
20170 * @param {DatePicker} this
20171 * @param {Date} date The selected month
20173 'monthchange': true,
20175 * @event evententer
20176 * Fires when mouse over an event
20177 * @param {Calendar} this
20178 * @param {event} Event
20180 'evententer': true,
20182 * @event eventleave
20183 * Fires when the mouse leaves an
20184 * @param {Calendar} this
20187 'eventleave': true,
20189 * @event eventclick
20190 * Fires when the mouse click an
20191 * @param {Calendar} this
20200 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20203 * @cfg {Number} startDay
20204 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20212 getAutoCreate : function(){
20215 var fc_button = function(name, corner, style, content ) {
20216 return Roo.apply({},{
20218 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20220 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20223 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20234 style : 'width:100%',
20241 cls : 'fc-header-left',
20243 fc_button('prev', 'left', 'arrow', '‹' ),
20244 fc_button('next', 'right', 'arrow', '›' ),
20245 { tag: 'span', cls: 'fc-header-space' },
20246 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20254 cls : 'fc-header-center',
20258 cls: 'fc-header-title',
20261 html : 'month / year'
20269 cls : 'fc-header-right',
20271 /* fc_button('month', 'left', '', 'month' ),
20272 fc_button('week', '', '', 'week' ),
20273 fc_button('day', 'right', '', 'day' )
20285 header = this.header;
20288 var cal_heads = function() {
20290 // fixme - handle this.
20292 for (var i =0; i < Date.dayNames.length; i++) {
20293 var d = Date.dayNames[i];
20296 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20297 html : d.substring(0,3)
20301 ret[0].cls += ' fc-first';
20302 ret[6].cls += ' fc-last';
20305 var cal_cell = function(n) {
20308 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20313 cls: 'fc-day-number',
20317 cls: 'fc-day-content',
20321 style: 'position: relative;' // height: 17px;
20333 var cal_rows = function() {
20336 for (var r = 0; r < 6; r++) {
20343 for (var i =0; i < Date.dayNames.length; i++) {
20344 var d = Date.dayNames[i];
20345 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20348 row.cn[0].cls+=' fc-first';
20349 row.cn[0].cn[0].style = 'min-height:90px';
20350 row.cn[6].cls+=' fc-last';
20354 ret[0].cls += ' fc-first';
20355 ret[4].cls += ' fc-prev-last';
20356 ret[5].cls += ' fc-last';
20363 cls: 'fc-border-separate',
20364 style : 'width:100%',
20372 cls : 'fc-first fc-last',
20390 cls : 'fc-content',
20391 style : "position: relative;",
20394 cls : 'fc-view fc-view-month fc-grid',
20395 style : 'position: relative',
20396 unselectable : 'on',
20399 cls : 'fc-event-container',
20400 style : 'position:absolute;z-index:8;top:0;left:0;'
20418 initEvents : function()
20421 throw "can not find store for calendar";
20427 style: "text-align:center",
20431 style: "background-color:white;width:50%;margin:250 auto",
20435 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20446 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20448 var size = this.el.select('.fc-content', true).first().getSize();
20449 this.maskEl.setSize(size.width, size.height);
20450 this.maskEl.enableDisplayMode("block");
20451 if(!this.loadMask){
20452 this.maskEl.hide();
20455 this.store = Roo.factory(this.store, Roo.data);
20456 this.store.on('load', this.onLoad, this);
20457 this.store.on('beforeload', this.onBeforeLoad, this);
20461 this.cells = this.el.select('.fc-day',true);
20462 //Roo.log(this.cells);
20463 this.textNodes = this.el.query('.fc-day-number');
20464 this.cells.addClassOnOver('fc-state-hover');
20466 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20467 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20468 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20469 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20471 this.on('monthchange', this.onMonthChange, this);
20473 this.update(new Date().clearTime());
20476 resize : function() {
20477 var sz = this.el.getSize();
20479 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20480 this.el.select('.fc-day-content div',true).setHeight(34);
20485 showPrevMonth : function(e){
20486 this.update(this.activeDate.add("mo", -1));
20488 showToday : function(e){
20489 this.update(new Date().clearTime());
20492 showNextMonth : function(e){
20493 this.update(this.activeDate.add("mo", 1));
20497 showPrevYear : function(){
20498 this.update(this.activeDate.add("y", -1));
20502 showNextYear : function(){
20503 this.update(this.activeDate.add("y", 1));
20508 update : function(date)
20510 var vd = this.activeDate;
20511 this.activeDate = date;
20512 // if(vd && this.el){
20513 // var t = date.getTime();
20514 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20515 // Roo.log('using add remove');
20517 // this.fireEvent('monthchange', this, date);
20519 // this.cells.removeClass("fc-state-highlight");
20520 // this.cells.each(function(c){
20521 // if(c.dateValue == t){
20522 // c.addClass("fc-state-highlight");
20523 // setTimeout(function(){
20524 // try{c.dom.firstChild.focus();}catch(e){}
20534 var days = date.getDaysInMonth();
20536 var firstOfMonth = date.getFirstDateOfMonth();
20537 var startingPos = firstOfMonth.getDay()-this.startDay;
20539 if(startingPos < this.startDay){
20543 var pm = date.add(Date.MONTH, -1);
20544 var prevStart = pm.getDaysInMonth()-startingPos;
20546 this.cells = this.el.select('.fc-day',true);
20547 this.textNodes = this.el.query('.fc-day-number');
20548 this.cells.addClassOnOver('fc-state-hover');
20550 var cells = this.cells.elements;
20551 var textEls = this.textNodes;
20553 Roo.each(cells, function(cell){
20554 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20557 days += startingPos;
20559 // convert everything to numbers so it's fast
20560 var day = 86400000;
20561 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20564 //Roo.log(prevStart);
20566 var today = new Date().clearTime().getTime();
20567 var sel = date.clearTime().getTime();
20568 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20569 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20570 var ddMatch = this.disabledDatesRE;
20571 var ddText = this.disabledDatesText;
20572 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20573 var ddaysText = this.disabledDaysText;
20574 var format = this.format;
20576 var setCellClass = function(cal, cell){
20580 //Roo.log('set Cell Class');
20582 var t = d.getTime();
20586 cell.dateValue = t;
20588 cell.className += " fc-today";
20589 cell.className += " fc-state-highlight";
20590 cell.title = cal.todayText;
20593 // disable highlight in other month..
20594 //cell.className += " fc-state-highlight";
20599 cell.className = " fc-state-disabled";
20600 cell.title = cal.minText;
20604 cell.className = " fc-state-disabled";
20605 cell.title = cal.maxText;
20609 if(ddays.indexOf(d.getDay()) != -1){
20610 cell.title = ddaysText;
20611 cell.className = " fc-state-disabled";
20614 if(ddMatch && format){
20615 var fvalue = d.dateFormat(format);
20616 if(ddMatch.test(fvalue)){
20617 cell.title = ddText.replace("%0", fvalue);
20618 cell.className = " fc-state-disabled";
20622 if (!cell.initialClassName) {
20623 cell.initialClassName = cell.dom.className;
20626 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20631 for(; i < startingPos; i++) {
20632 textEls[i].innerHTML = (++prevStart);
20633 d.setDate(d.getDate()+1);
20635 cells[i].className = "fc-past fc-other-month";
20636 setCellClass(this, cells[i]);
20641 for(; i < days; i++){
20642 intDay = i - startingPos + 1;
20643 textEls[i].innerHTML = (intDay);
20644 d.setDate(d.getDate()+1);
20646 cells[i].className = ''; // "x-date-active";
20647 setCellClass(this, cells[i]);
20651 for(; i < 42; i++) {
20652 textEls[i].innerHTML = (++extraDays);
20653 d.setDate(d.getDate()+1);
20655 cells[i].className = "fc-future fc-other-month";
20656 setCellClass(this, cells[i]);
20659 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20661 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20663 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20664 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20666 if(totalRows != 6){
20667 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20668 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20671 this.fireEvent('monthchange', this, date);
20675 if(!this.internalRender){
20676 var main = this.el.dom.firstChild;
20677 var w = main.offsetWidth;
20678 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20679 Roo.fly(main).setWidth(w);
20680 this.internalRender = true;
20681 // opera does not respect the auto grow header center column
20682 // then, after it gets a width opera refuses to recalculate
20683 // without a second pass
20684 if(Roo.isOpera && !this.secondPass){
20685 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20686 this.secondPass = true;
20687 this.update.defer(10, this, [date]);
20694 findCell : function(dt) {
20695 dt = dt.clearTime().getTime();
20697 this.cells.each(function(c){
20698 //Roo.log("check " +c.dateValue + '?=' + dt);
20699 if(c.dateValue == dt){
20709 findCells : function(ev) {
20710 var s = ev.start.clone().clearTime().getTime();
20712 var e= ev.end.clone().clearTime().getTime();
20715 this.cells.each(function(c){
20716 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20718 if(c.dateValue > e){
20721 if(c.dateValue < s){
20730 // findBestRow: function(cells)
20734 // for (var i =0 ; i < cells.length;i++) {
20735 // ret = Math.max(cells[i].rows || 0,ret);
20742 addItem : function(ev)
20744 // look for vertical location slot in
20745 var cells = this.findCells(ev);
20747 // ev.row = this.findBestRow(cells);
20749 // work out the location.
20753 for(var i =0; i < cells.length; i++) {
20755 cells[i].row = cells[0].row;
20758 cells[i].row = cells[i].row + 1;
20768 if (crow.start.getY() == cells[i].getY()) {
20770 crow.end = cells[i];
20787 cells[0].events.push(ev);
20789 this.calevents.push(ev);
20792 clearEvents: function() {
20794 if(!this.calevents){
20798 Roo.each(this.cells.elements, function(c){
20804 Roo.each(this.calevents, function(e) {
20805 Roo.each(e.els, function(el) {
20806 el.un('mouseenter' ,this.onEventEnter, this);
20807 el.un('mouseleave' ,this.onEventLeave, this);
20812 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20818 renderEvents: function()
20822 this.cells.each(function(c) {
20831 if(c.row != c.events.length){
20832 r = 4 - (4 - (c.row - c.events.length));
20835 c.events = ev.slice(0, r);
20836 c.more = ev.slice(r);
20838 if(c.more.length && c.more.length == 1){
20839 c.events.push(c.more.pop());
20842 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20846 this.cells.each(function(c) {
20848 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20851 for (var e = 0; e < c.events.length; e++){
20852 var ev = c.events[e];
20853 var rows = ev.rows;
20855 for(var i = 0; i < rows.length; i++) {
20857 // how many rows should it span..
20860 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20861 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20863 unselectable : "on",
20866 cls: 'fc-event-inner',
20870 // cls: 'fc-event-time',
20871 // html : cells.length > 1 ? '' : ev.time
20875 cls: 'fc-event-title',
20876 html : String.format('{0}', ev.title)
20883 cls: 'ui-resizable-handle ui-resizable-e',
20884 html : '  '
20891 cfg.cls += ' fc-event-start';
20893 if ((i+1) == rows.length) {
20894 cfg.cls += ' fc-event-end';
20897 var ctr = _this.el.select('.fc-event-container',true).first();
20898 var cg = ctr.createChild(cfg);
20900 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20901 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20903 var r = (c.more.length) ? 1 : 0;
20904 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20905 cg.setWidth(ebox.right - sbox.x -2);
20907 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20908 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20909 cg.on('click', _this.onEventClick, _this, ev);
20920 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20921 style : 'position: absolute',
20922 unselectable : "on",
20925 cls: 'fc-event-inner',
20929 cls: 'fc-event-title',
20937 cls: 'ui-resizable-handle ui-resizable-e',
20938 html : '  '
20944 var ctr = _this.el.select('.fc-event-container',true).first();
20945 var cg = ctr.createChild(cfg);
20947 var sbox = c.select('.fc-day-content',true).first().getBox();
20948 var ebox = c.select('.fc-day-content',true).first().getBox();
20950 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20951 cg.setWidth(ebox.right - sbox.x -2);
20953 cg.on('click', _this.onMoreEventClick, _this, c.more);
20963 onEventEnter: function (e, el,event,d) {
20964 this.fireEvent('evententer', this, el, event);
20967 onEventLeave: function (e, el,event,d) {
20968 this.fireEvent('eventleave', this, el, event);
20971 onEventClick: function (e, el,event,d) {
20972 this.fireEvent('eventclick', this, el, event);
20975 onMonthChange: function () {
20979 onMoreEventClick: function(e, el, more)
20983 this.calpopover.placement = 'right';
20984 this.calpopover.setTitle('More');
20986 this.calpopover.setContent('');
20988 var ctr = this.calpopover.el.select('.popover-content', true).first();
20990 Roo.each(more, function(m){
20992 cls : 'fc-event-hori fc-event-draggable',
20995 var cg = ctr.createChild(cfg);
20997 cg.on('click', _this.onEventClick, _this, m);
21000 this.calpopover.show(el);
21005 onLoad: function ()
21007 this.calevents = [];
21010 if(this.store.getCount() > 0){
21011 this.store.data.each(function(d){
21014 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21015 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21016 time : d.data.start_time,
21017 title : d.data.title,
21018 description : d.data.description,
21019 venue : d.data.venue
21024 this.renderEvents();
21026 if(this.calevents.length && this.loadMask){
21027 this.maskEl.hide();
21031 onBeforeLoad: function()
21033 this.clearEvents();
21035 this.maskEl.show();
21049 * @class Roo.bootstrap.Popover
21050 * @extends Roo.bootstrap.Component
21051 * Bootstrap Popover class
21052 * @cfg {String} html contents of the popover (or false to use children..)
21053 * @cfg {String} title of popover (or false to hide)
21054 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21055 * @cfg {String} trigger click || hover (or false to trigger manually)
21056 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21057 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21058 * - if false and it has a 'parent' then it will be automatically added to that element
21059 * - if string - Roo.get will be called
21060 * @cfg {Number} delay - delay before showing
21063 * Create a new Popover
21064 * @param {Object} config The config object
21067 Roo.bootstrap.Popover = function(config){
21068 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21074 * After the popover show
21076 * @param {Roo.bootstrap.Popover} this
21081 * After the popover hide
21083 * @param {Roo.bootstrap.Popover} this
21089 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21094 placement : 'right',
21095 trigger : 'hover', // hover
21101 can_build_overlaid : false,
21103 maskEl : false, // the mask element
21106 alignEl : false, // when show is called with an element - this get's stored.
21108 getChildContainer : function()
21110 return this.contentEl;
21113 getPopoverHeader : function()
21115 this.title = true; // flag not to hide it..
21116 this.headerEl.addClass('p-0');
21117 return this.headerEl
21121 getAutoCreate : function(){
21124 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21125 style: 'display:block',
21131 cls : 'popover-inner ',
21135 cls: 'popover-title popover-header',
21136 html : this.title === false ? '' : this.title
21139 cls : 'popover-content popover-body ' + (this.cls || ''),
21140 html : this.html || ''
21151 * @param {string} the title
21153 setTitle: function(str)
21157 this.headerEl.dom.innerHTML = str;
21162 * @param {string} the body content
21164 setContent: function(str)
21167 if (this.contentEl) {
21168 this.contentEl.dom.innerHTML = str;
21172 // as it get's added to the bottom of the page.
21173 onRender : function(ct, position)
21175 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21180 var cfg = Roo.apply({}, this.getAutoCreate());
21184 cfg.cls += ' ' + this.cls;
21187 cfg.style = this.style;
21189 //Roo.log("adding to ");
21190 this.el = Roo.get(document.body).createChild(cfg, position);
21191 // Roo.log(this.el);
21194 this.contentEl = this.el.select('.popover-content',true).first();
21195 this.headerEl = this.el.select('.popover-title',true).first();
21198 if(typeof(this.items) != 'undefined'){
21199 var items = this.items;
21202 for(var i =0;i < items.length;i++) {
21203 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21207 this.items = nitems;
21209 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21210 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21217 resizeMask : function()
21219 this.maskEl.setSize(
21220 Roo.lib.Dom.getViewWidth(true),
21221 Roo.lib.Dom.getViewHeight(true)
21225 initEvents : function()
21229 Roo.bootstrap.Popover.register(this);
21232 this.arrowEl = this.el.select('.arrow',true).first();
21233 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21234 this.el.enableDisplayMode('block');
21238 if (this.over === false && !this.parent()) {
21241 if (this.triggers === false) {
21246 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21247 var triggers = this.trigger ? this.trigger.split(' ') : [];
21248 Roo.each(triggers, function(trigger) {
21250 if (trigger == 'click') {
21251 on_el.on('click', this.toggle, this);
21252 } else if (trigger != 'manual') {
21253 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21254 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21256 on_el.on(eventIn ,this.enter, this);
21257 on_el.on(eventOut, this.leave, this);
21267 toggle : function () {
21268 this.hoverState == 'in' ? this.leave() : this.enter();
21271 enter : function () {
21273 clearTimeout(this.timeout);
21275 this.hoverState = 'in';
21277 if (!this.delay || !this.delay.show) {
21282 this.timeout = setTimeout(function () {
21283 if (_t.hoverState == 'in') {
21286 }, this.delay.show)
21289 leave : function() {
21290 clearTimeout(this.timeout);
21292 this.hoverState = 'out';
21294 if (!this.delay || !this.delay.hide) {
21299 this.timeout = setTimeout(function () {
21300 if (_t.hoverState == 'out') {
21303 }, this.delay.hide)
21307 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21308 * @param {string} (left|right|top|bottom) position
21310 show : function (on_el, placement)
21312 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21313 on_el = on_el || false; // default to false
21316 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21317 on_el = this.parent().el;
21318 } else if (this.over) {
21319 on_el = Roo.get(this.over);
21324 this.alignEl = Roo.get( on_el );
21327 this.render(document.body);
21333 if (this.title === false) {
21334 this.headerEl.hide();
21339 this.el.dom.style.display = 'block';
21342 if (this.alignEl) {
21343 this.updatePosition(this.placement, true);
21346 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21347 var es = this.el.getSize();
21348 var x = Roo.lib.Dom.getViewWidth()/2;
21349 var y = Roo.lib.Dom.getViewHeight()/2;
21350 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21355 //var arrow = this.el.select('.arrow',true).first();
21356 //arrow.set(align[2],
21358 this.el.addClass('in');
21362 this.hoverState = 'in';
21365 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21366 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21367 this.maskEl.dom.style.display = 'block';
21368 this.maskEl.addClass('show');
21370 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21372 this.fireEvent('show', this);
21376 * fire this manually after loading a grid in the table for example
21377 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21378 * @param {Boolean} try and move it if we cant get right position.
21380 updatePosition : function(placement, try_move)
21382 // allow for calling with no parameters
21383 placement = placement ? placement : this.placement;
21384 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21386 this.el.removeClass([
21387 'fade','top','bottom', 'left', 'right','in',
21388 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21390 this.el.addClass(placement + ' bs-popover-' + placement);
21392 if (!this.alignEl ) {
21396 switch (placement) {
21398 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21399 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21400 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21401 //normal display... or moved up/down.
21402 this.el.setXY(offset);
21403 var xy = this.alignEl.getAnchorXY('tr', false);
21405 this.arrowEl.setXY(xy);
21408 // continue through...
21409 return this.updatePosition('left', false);
21413 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21414 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21415 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21416 //normal display... or moved up/down.
21417 this.el.setXY(offset);
21418 var xy = this.alignEl.getAnchorXY('tl', false);
21419 xy[0]-=10;xy[1]+=5; // << fix me
21420 this.arrowEl.setXY(xy);
21424 return this.updatePosition('right', false);
21427 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21428 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21429 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21430 //normal display... or moved up/down.
21431 this.el.setXY(offset);
21432 var xy = this.alignEl.getAnchorXY('t', false);
21433 xy[1]-=10; // << fix me
21434 this.arrowEl.setXY(xy);
21438 return this.updatePosition('bottom', false);
21441 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21442 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21443 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21444 //normal display... or moved up/down.
21445 this.el.setXY(offset);
21446 var xy = this.alignEl.getAnchorXY('b', false);
21447 xy[1]+=2; // << fix me
21448 this.arrowEl.setXY(xy);
21452 return this.updatePosition('top', false);
21463 this.el.setXY([0,0]);
21464 this.el.removeClass('in');
21466 this.hoverState = null;
21467 this.maskEl.hide(); // always..
21468 this.fireEvent('hide', this);
21474 Roo.apply(Roo.bootstrap.Popover, {
21477 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21478 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21479 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21480 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21485 clickHander : false,
21489 onMouseDown : function(e)
21491 if (this.popups.length && !e.getTarget(".roo-popover")) {
21492 /// what is nothing is showing..
21501 register : function(popup)
21503 if (!Roo.bootstrap.Popover.clickHandler) {
21504 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21506 // hide other popups.
21507 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21508 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21509 this.hideAll(); //<< why?
21510 //this.popups.push(popup);
21512 hideAll : function()
21514 this.popups.forEach(function(p) {
21518 onShow : function() {
21519 Roo.bootstrap.Popover.popups.push(this);
21521 onHide : function() {
21522 Roo.bootstrap.Popover.popups.remove(this);
21528 * Card header - holder for the card header elements.
21533 * @class Roo.bootstrap.PopoverNav
21534 * @extends Roo.bootstrap.NavGroup
21535 * Bootstrap Popover header navigation class
21537 * Create a new Popover Header Navigation
21538 * @param {Object} config The config object
21541 Roo.bootstrap.PopoverNav = function(config){
21542 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21545 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21548 container_method : 'getPopoverHeader'
21566 * @class Roo.bootstrap.Progress
21567 * @extends Roo.bootstrap.Component
21568 * Bootstrap Progress class
21569 * @cfg {Boolean} striped striped of the progress bar
21570 * @cfg {Boolean} active animated of the progress bar
21574 * Create a new Progress
21575 * @param {Object} config The config object
21578 Roo.bootstrap.Progress = function(config){
21579 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21582 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21587 getAutoCreate : function(){
21595 cfg.cls += ' progress-striped';
21599 cfg.cls += ' active';
21618 * @class Roo.bootstrap.ProgressBar
21619 * @extends Roo.bootstrap.Component
21620 * Bootstrap ProgressBar class
21621 * @cfg {Number} aria_valuenow aria-value now
21622 * @cfg {Number} aria_valuemin aria-value min
21623 * @cfg {Number} aria_valuemax aria-value max
21624 * @cfg {String} label label for the progress bar
21625 * @cfg {String} panel (success | info | warning | danger )
21626 * @cfg {String} role role of the progress bar
21627 * @cfg {String} sr_only text
21631 * Create a new ProgressBar
21632 * @param {Object} config The config object
21635 Roo.bootstrap.ProgressBar = function(config){
21636 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21639 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21643 aria_valuemax : 100,
21649 getAutoCreate : function()
21654 cls: 'progress-bar',
21655 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21667 cfg.role = this.role;
21670 if(this.aria_valuenow){
21671 cfg['aria-valuenow'] = this.aria_valuenow;
21674 if(this.aria_valuemin){
21675 cfg['aria-valuemin'] = this.aria_valuemin;
21678 if(this.aria_valuemax){
21679 cfg['aria-valuemax'] = this.aria_valuemax;
21682 if(this.label && !this.sr_only){
21683 cfg.html = this.label;
21687 cfg.cls += ' progress-bar-' + this.panel;
21693 update : function(aria_valuenow)
21695 this.aria_valuenow = aria_valuenow;
21697 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21712 * @class Roo.bootstrap.TabGroup
21713 * @extends Roo.bootstrap.Column
21714 * Bootstrap Column class
21715 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21716 * @cfg {Boolean} carousel true to make the group behave like a carousel
21717 * @cfg {Boolean} bullets show bullets for the panels
21718 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21719 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21720 * @cfg {Boolean} showarrow (true|false) show arrow default true
21723 * Create a new TabGroup
21724 * @param {Object} config The config object
21727 Roo.bootstrap.TabGroup = function(config){
21728 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21730 this.navId = Roo.id();
21733 Roo.bootstrap.TabGroup.register(this);
21737 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21740 transition : false,
21745 slideOnTouch : false,
21748 getAutoCreate : function()
21750 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21752 cfg.cls += ' tab-content';
21754 if (this.carousel) {
21755 cfg.cls += ' carousel slide';
21758 cls : 'carousel-inner',
21762 if(this.bullets && !Roo.isTouch){
21765 cls : 'carousel-bullets',
21769 if(this.bullets_cls){
21770 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21777 cfg.cn[0].cn.push(bullets);
21780 if(this.showarrow){
21781 cfg.cn[0].cn.push({
21783 class : 'carousel-arrow',
21787 class : 'carousel-prev',
21791 class : 'fa fa-chevron-left'
21797 class : 'carousel-next',
21801 class : 'fa fa-chevron-right'
21814 initEvents: function()
21816 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21817 // this.el.on("touchstart", this.onTouchStart, this);
21820 if(this.autoslide){
21823 this.slideFn = window.setInterval(function() {
21824 _this.showPanelNext();
21828 if(this.showarrow){
21829 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21830 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21836 // onTouchStart : function(e, el, o)
21838 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21842 // this.showPanelNext();
21846 getChildContainer : function()
21848 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21852 * register a Navigation item
21853 * @param {Roo.bootstrap.NavItem} the navitem to add
21855 register : function(item)
21857 this.tabs.push( item);
21858 item.navId = this.navId; // not really needed..
21863 getActivePanel : function()
21866 Roo.each(this.tabs, function(t) {
21876 getPanelByName : function(n)
21879 Roo.each(this.tabs, function(t) {
21880 if (t.tabId == n) {
21888 indexOfPanel : function(p)
21891 Roo.each(this.tabs, function(t,i) {
21892 if (t.tabId == p.tabId) {
21901 * show a specific panel
21902 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21903 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21905 showPanel : function (pan)
21907 if(this.transition || typeof(pan) == 'undefined'){
21908 Roo.log("waiting for the transitionend");
21912 if (typeof(pan) == 'number') {
21913 pan = this.tabs[pan];
21916 if (typeof(pan) == 'string') {
21917 pan = this.getPanelByName(pan);
21920 var cur = this.getActivePanel();
21923 Roo.log('pan or acitve pan is undefined');
21927 if (pan.tabId == this.getActivePanel().tabId) {
21931 if (false === cur.fireEvent('beforedeactivate')) {
21935 if(this.bullets > 0 && !Roo.isTouch){
21936 this.setActiveBullet(this.indexOfPanel(pan));
21939 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21941 //class="carousel-item carousel-item-next carousel-item-left"
21943 this.transition = true;
21944 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21945 var lr = dir == 'next' ? 'left' : 'right';
21946 pan.el.addClass(dir); // or prev
21947 pan.el.addClass('carousel-item-' + dir); // or prev
21948 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21949 cur.el.addClass(lr); // or right
21950 pan.el.addClass(lr);
21951 cur.el.addClass('carousel-item-' +lr); // or right
21952 pan.el.addClass('carousel-item-' +lr);
21956 cur.el.on('transitionend', function() {
21957 Roo.log("trans end?");
21959 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21960 pan.setActive(true);
21962 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21963 cur.setActive(false);
21965 _this.transition = false;
21967 }, this, { single: true } );
21972 cur.setActive(false);
21973 pan.setActive(true);
21978 showPanelNext : function()
21980 var i = this.indexOfPanel(this.getActivePanel());
21982 if (i >= this.tabs.length - 1 && !this.autoslide) {
21986 if (i >= this.tabs.length - 1 && this.autoslide) {
21990 this.showPanel(this.tabs[i+1]);
21993 showPanelPrev : function()
21995 var i = this.indexOfPanel(this.getActivePanel());
21997 if (i < 1 && !this.autoslide) {
22001 if (i < 1 && this.autoslide) {
22002 i = this.tabs.length;
22005 this.showPanel(this.tabs[i-1]);
22009 addBullet: function()
22011 if(!this.bullets || Roo.isTouch){
22014 var ctr = this.el.select('.carousel-bullets',true).first();
22015 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22016 var bullet = ctr.createChild({
22017 cls : 'bullet bullet-' + i
22018 },ctr.dom.lastChild);
22023 bullet.on('click', (function(e, el, o, ii, t){
22025 e.preventDefault();
22027 this.showPanel(ii);
22029 if(this.autoslide && this.slideFn){
22030 clearInterval(this.slideFn);
22031 this.slideFn = window.setInterval(function() {
22032 _this.showPanelNext();
22036 }).createDelegate(this, [i, bullet], true));
22041 setActiveBullet : function(i)
22047 Roo.each(this.el.select('.bullet', true).elements, function(el){
22048 el.removeClass('selected');
22051 var bullet = this.el.select('.bullet-' + i, true).first();
22057 bullet.addClass('selected');
22068 Roo.apply(Roo.bootstrap.TabGroup, {
22072 * register a Navigation Group
22073 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22075 register : function(navgrp)
22077 this.groups[navgrp.navId] = navgrp;
22081 * fetch a Navigation Group based on the navigation ID
22082 * if one does not exist , it will get created.
22083 * @param {string} the navgroup to add
22084 * @returns {Roo.bootstrap.NavGroup} the navgroup
22086 get: function(navId) {
22087 if (typeof(this.groups[navId]) == 'undefined') {
22088 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22090 return this.groups[navId] ;
22105 * @class Roo.bootstrap.TabPanel
22106 * @extends Roo.bootstrap.Component
22107 * Bootstrap TabPanel class
22108 * @cfg {Boolean} active panel active
22109 * @cfg {String} html panel content
22110 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22111 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22112 * @cfg {String} href click to link..
22113 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22117 * Create a new TabPanel
22118 * @param {Object} config The config object
22121 Roo.bootstrap.TabPanel = function(config){
22122 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22126 * Fires when the active status changes
22127 * @param {Roo.bootstrap.TabPanel} this
22128 * @param {Boolean} state the new state
22133 * @event beforedeactivate
22134 * Fires before a tab is de-activated - can be used to do validation on a form.
22135 * @param {Roo.bootstrap.TabPanel} this
22136 * @return {Boolean} false if there is an error
22139 'beforedeactivate': true
22142 this.tabId = this.tabId || Roo.id();
22146 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22153 touchSlide : false,
22154 getAutoCreate : function(){
22159 // item is needed for carousel - not sure if it has any effect otherwise
22160 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22161 html: this.html || ''
22165 cfg.cls += ' active';
22169 cfg.tabId = this.tabId;
22177 initEvents: function()
22179 var p = this.parent();
22181 this.navId = this.navId || p.navId;
22183 if (typeof(this.navId) != 'undefined') {
22184 // not really needed.. but just in case.. parent should be a NavGroup.
22185 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22189 var i = tg.tabs.length - 1;
22191 if(this.active && tg.bullets > 0 && i < tg.bullets){
22192 tg.setActiveBullet(i);
22196 this.el.on('click', this.onClick, this);
22198 if(Roo.isTouch && this.touchSlide){
22199 this.el.on("touchstart", this.onTouchStart, this);
22200 this.el.on("touchmove", this.onTouchMove, this);
22201 this.el.on("touchend", this.onTouchEnd, this);
22206 onRender : function(ct, position)
22208 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22211 setActive : function(state)
22213 Roo.log("panel - set active " + this.tabId + "=" + state);
22215 this.active = state;
22217 this.el.removeClass('active');
22219 } else if (!this.el.hasClass('active')) {
22220 this.el.addClass('active');
22223 this.fireEvent('changed', this, state);
22226 onClick : function(e)
22228 e.preventDefault();
22230 if(!this.href.length){
22234 window.location.href = this.href;
22243 onTouchStart : function(e)
22245 this.swiping = false;
22247 this.startX = e.browserEvent.touches[0].clientX;
22248 this.startY = e.browserEvent.touches[0].clientY;
22251 onTouchMove : function(e)
22253 this.swiping = true;
22255 this.endX = e.browserEvent.touches[0].clientX;
22256 this.endY = e.browserEvent.touches[0].clientY;
22259 onTouchEnd : function(e)
22266 var tabGroup = this.parent();
22268 if(this.endX > this.startX){ // swiping right
22269 tabGroup.showPanelPrev();
22273 if(this.startX > this.endX){ // swiping left
22274 tabGroup.showPanelNext();
22293 * @class Roo.bootstrap.DateField
22294 * @extends Roo.bootstrap.Input
22295 * Bootstrap DateField class
22296 * @cfg {Number} weekStart default 0
22297 * @cfg {String} viewMode default empty, (months|years)
22298 * @cfg {String} minViewMode default empty, (months|years)
22299 * @cfg {Number} startDate default -Infinity
22300 * @cfg {Number} endDate default Infinity
22301 * @cfg {Boolean} todayHighlight default false
22302 * @cfg {Boolean} todayBtn default false
22303 * @cfg {Boolean} calendarWeeks default false
22304 * @cfg {Object} daysOfWeekDisabled default empty
22305 * @cfg {Boolean} singleMode default false (true | false)
22307 * @cfg {Boolean} keyboardNavigation default true
22308 * @cfg {String} language default en
22311 * Create a new DateField
22312 * @param {Object} config The config object
22315 Roo.bootstrap.DateField = function(config){
22316 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22320 * Fires when this field show.
22321 * @param {Roo.bootstrap.DateField} this
22322 * @param {Mixed} date The date value
22327 * Fires when this field hide.
22328 * @param {Roo.bootstrap.DateField} this
22329 * @param {Mixed} date The date value
22334 * Fires when select a date.
22335 * @param {Roo.bootstrap.DateField} this
22336 * @param {Mixed} date The date value
22340 * @event beforeselect
22341 * Fires when before select a date.
22342 * @param {Roo.bootstrap.DateField} this
22343 * @param {Mixed} date The date value
22345 beforeselect : true
22349 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22352 * @cfg {String} format
22353 * The default date format string which can be overriden for localization support. The format must be
22354 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22358 * @cfg {String} altFormats
22359 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22360 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22362 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22370 todayHighlight : false,
22376 keyboardNavigation: true,
22378 calendarWeeks: false,
22380 startDate: -Infinity,
22384 daysOfWeekDisabled: [],
22388 singleMode : false,
22390 UTCDate: function()
22392 return new Date(Date.UTC.apply(Date, arguments));
22395 UTCToday: function()
22397 var today = new Date();
22398 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22401 getDate: function() {
22402 var d = this.getUTCDate();
22403 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22406 getUTCDate: function() {
22410 setDate: function(d) {
22411 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22414 setUTCDate: function(d) {
22416 this.setValue(this.formatDate(this.date));
22419 onRender: function(ct, position)
22422 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22424 this.language = this.language || 'en';
22425 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22426 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22428 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22429 this.format = this.format || 'm/d/y';
22430 this.isInline = false;
22431 this.isInput = true;
22432 this.component = this.el.select('.add-on', true).first() || false;
22433 this.component = (this.component && this.component.length === 0) ? false : this.component;
22434 this.hasInput = this.component && this.inputEl().length;
22436 if (typeof(this.minViewMode === 'string')) {
22437 switch (this.minViewMode) {
22439 this.minViewMode = 1;
22442 this.minViewMode = 2;
22445 this.minViewMode = 0;
22450 if (typeof(this.viewMode === 'string')) {
22451 switch (this.viewMode) {
22464 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22466 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22468 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22470 this.picker().on('mousedown', this.onMousedown, this);
22471 this.picker().on('click', this.onClick, this);
22473 this.picker().addClass('datepicker-dropdown');
22475 this.startViewMode = this.viewMode;
22477 if(this.singleMode){
22478 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22479 v.setVisibilityMode(Roo.Element.DISPLAY);
22483 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22484 v.setStyle('width', '189px');
22488 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22489 if(!this.calendarWeeks){
22494 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22495 v.attr('colspan', function(i, val){
22496 return parseInt(val) + 1;
22501 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22503 this.setStartDate(this.startDate);
22504 this.setEndDate(this.endDate);
22506 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22513 if(this.isInline) {
22518 picker : function()
22520 return this.pickerEl;
22521 // return this.el.select('.datepicker', true).first();
22524 fillDow: function()
22526 var dowCnt = this.weekStart;
22535 if(this.calendarWeeks){
22543 while (dowCnt < this.weekStart + 7) {
22547 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22551 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22554 fillMonths: function()
22557 var months = this.picker().select('>.datepicker-months td', true).first();
22559 months.dom.innerHTML = '';
22565 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22568 months.createChild(month);
22575 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;
22577 if (this.date < this.startDate) {
22578 this.viewDate = new Date(this.startDate);
22579 } else if (this.date > this.endDate) {
22580 this.viewDate = new Date(this.endDate);
22582 this.viewDate = new Date(this.date);
22590 var d = new Date(this.viewDate),
22591 year = d.getUTCFullYear(),
22592 month = d.getUTCMonth(),
22593 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22594 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22595 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22596 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22597 currentDate = this.date && this.date.valueOf(),
22598 today = this.UTCToday();
22600 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22602 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22604 // this.picker.select('>tfoot th.today').
22605 // .text(dates[this.language].today)
22606 // .toggle(this.todayBtn !== false);
22608 this.updateNavArrows();
22611 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22613 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22615 prevMonth.setUTCDate(day);
22617 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22619 var nextMonth = new Date(prevMonth);
22621 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22623 nextMonth = nextMonth.valueOf();
22625 var fillMonths = false;
22627 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22629 while(prevMonth.valueOf() <= nextMonth) {
22632 if (prevMonth.getUTCDay() === this.weekStart) {
22634 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22642 if(this.calendarWeeks){
22643 // ISO 8601: First week contains first thursday.
22644 // ISO also states week starts on Monday, but we can be more abstract here.
22646 // Start of current week: based on weekstart/current date
22647 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22648 // Thursday of this week
22649 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22650 // First Thursday of year, year from thursday
22651 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22652 // Calendar week: ms between thursdays, div ms per day, div 7 days
22653 calWeek = (th - yth) / 864e5 / 7 + 1;
22655 fillMonths.cn.push({
22663 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22665 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22668 if (this.todayHighlight &&
22669 prevMonth.getUTCFullYear() == today.getFullYear() &&
22670 prevMonth.getUTCMonth() == today.getMonth() &&
22671 prevMonth.getUTCDate() == today.getDate()) {
22672 clsName += ' today';
22675 if (currentDate && prevMonth.valueOf() === currentDate) {
22676 clsName += ' active';
22679 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22680 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22681 clsName += ' disabled';
22684 fillMonths.cn.push({
22686 cls: 'day ' + clsName,
22687 html: prevMonth.getDate()
22690 prevMonth.setDate(prevMonth.getDate()+1);
22693 var currentYear = this.date && this.date.getUTCFullYear();
22694 var currentMonth = this.date && this.date.getUTCMonth();
22696 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22698 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22699 v.removeClass('active');
22701 if(currentYear === year && k === currentMonth){
22702 v.addClass('active');
22705 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22706 v.addClass('disabled');
22712 year = parseInt(year/10, 10) * 10;
22714 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22716 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22719 for (var i = -1; i < 11; i++) {
22720 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22722 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22730 showMode: function(dir)
22733 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22736 Roo.each(this.picker().select('>div',true).elements, function(v){
22737 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22740 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22745 if(this.isInline) {
22749 this.picker().removeClass(['bottom', 'top']);
22751 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22753 * place to the top of element!
22757 this.picker().addClass('top');
22758 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22763 this.picker().addClass('bottom');
22765 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22768 parseDate : function(value)
22770 if(!value || value instanceof Date){
22773 var v = Date.parseDate(value, this.format);
22774 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22775 v = Date.parseDate(value, 'Y-m-d');
22777 if(!v && this.altFormats){
22778 if(!this.altFormatsArray){
22779 this.altFormatsArray = this.altFormats.split("|");
22781 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22782 v = Date.parseDate(value, this.altFormatsArray[i]);
22788 formatDate : function(date, fmt)
22790 return (!date || !(date instanceof Date)) ?
22791 date : date.dateFormat(fmt || this.format);
22794 onFocus : function()
22796 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22800 onBlur : function()
22802 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22804 var d = this.inputEl().getValue();
22811 showPopup : function()
22813 this.picker().show();
22817 this.fireEvent('showpopup', this, this.date);
22820 hidePopup : function()
22822 if(this.isInline) {
22825 this.picker().hide();
22826 this.viewMode = this.startViewMode;
22829 this.fireEvent('hidepopup', this, this.date);
22833 onMousedown: function(e)
22835 e.stopPropagation();
22836 e.preventDefault();
22841 Roo.bootstrap.DateField.superclass.keyup.call(this);
22845 setValue: function(v)
22847 if(this.fireEvent('beforeselect', this, v) !== false){
22848 var d = new Date(this.parseDate(v) ).clearTime();
22850 if(isNaN(d.getTime())){
22851 this.date = this.viewDate = '';
22852 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22856 v = this.formatDate(d);
22858 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22860 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22864 this.fireEvent('select', this, this.date);
22868 getValue: function()
22870 return this.formatDate(this.date);
22873 fireKey: function(e)
22875 if (!this.picker().isVisible()){
22876 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22882 var dateChanged = false,
22884 newDate, newViewDate;
22889 e.preventDefault();
22893 if (!this.keyboardNavigation) {
22896 dir = e.keyCode == 37 ? -1 : 1;
22899 newDate = this.moveYear(this.date, dir);
22900 newViewDate = this.moveYear(this.viewDate, dir);
22901 } else if (e.shiftKey){
22902 newDate = this.moveMonth(this.date, dir);
22903 newViewDate = this.moveMonth(this.viewDate, dir);
22905 newDate = new Date(this.date);
22906 newDate.setUTCDate(this.date.getUTCDate() + dir);
22907 newViewDate = new Date(this.viewDate);
22908 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22910 if (this.dateWithinRange(newDate)){
22911 this.date = newDate;
22912 this.viewDate = newViewDate;
22913 this.setValue(this.formatDate(this.date));
22915 e.preventDefault();
22916 dateChanged = true;
22921 if (!this.keyboardNavigation) {
22924 dir = e.keyCode == 38 ? -1 : 1;
22926 newDate = this.moveYear(this.date, dir);
22927 newViewDate = this.moveYear(this.viewDate, dir);
22928 } else if (e.shiftKey){
22929 newDate = this.moveMonth(this.date, dir);
22930 newViewDate = this.moveMonth(this.viewDate, dir);
22932 newDate = new Date(this.date);
22933 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22934 newViewDate = new Date(this.viewDate);
22935 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22937 if (this.dateWithinRange(newDate)){
22938 this.date = newDate;
22939 this.viewDate = newViewDate;
22940 this.setValue(this.formatDate(this.date));
22942 e.preventDefault();
22943 dateChanged = true;
22947 this.setValue(this.formatDate(this.date));
22949 e.preventDefault();
22952 this.setValue(this.formatDate(this.date));
22966 onClick: function(e)
22968 e.stopPropagation();
22969 e.preventDefault();
22971 var target = e.getTarget();
22973 if(target.nodeName.toLowerCase() === 'i'){
22974 target = Roo.get(target).dom.parentNode;
22977 var nodeName = target.nodeName;
22978 var className = target.className;
22979 var html = target.innerHTML;
22980 //Roo.log(nodeName);
22982 switch(nodeName.toLowerCase()) {
22984 switch(className) {
22990 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22991 switch(this.viewMode){
22993 this.viewDate = this.moveMonth(this.viewDate, dir);
22997 this.viewDate = this.moveYear(this.viewDate, dir);
23003 var date = new Date();
23004 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23006 this.setValue(this.formatDate(this.date));
23013 if (className.indexOf('disabled') < 0) {
23014 if (!this.viewDate) {
23015 this.viewDate = new Date();
23017 this.viewDate.setUTCDate(1);
23018 if (className.indexOf('month') > -1) {
23019 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23021 var year = parseInt(html, 10) || 0;
23022 this.viewDate.setUTCFullYear(year);
23026 if(this.singleMode){
23027 this.setValue(this.formatDate(this.viewDate));
23038 //Roo.log(className);
23039 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23040 var day = parseInt(html, 10) || 1;
23041 var year = (this.viewDate || new Date()).getUTCFullYear(),
23042 month = (this.viewDate || new Date()).getUTCMonth();
23044 if (className.indexOf('old') > -1) {
23051 } else if (className.indexOf('new') > -1) {
23059 //Roo.log([year,month,day]);
23060 this.date = this.UTCDate(year, month, day,0,0,0,0);
23061 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23063 //Roo.log(this.formatDate(this.date));
23064 this.setValue(this.formatDate(this.date));
23071 setStartDate: function(startDate)
23073 this.startDate = startDate || -Infinity;
23074 if (this.startDate !== -Infinity) {
23075 this.startDate = this.parseDate(this.startDate);
23078 this.updateNavArrows();
23081 setEndDate: function(endDate)
23083 this.endDate = endDate || Infinity;
23084 if (this.endDate !== Infinity) {
23085 this.endDate = this.parseDate(this.endDate);
23088 this.updateNavArrows();
23091 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23093 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23094 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23095 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23097 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23098 return parseInt(d, 10);
23101 this.updateNavArrows();
23104 updateNavArrows: function()
23106 if(this.singleMode){
23110 var d = new Date(this.viewDate),
23111 year = d.getUTCFullYear(),
23112 month = d.getUTCMonth();
23114 Roo.each(this.picker().select('.prev', true).elements, function(v){
23116 switch (this.viewMode) {
23119 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23125 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23132 Roo.each(this.picker().select('.next', true).elements, function(v){
23134 switch (this.viewMode) {
23137 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23143 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23151 moveMonth: function(date, dir)
23156 var new_date = new Date(date.valueOf()),
23157 day = new_date.getUTCDate(),
23158 month = new_date.getUTCMonth(),
23159 mag = Math.abs(dir),
23161 dir = dir > 0 ? 1 : -1;
23164 // If going back one month, make sure month is not current month
23165 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23167 return new_date.getUTCMonth() == month;
23169 // If going forward one month, make sure month is as expected
23170 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23172 return new_date.getUTCMonth() != new_month;
23174 new_month = month + dir;
23175 new_date.setUTCMonth(new_month);
23176 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23177 if (new_month < 0 || new_month > 11) {
23178 new_month = (new_month + 12) % 12;
23181 // For magnitudes >1, move one month at a time...
23182 for (var i=0; i<mag; i++) {
23183 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23184 new_date = this.moveMonth(new_date, dir);
23186 // ...then reset the day, keeping it in the new month
23187 new_month = new_date.getUTCMonth();
23188 new_date.setUTCDate(day);
23190 return new_month != new_date.getUTCMonth();
23193 // Common date-resetting loop -- if date is beyond end of month, make it
23196 new_date.setUTCDate(--day);
23197 new_date.setUTCMonth(new_month);
23202 moveYear: function(date, dir)
23204 return this.moveMonth(date, dir*12);
23207 dateWithinRange: function(date)
23209 return date >= this.startDate && date <= this.endDate;
23215 this.picker().remove();
23218 validateValue : function(value)
23220 if(this.getVisibilityEl().hasClass('hidden')){
23224 if(value.length < 1) {
23225 if(this.allowBlank){
23231 if(value.length < this.minLength){
23234 if(value.length > this.maxLength){
23238 var vt = Roo.form.VTypes;
23239 if(!vt[this.vtype](value, this)){
23243 if(typeof this.validator == "function"){
23244 var msg = this.validator(value);
23250 if(this.regex && !this.regex.test(value)){
23254 if(typeof(this.parseDate(value)) == 'undefined'){
23258 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23262 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23272 this.date = this.viewDate = '';
23274 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23279 Roo.apply(Roo.bootstrap.DateField, {
23290 html: '<i class="fa fa-arrow-left"/>'
23300 html: '<i class="fa fa-arrow-right"/>'
23342 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23343 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23344 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23345 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23346 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23359 navFnc: 'FullYear',
23364 navFnc: 'FullYear',
23369 Roo.apply(Roo.bootstrap.DateField, {
23373 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23377 cls: 'datepicker-days',
23381 cls: 'table-condensed',
23383 Roo.bootstrap.DateField.head,
23387 Roo.bootstrap.DateField.footer
23394 cls: 'datepicker-months',
23398 cls: 'table-condensed',
23400 Roo.bootstrap.DateField.head,
23401 Roo.bootstrap.DateField.content,
23402 Roo.bootstrap.DateField.footer
23409 cls: 'datepicker-years',
23413 cls: 'table-condensed',
23415 Roo.bootstrap.DateField.head,
23416 Roo.bootstrap.DateField.content,
23417 Roo.bootstrap.DateField.footer
23436 * @class Roo.bootstrap.TimeField
23437 * @extends Roo.bootstrap.Input
23438 * Bootstrap DateField class
23442 * Create a new TimeField
23443 * @param {Object} config The config object
23446 Roo.bootstrap.TimeField = function(config){
23447 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23451 * Fires when this field show.
23452 * @param {Roo.bootstrap.DateField} thisthis
23453 * @param {Mixed} date The date value
23458 * Fires when this field hide.
23459 * @param {Roo.bootstrap.DateField} this
23460 * @param {Mixed} date The date value
23465 * Fires when select a date.
23466 * @param {Roo.bootstrap.DateField} this
23467 * @param {Mixed} date The date value
23473 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23476 * @cfg {String} format
23477 * The default time format string which can be overriden for localization support. The format must be
23478 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23482 getAutoCreate : function()
23484 this.after = '<i class="fa far fa-clock"></i>';
23485 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23489 onRender: function(ct, position)
23492 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23494 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23496 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23498 this.pop = this.picker().select('>.datepicker-time',true).first();
23499 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23501 this.picker().on('mousedown', this.onMousedown, this);
23502 this.picker().on('click', this.onClick, this);
23504 this.picker().addClass('datepicker-dropdown');
23509 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23510 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23511 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23512 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23513 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23514 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23518 fireKey: function(e){
23519 if (!this.picker().isVisible()){
23520 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23526 e.preventDefault();
23534 this.onTogglePeriod();
23537 this.onIncrementMinutes();
23540 this.onDecrementMinutes();
23549 onClick: function(e) {
23550 e.stopPropagation();
23551 e.preventDefault();
23554 picker : function()
23556 return this.pickerEl;
23559 fillTime: function()
23561 var time = this.pop.select('tbody', true).first();
23563 time.dom.innerHTML = '';
23578 cls: 'hours-up fa fas fa-chevron-up'
23598 cls: 'minutes-up fa fas fa-chevron-up'
23619 cls: 'timepicker-hour',
23634 cls: 'timepicker-minute',
23649 cls: 'btn btn-primary period',
23671 cls: 'hours-down fa fas fa-chevron-down'
23691 cls: 'minutes-down fa fas fa-chevron-down'
23709 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23716 var hours = this.time.getHours();
23717 var minutes = this.time.getMinutes();
23730 hours = hours - 12;
23734 hours = '0' + hours;
23738 minutes = '0' + minutes;
23741 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23742 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23743 this.pop.select('button', true).first().dom.innerHTML = period;
23749 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23751 var cls = ['bottom'];
23753 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23760 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23764 //this.picker().setXY(20000,20000);
23765 this.picker().addClass(cls.join('-'));
23769 Roo.each(cls, function(c){
23774 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23775 //_this.picker().setTop(_this.inputEl().getHeight());
23779 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23781 //_this.picker().setTop(0 - _this.picker().getHeight());
23786 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23790 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23798 onFocus : function()
23800 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23804 onBlur : function()
23806 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23812 this.picker().show();
23817 this.fireEvent('show', this, this.date);
23822 this.picker().hide();
23825 this.fireEvent('hide', this, this.date);
23828 setTime : function()
23831 this.setValue(this.time.format(this.format));
23833 this.fireEvent('select', this, this.date);
23838 onMousedown: function(e){
23839 e.stopPropagation();
23840 e.preventDefault();
23843 onIncrementHours: function()
23845 Roo.log('onIncrementHours');
23846 this.time = this.time.add(Date.HOUR, 1);
23851 onDecrementHours: function()
23853 Roo.log('onDecrementHours');
23854 this.time = this.time.add(Date.HOUR, -1);
23858 onIncrementMinutes: function()
23860 Roo.log('onIncrementMinutes');
23861 this.time = this.time.add(Date.MINUTE, 1);
23865 onDecrementMinutes: function()
23867 Roo.log('onDecrementMinutes');
23868 this.time = this.time.add(Date.MINUTE, -1);
23872 onTogglePeriod: function()
23874 Roo.log('onTogglePeriod');
23875 this.time = this.time.add(Date.HOUR, 12);
23883 Roo.apply(Roo.bootstrap.TimeField, {
23887 cls: 'datepicker dropdown-menu',
23891 cls: 'datepicker-time',
23895 cls: 'table-condensed',
23924 cls: 'btn btn-info ok',
23952 * @class Roo.bootstrap.MonthField
23953 * @extends Roo.bootstrap.Input
23954 * Bootstrap MonthField class
23956 * @cfg {String} language default en
23959 * Create a new MonthField
23960 * @param {Object} config The config object
23963 Roo.bootstrap.MonthField = function(config){
23964 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23969 * Fires when this field show.
23970 * @param {Roo.bootstrap.MonthField} this
23971 * @param {Mixed} date The date value
23976 * Fires when this field hide.
23977 * @param {Roo.bootstrap.MonthField} this
23978 * @param {Mixed} date The date value
23983 * Fires when select a date.
23984 * @param {Roo.bootstrap.MonthField} this
23985 * @param {String} oldvalue The old value
23986 * @param {String} newvalue The new value
23992 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23994 onRender: function(ct, position)
23997 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23999 this.language = this.language || 'en';
24000 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24001 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24003 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24004 this.isInline = false;
24005 this.isInput = true;
24006 this.component = this.el.select('.add-on', true).first() || false;
24007 this.component = (this.component && this.component.length === 0) ? false : this.component;
24008 this.hasInput = this.component && this.inputEL().length;
24010 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24012 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24014 this.picker().on('mousedown', this.onMousedown, this);
24015 this.picker().on('click', this.onClick, this);
24017 this.picker().addClass('datepicker-dropdown');
24019 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24020 v.setStyle('width', '189px');
24027 if(this.isInline) {
24033 setValue: function(v, suppressEvent)
24035 var o = this.getValue();
24037 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24041 if(suppressEvent !== true){
24042 this.fireEvent('select', this, o, v);
24047 getValue: function()
24052 onClick: function(e)
24054 e.stopPropagation();
24055 e.preventDefault();
24057 var target = e.getTarget();
24059 if(target.nodeName.toLowerCase() === 'i'){
24060 target = Roo.get(target).dom.parentNode;
24063 var nodeName = target.nodeName;
24064 var className = target.className;
24065 var html = target.innerHTML;
24067 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24071 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24073 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24079 picker : function()
24081 return this.pickerEl;
24084 fillMonths: function()
24087 var months = this.picker().select('>.datepicker-months td', true).first();
24089 months.dom.innerHTML = '';
24095 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24098 months.createChild(month);
24107 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24108 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24111 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24112 e.removeClass('active');
24114 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24115 e.addClass('active');
24122 if(this.isInline) {
24126 this.picker().removeClass(['bottom', 'top']);
24128 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24130 * place to the top of element!
24134 this.picker().addClass('top');
24135 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24140 this.picker().addClass('bottom');
24142 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24145 onFocus : function()
24147 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24151 onBlur : function()
24153 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24155 var d = this.inputEl().getValue();
24164 this.picker().show();
24165 this.picker().select('>.datepicker-months', true).first().show();
24169 this.fireEvent('show', this, this.date);
24174 if(this.isInline) {
24177 this.picker().hide();
24178 this.fireEvent('hide', this, this.date);
24182 onMousedown: function(e)
24184 e.stopPropagation();
24185 e.preventDefault();
24190 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24194 fireKey: function(e)
24196 if (!this.picker().isVisible()){
24197 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24208 e.preventDefault();
24212 dir = e.keyCode == 37 ? -1 : 1;
24214 this.vIndex = this.vIndex + dir;
24216 if(this.vIndex < 0){
24220 if(this.vIndex > 11){
24224 if(isNaN(this.vIndex)){
24228 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24234 dir = e.keyCode == 38 ? -1 : 1;
24236 this.vIndex = this.vIndex + dir * 4;
24238 if(this.vIndex < 0){
24242 if(this.vIndex > 11){
24246 if(isNaN(this.vIndex)){
24250 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24255 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24256 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24260 e.preventDefault();
24263 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24264 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24280 this.picker().remove();
24285 Roo.apply(Roo.bootstrap.MonthField, {
24304 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24305 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24310 Roo.apply(Roo.bootstrap.MonthField, {
24314 cls: 'datepicker dropdown-menu roo-dynamic',
24318 cls: 'datepicker-months',
24322 cls: 'table-condensed',
24324 Roo.bootstrap.DateField.content
24344 * @class Roo.bootstrap.CheckBox
24345 * @extends Roo.bootstrap.Input
24346 * Bootstrap CheckBox class
24348 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24349 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24350 * @cfg {String} boxLabel The text that appears beside the checkbox
24351 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24352 * @cfg {Boolean} checked initnal the element
24353 * @cfg {Boolean} inline inline the element (default false)
24354 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24355 * @cfg {String} tooltip label tooltip
24358 * Create a new CheckBox
24359 * @param {Object} config The config object
24362 Roo.bootstrap.CheckBox = function(config){
24363 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24368 * Fires when the element is checked or unchecked.
24369 * @param {Roo.bootstrap.CheckBox} this This input
24370 * @param {Boolean} checked The new checked value
24375 * Fires when the element is click.
24376 * @param {Roo.bootstrap.CheckBox} this This input
24383 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24385 inputType: 'checkbox',
24394 // checkbox success does not make any sense really..
24399 getAutoCreate : function()
24401 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24407 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24410 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24416 type : this.inputType,
24417 value : this.inputValue,
24418 cls : 'roo-' + this.inputType, //'form-box',
24419 placeholder : this.placeholder || ''
24423 if(this.inputType != 'radio'){
24427 cls : 'roo-hidden-value',
24428 value : this.checked ? this.inputValue : this.valueOff
24433 if (this.weight) { // Validity check?
24434 cfg.cls += " " + this.inputType + "-" + this.weight;
24437 if (this.disabled) {
24438 input.disabled=true;
24442 input.checked = this.checked;
24447 input.name = this.name;
24449 if(this.inputType != 'radio'){
24450 hidden.name = this.name;
24451 input.name = '_hidden_' + this.name;
24456 input.cls += ' input-' + this.size;
24461 ['xs','sm','md','lg'].map(function(size){
24462 if (settings[size]) {
24463 cfg.cls += ' col-' + size + '-' + settings[size];
24467 var inputblock = input;
24469 if (this.before || this.after) {
24472 cls : 'input-group',
24477 inputblock.cn.push({
24479 cls : 'input-group-addon',
24484 inputblock.cn.push(input);
24486 if(this.inputType != 'radio'){
24487 inputblock.cn.push(hidden);
24491 inputblock.cn.push({
24493 cls : 'input-group-addon',
24499 var boxLabelCfg = false;
24505 //'for': id, // box label is handled by onclick - so no for...
24507 html: this.boxLabel
24510 boxLabelCfg.tooltip = this.tooltip;
24516 if (align ==='left' && this.fieldLabel.length) {
24517 // Roo.log("left and has label");
24522 cls : 'control-label',
24523 html : this.fieldLabel
24534 cfg.cn[1].cn.push(boxLabelCfg);
24537 if(this.labelWidth > 12){
24538 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24541 if(this.labelWidth < 13 && this.labelmd == 0){
24542 this.labelmd = this.labelWidth;
24545 if(this.labellg > 0){
24546 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24547 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24550 if(this.labelmd > 0){
24551 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24552 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24555 if(this.labelsm > 0){
24556 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24557 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24560 if(this.labelxs > 0){
24561 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24562 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24565 } else if ( this.fieldLabel.length) {
24566 // Roo.log(" label");
24570 tag: this.boxLabel ? 'span' : 'label',
24572 cls: 'control-label box-input-label',
24573 //cls : 'input-group-addon',
24574 html : this.fieldLabel
24581 cfg.cn.push(boxLabelCfg);
24586 // Roo.log(" no label && no align");
24587 cfg.cn = [ inputblock ] ;
24589 cfg.cn.push(boxLabelCfg);
24597 if(this.inputType != 'radio'){
24598 cfg.cn.push(hidden);
24606 * return the real input element.
24608 inputEl: function ()
24610 return this.el.select('input.roo-' + this.inputType,true).first();
24612 hiddenEl: function ()
24614 return this.el.select('input.roo-hidden-value',true).first();
24617 labelEl: function()
24619 return this.el.select('label.control-label',true).first();
24621 /* depricated... */
24625 return this.labelEl();
24628 boxLabelEl: function()
24630 return this.el.select('label.box-label',true).first();
24633 initEvents : function()
24635 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24637 this.inputEl().on('click', this.onClick, this);
24639 if (this.boxLabel) {
24640 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24643 this.startValue = this.getValue();
24646 Roo.bootstrap.CheckBox.register(this);
24650 onClick : function(e)
24652 if(this.fireEvent('click', this, e) !== false){
24653 this.setChecked(!this.checked);
24658 setChecked : function(state,suppressEvent)
24660 this.startValue = this.getValue();
24662 if(this.inputType == 'radio'){
24664 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24665 e.dom.checked = false;
24668 this.inputEl().dom.checked = true;
24670 this.inputEl().dom.value = this.inputValue;
24672 if(suppressEvent !== true){
24673 this.fireEvent('check', this, true);
24681 this.checked = state;
24683 this.inputEl().dom.checked = state;
24686 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24688 if(suppressEvent !== true){
24689 this.fireEvent('check', this, state);
24695 getValue : function()
24697 if(this.inputType == 'radio'){
24698 return this.getGroupValue();
24701 return this.hiddenEl().dom.value;
24705 getGroupValue : function()
24707 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24711 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24714 setValue : function(v,suppressEvent)
24716 if(this.inputType == 'radio'){
24717 this.setGroupValue(v, suppressEvent);
24721 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24726 setGroupValue : function(v, suppressEvent)
24728 this.startValue = this.getValue();
24730 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24731 e.dom.checked = false;
24733 if(e.dom.value == v){
24734 e.dom.checked = true;
24738 if(suppressEvent !== true){
24739 this.fireEvent('check', this, true);
24747 validate : function()
24749 if(this.getVisibilityEl().hasClass('hidden')){
24755 (this.inputType == 'radio' && this.validateRadio()) ||
24756 (this.inputType == 'checkbox' && this.validateCheckbox())
24762 this.markInvalid();
24766 validateRadio : function()
24768 if(this.getVisibilityEl().hasClass('hidden')){
24772 if(this.allowBlank){
24778 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24779 if(!e.dom.checked){
24791 validateCheckbox : function()
24794 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24795 //return (this.getValue() == this.inputValue) ? true : false;
24798 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24806 for(var i in group){
24807 if(group[i].el.isVisible(true)){
24815 for(var i in group){
24820 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24827 * Mark this field as valid
24829 markValid : function()
24833 this.fireEvent('valid', this);
24835 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24838 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24845 if(this.inputType == 'radio'){
24846 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24847 var fg = e.findParent('.form-group', false, true);
24848 if (Roo.bootstrap.version == 3) {
24849 fg.removeClass([_this.invalidClass, _this.validClass]);
24850 fg.addClass(_this.validClass);
24852 fg.removeClass(['is-valid', 'is-invalid']);
24853 fg.addClass('is-valid');
24861 var fg = this.el.findParent('.form-group', false, true);
24862 if (Roo.bootstrap.version == 3) {
24863 fg.removeClass([this.invalidClass, this.validClass]);
24864 fg.addClass(this.validClass);
24866 fg.removeClass(['is-valid', 'is-invalid']);
24867 fg.addClass('is-valid');
24872 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24878 for(var i in group){
24879 var fg = group[i].el.findParent('.form-group', false, true);
24880 if (Roo.bootstrap.version == 3) {
24881 fg.removeClass([this.invalidClass, this.validClass]);
24882 fg.addClass(this.validClass);
24884 fg.removeClass(['is-valid', 'is-invalid']);
24885 fg.addClass('is-valid');
24891 * Mark this field as invalid
24892 * @param {String} msg The validation message
24894 markInvalid : function(msg)
24896 if(this.allowBlank){
24902 this.fireEvent('invalid', this, msg);
24904 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24907 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24911 label.markInvalid();
24914 if(this.inputType == 'radio'){
24916 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24917 var fg = e.findParent('.form-group', false, true);
24918 if (Roo.bootstrap.version == 3) {
24919 fg.removeClass([_this.invalidClass, _this.validClass]);
24920 fg.addClass(_this.invalidClass);
24922 fg.removeClass(['is-invalid', 'is-valid']);
24923 fg.addClass('is-invalid');
24931 var fg = this.el.findParent('.form-group', false, true);
24932 if (Roo.bootstrap.version == 3) {
24933 fg.removeClass([_this.invalidClass, _this.validClass]);
24934 fg.addClass(_this.invalidClass);
24936 fg.removeClass(['is-invalid', 'is-valid']);
24937 fg.addClass('is-invalid');
24942 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24948 for(var i in group){
24949 var fg = group[i].el.findParent('.form-group', false, true);
24950 if (Roo.bootstrap.version == 3) {
24951 fg.removeClass([_this.invalidClass, _this.validClass]);
24952 fg.addClass(_this.invalidClass);
24954 fg.removeClass(['is-invalid', 'is-valid']);
24955 fg.addClass('is-invalid');
24961 clearInvalid : function()
24963 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24965 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24967 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24969 if (label && label.iconEl) {
24970 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24971 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24975 disable : function()
24977 if(this.inputType != 'radio'){
24978 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24985 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24986 _this.getActionEl().addClass(this.disabledClass);
24987 e.dom.disabled = true;
24991 this.disabled = true;
24992 this.fireEvent("disable", this);
24996 enable : function()
24998 if(this.inputType != 'radio'){
24999 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25006 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25007 _this.getActionEl().removeClass(this.disabledClass);
25008 e.dom.disabled = false;
25012 this.disabled = false;
25013 this.fireEvent("enable", this);
25017 setBoxLabel : function(v)
25022 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25028 Roo.apply(Roo.bootstrap.CheckBox, {
25033 * register a CheckBox Group
25034 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25036 register : function(checkbox)
25038 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25039 this.groups[checkbox.groupId] = {};
25042 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25046 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25050 * fetch a CheckBox Group based on the group ID
25051 * @param {string} the group ID
25052 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25054 get: function(groupId) {
25055 if (typeof(this.groups[groupId]) == 'undefined') {
25059 return this.groups[groupId] ;
25072 * @class Roo.bootstrap.Radio
25073 * @extends Roo.bootstrap.Component
25074 * Bootstrap Radio class
25075 * @cfg {String} boxLabel - the label associated
25076 * @cfg {String} value - the value of radio
25079 * Create a new Radio
25080 * @param {Object} config The config object
25082 Roo.bootstrap.Radio = function(config){
25083 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25087 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25093 getAutoCreate : function()
25097 cls : 'form-group radio',
25102 html : this.boxLabel
25110 initEvents : function()
25112 this.parent().register(this);
25114 this.el.on('click', this.onClick, this);
25118 onClick : function(e)
25120 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25121 this.setChecked(true);
25125 setChecked : function(state, suppressEvent)
25127 this.parent().setValue(this.value, suppressEvent);
25131 setBoxLabel : function(v)
25136 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25151 * @class Roo.bootstrap.SecurePass
25152 * @extends Roo.bootstrap.Input
25153 * Bootstrap SecurePass class
25157 * Create a new SecurePass
25158 * @param {Object} config The config object
25161 Roo.bootstrap.SecurePass = function (config) {
25162 // these go here, so the translation tool can replace them..
25164 PwdEmpty: "Please type a password, and then retype it to confirm.",
25165 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25166 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25167 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25168 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25169 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25170 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25171 TooWeak: "Your password is Too Weak."
25173 this.meterLabel = "Password strength:";
25174 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25175 this.meterClass = [
25176 "roo-password-meter-tooweak",
25177 "roo-password-meter-weak",
25178 "roo-password-meter-medium",
25179 "roo-password-meter-strong",
25180 "roo-password-meter-grey"
25185 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25188 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25190 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25192 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25193 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25194 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25195 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25196 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25197 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25198 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25208 * @cfg {String/Object} Label for the strength meter (defaults to
25209 * 'Password strength:')
25214 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25215 * ['Weak', 'Medium', 'Strong'])
25218 pwdStrengths: false,
25231 initEvents: function ()
25233 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25235 if (this.el.is('input[type=password]') && Roo.isSafari) {
25236 this.el.on('keydown', this.SafariOnKeyDown, this);
25239 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25242 onRender: function (ct, position)
25244 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25245 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25246 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25248 this.trigger.createChild({
25253 cls: 'roo-password-meter-grey col-xs-12',
25256 //width: this.meterWidth + 'px'
25260 cls: 'roo-password-meter-text'
25266 if (this.hideTrigger) {
25267 this.trigger.setDisplayed(false);
25269 this.setSize(this.width || '', this.height || '');
25272 onDestroy: function ()
25274 if (this.trigger) {
25275 this.trigger.removeAllListeners();
25276 this.trigger.remove();
25279 this.wrap.remove();
25281 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25284 checkStrength: function ()
25286 var pwd = this.inputEl().getValue();
25287 if (pwd == this._lastPwd) {
25292 if (this.ClientSideStrongPassword(pwd)) {
25294 } else if (this.ClientSideMediumPassword(pwd)) {
25296 } else if (this.ClientSideWeakPassword(pwd)) {
25302 Roo.log('strength1: ' + strength);
25304 //var pm = this.trigger.child('div/div/div').dom;
25305 var pm = this.trigger.child('div/div');
25306 pm.removeClass(this.meterClass);
25307 pm.addClass(this.meterClass[strength]);
25310 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25312 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25314 this._lastPwd = pwd;
25318 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25320 this._lastPwd = '';
25322 var pm = this.trigger.child('div/div');
25323 pm.removeClass(this.meterClass);
25324 pm.addClass('roo-password-meter-grey');
25327 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25330 this.inputEl().dom.type='password';
25333 validateValue: function (value)
25335 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25338 if (value.length == 0) {
25339 if (this.allowBlank) {
25340 this.clearInvalid();
25344 this.markInvalid(this.errors.PwdEmpty);
25345 this.errorMsg = this.errors.PwdEmpty;
25353 if (!value.match(/[\x21-\x7e]+/)) {
25354 this.markInvalid(this.errors.PwdBadChar);
25355 this.errorMsg = this.errors.PwdBadChar;
25358 if (value.length < 6) {
25359 this.markInvalid(this.errors.PwdShort);
25360 this.errorMsg = this.errors.PwdShort;
25363 if (value.length > 16) {
25364 this.markInvalid(this.errors.PwdLong);
25365 this.errorMsg = this.errors.PwdLong;
25369 if (this.ClientSideStrongPassword(value)) {
25371 } else if (this.ClientSideMediumPassword(value)) {
25373 } else if (this.ClientSideWeakPassword(value)) {
25380 if (strength < 2) {
25381 //this.markInvalid(this.errors.TooWeak);
25382 this.errorMsg = this.errors.TooWeak;
25387 console.log('strength2: ' + strength);
25389 //var pm = this.trigger.child('div/div/div').dom;
25391 var pm = this.trigger.child('div/div');
25392 pm.removeClass(this.meterClass);
25393 pm.addClass(this.meterClass[strength]);
25395 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25397 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25399 this.errorMsg = '';
25403 CharacterSetChecks: function (type)
25406 this.fResult = false;
25409 isctype: function (character, type)
25412 case this.kCapitalLetter:
25413 if (character >= 'A' && character <= 'Z') {
25418 case this.kSmallLetter:
25419 if (character >= 'a' && character <= 'z') {
25425 if (character >= '0' && character <= '9') {
25430 case this.kPunctuation:
25431 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25442 IsLongEnough: function (pwd, size)
25444 return !(pwd == null || isNaN(size) || pwd.length < size);
25447 SpansEnoughCharacterSets: function (word, nb)
25449 if (!this.IsLongEnough(word, nb))
25454 var characterSetChecks = new Array(
25455 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25456 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25459 for (var index = 0; index < word.length; ++index) {
25460 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25461 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25462 characterSetChecks[nCharSet].fResult = true;
25469 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25470 if (characterSetChecks[nCharSet].fResult) {
25475 if (nCharSets < nb) {
25481 ClientSideStrongPassword: function (pwd)
25483 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25486 ClientSideMediumPassword: function (pwd)
25488 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25491 ClientSideWeakPassword: function (pwd)
25493 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25496 })//<script type="text/javascript">
25499 * Based Ext JS Library 1.1.1
25500 * Copyright(c) 2006-2007, Ext JS, LLC.
25506 * @class Roo.HtmlEditorCore
25507 * @extends Roo.Component
25508 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25510 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25513 Roo.HtmlEditorCore = function(config){
25516 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25521 * @event initialize
25522 * Fires when the editor is fully initialized (including the iframe)
25523 * @param {Roo.HtmlEditorCore} this
25528 * Fires when the editor is first receives the focus. Any insertion must wait
25529 * until after this event.
25530 * @param {Roo.HtmlEditorCore} this
25534 * @event beforesync
25535 * Fires before the textarea is updated with content from the editor iframe. Return false
25536 * to cancel the sync.
25537 * @param {Roo.HtmlEditorCore} this
25538 * @param {String} html
25542 * @event beforepush
25543 * Fires before the iframe editor is updated with content from the textarea. Return false
25544 * to cancel the push.
25545 * @param {Roo.HtmlEditorCore} this
25546 * @param {String} html
25551 * Fires when the textarea is updated with content from the editor iframe.
25552 * @param {Roo.HtmlEditorCore} this
25553 * @param {String} html
25558 * Fires when the iframe editor is updated with content from the textarea.
25559 * @param {Roo.HtmlEditorCore} this
25560 * @param {String} html
25565 * @event editorevent
25566 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25567 * @param {Roo.HtmlEditorCore} this
25573 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25575 // defaults : white / black...
25576 this.applyBlacklists();
25583 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25587 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25593 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25598 * @cfg {Number} height (in pixels)
25602 * @cfg {Number} width (in pixels)
25607 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25610 stylesheets: false,
25613 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25616 allowComments: false,
25620 // private properties
25621 validationEvent : false,
25623 initialized : false,
25625 sourceEditMode : false,
25626 onFocus : Roo.emptyFn,
25628 hideMode:'offsets',
25632 // blacklist + whitelisted elements..
25639 * Protected method that will not generally be called directly. It
25640 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25641 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25643 getDocMarkup : function(){
25647 // inherit styels from page...??
25648 if (this.stylesheets === false) {
25650 Roo.get(document.head).select('style').each(function(node) {
25651 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25654 Roo.get(document.head).select('link').each(function(node) {
25655 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25658 } else if (!this.stylesheets.length) {
25660 st = '<style type="text/css">' +
25661 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25664 for (var i in this.stylesheets) {
25665 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25670 st += '<style type="text/css">' +
25671 'IMG { cursor: pointer } ' +
25674 var cls = 'roo-htmleditor-body';
25676 if(this.bodyCls.length){
25677 cls += ' ' + this.bodyCls;
25680 return '<html><head>' + st +
25681 //<style type="text/css">' +
25682 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25684 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25688 onRender : function(ct, position)
25691 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25692 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25695 this.el.dom.style.border = '0 none';
25696 this.el.dom.setAttribute('tabIndex', -1);
25697 this.el.addClass('x-hidden hide');
25701 if(Roo.isIE){ // fix IE 1px bogus margin
25702 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25706 this.frameId = Roo.id();
25710 var iframe = this.owner.wrap.createChild({
25712 cls: 'form-control', // bootstrap..
25714 name: this.frameId,
25715 frameBorder : 'no',
25716 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25721 this.iframe = iframe.dom;
25723 this.assignDocWin();
25725 this.doc.designMode = 'on';
25728 this.doc.write(this.getDocMarkup());
25732 var task = { // must defer to wait for browser to be ready
25734 //console.log("run task?" + this.doc.readyState);
25735 this.assignDocWin();
25736 if(this.doc.body || this.doc.readyState == 'complete'){
25738 this.doc.designMode="on";
25742 Roo.TaskMgr.stop(task);
25743 this.initEditor.defer(10, this);
25750 Roo.TaskMgr.start(task);
25755 onResize : function(w, h)
25757 Roo.log('resize: ' +w + ',' + h );
25758 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25762 if(typeof w == 'number'){
25764 this.iframe.style.width = w + 'px';
25766 if(typeof h == 'number'){
25768 this.iframe.style.height = h + 'px';
25770 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25777 * Toggles the editor between standard and source edit mode.
25778 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25780 toggleSourceEdit : function(sourceEditMode){
25782 this.sourceEditMode = sourceEditMode === true;
25784 if(this.sourceEditMode){
25786 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25789 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25790 //this.iframe.className = '';
25793 //this.setSize(this.owner.wrap.getSize());
25794 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25801 * Protected method that will not generally be called directly. If you need/want
25802 * custom HTML cleanup, this is the method you should override.
25803 * @param {String} html The HTML to be cleaned
25804 * return {String} The cleaned HTML
25806 cleanHtml : function(html){
25807 html = String(html);
25808 if(html.length > 5){
25809 if(Roo.isSafari){ // strip safari nonsense
25810 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25813 if(html == ' '){
25820 * HTML Editor -> Textarea
25821 * Protected method that will not generally be called directly. Syncs the contents
25822 * of the editor iframe with the textarea.
25824 syncValue : function(){
25825 if(this.initialized){
25826 var bd = (this.doc.body || this.doc.documentElement);
25827 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25828 var html = bd.innerHTML;
25830 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25831 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25833 html = '<div style="'+m[0]+'">' + html + '</div>';
25836 html = this.cleanHtml(html);
25837 // fix up the special chars.. normaly like back quotes in word...
25838 // however we do not want to do this with chinese..
25839 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25841 var cc = match.charCodeAt();
25843 // Get the character value, handling surrogate pairs
25844 if (match.length == 2) {
25845 // It's a surrogate pair, calculate the Unicode code point
25846 var high = match.charCodeAt(0) - 0xD800;
25847 var low = match.charCodeAt(1) - 0xDC00;
25848 cc = (high * 0x400) + low + 0x10000;
25850 (cc >= 0x4E00 && cc < 0xA000 ) ||
25851 (cc >= 0x3400 && cc < 0x4E00 ) ||
25852 (cc >= 0xf900 && cc < 0xfb00 )
25857 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25858 return "&#" + cc + ";";
25865 if(this.owner.fireEvent('beforesync', this, html) !== false){
25866 this.el.dom.value = html;
25867 this.owner.fireEvent('sync', this, html);
25873 * Protected method that will not generally be called directly. Pushes the value of the textarea
25874 * into the iframe editor.
25876 pushValue : function(){
25877 if(this.initialized){
25878 var v = this.el.dom.value.trim();
25880 // if(v.length < 1){
25884 if(this.owner.fireEvent('beforepush', this, v) !== false){
25885 var d = (this.doc.body || this.doc.documentElement);
25887 this.cleanUpPaste();
25888 this.el.dom.value = d.innerHTML;
25889 this.owner.fireEvent('push', this, v);
25895 deferFocus : function(){
25896 this.focus.defer(10, this);
25900 focus : function(){
25901 if(this.win && !this.sourceEditMode){
25908 assignDocWin: function()
25910 var iframe = this.iframe;
25913 this.doc = iframe.contentWindow.document;
25914 this.win = iframe.contentWindow;
25916 // if (!Roo.get(this.frameId)) {
25919 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25920 // this.win = Roo.get(this.frameId).dom.contentWindow;
25922 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25926 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25927 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25932 initEditor : function(){
25933 //console.log("INIT EDITOR");
25934 this.assignDocWin();
25938 this.doc.designMode="on";
25940 this.doc.write(this.getDocMarkup());
25943 var dbody = (this.doc.body || this.doc.documentElement);
25944 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25945 // this copies styles from the containing element into thsi one..
25946 // not sure why we need all of this..
25947 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25949 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25950 //ss['background-attachment'] = 'fixed'; // w3c
25951 dbody.bgProperties = 'fixed'; // ie
25952 //Roo.DomHelper.applyStyles(dbody, ss);
25953 Roo.EventManager.on(this.doc, {
25954 //'mousedown': this.onEditorEvent,
25955 'mouseup': this.onEditorEvent,
25956 'dblclick': this.onEditorEvent,
25957 'click': this.onEditorEvent,
25958 'keyup': this.onEditorEvent,
25963 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25965 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25966 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25968 this.initialized = true;
25970 this.owner.fireEvent('initialize', this);
25975 onDestroy : function(){
25981 //for (var i =0; i < this.toolbars.length;i++) {
25982 // // fixme - ask toolbars for heights?
25983 // this.toolbars[i].onDestroy();
25986 //this.wrap.dom.innerHTML = '';
25987 //this.wrap.remove();
25992 onFirstFocus : function(){
25994 this.assignDocWin();
25997 this.activated = true;
26000 if(Roo.isGecko){ // prevent silly gecko errors
26002 var s = this.win.getSelection();
26003 if(!s.focusNode || s.focusNode.nodeType != 3){
26004 var r = s.getRangeAt(0);
26005 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26010 this.execCmd('useCSS', true);
26011 this.execCmd('styleWithCSS', false);
26014 this.owner.fireEvent('activate', this);
26018 adjustFont: function(btn){
26019 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26020 //if(Roo.isSafari){ // safari
26023 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26024 if(Roo.isSafari){ // safari
26025 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26026 v = (v < 10) ? 10 : v;
26027 v = (v > 48) ? 48 : v;
26028 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26033 v = Math.max(1, v+adjust);
26035 this.execCmd('FontSize', v );
26038 onEditorEvent : function(e)
26040 this.owner.fireEvent('editorevent', this, e);
26041 // this.updateToolbar();
26042 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26045 insertTag : function(tg)
26047 // could be a bit smarter... -> wrap the current selected tRoo..
26048 if (tg.toLowerCase() == 'span' ||
26049 tg.toLowerCase() == 'code' ||
26050 tg.toLowerCase() == 'sup' ||
26051 tg.toLowerCase() == 'sub'
26054 range = this.createRange(this.getSelection());
26055 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26056 wrappingNode.appendChild(range.extractContents());
26057 range.insertNode(wrappingNode);
26064 this.execCmd("formatblock", tg);
26068 insertText : function(txt)
26072 var range = this.createRange();
26073 range.deleteContents();
26074 //alert(Sender.getAttribute('label'));
26076 range.insertNode(this.doc.createTextNode(txt));
26082 * Executes a Midas editor command on the editor document and performs necessary focus and
26083 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26084 * @param {String} cmd The Midas command
26085 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26087 relayCmd : function(cmd, value){
26089 this.execCmd(cmd, value);
26090 this.owner.fireEvent('editorevent', this);
26091 //this.updateToolbar();
26092 this.owner.deferFocus();
26096 * Executes a Midas editor command directly on the editor document.
26097 * For visual commands, you should use {@link #relayCmd} instead.
26098 * <b>This should only be called after the editor is initialized.</b>
26099 * @param {String} cmd The Midas command
26100 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26102 execCmd : function(cmd, value){
26103 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26110 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26112 * @param {String} text | dom node..
26114 insertAtCursor : function(text)
26117 if(!this.activated){
26123 var r = this.doc.selection.createRange();
26134 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26138 // from jquery ui (MIT licenced)
26140 var win = this.win;
26142 if (win.getSelection && win.getSelection().getRangeAt) {
26143 range = win.getSelection().getRangeAt(0);
26144 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26145 range.insertNode(node);
26146 } else if (win.document.selection && win.document.selection.createRange) {
26147 // no firefox support
26148 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26149 win.document.selection.createRange().pasteHTML(txt);
26151 // no firefox support
26152 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26153 this.execCmd('InsertHTML', txt);
26162 mozKeyPress : function(e){
26164 var c = e.getCharCode(), cmd;
26167 c = String.fromCharCode(c).toLowerCase();
26181 this.cleanUpPaste.defer(100, this);
26189 e.preventDefault();
26197 fixKeys : function(){ // load time branching for fastest keydown performance
26199 return function(e){
26200 var k = e.getKey(), r;
26203 r = this.doc.selection.createRange();
26206 r.pasteHTML('    ');
26213 r = this.doc.selection.createRange();
26215 var target = r.parentElement();
26216 if(!target || target.tagName.toLowerCase() != 'li'){
26218 r.pasteHTML('<br />');
26224 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26225 this.cleanUpPaste.defer(100, this);
26231 }else if(Roo.isOpera){
26232 return function(e){
26233 var k = e.getKey();
26237 this.execCmd('InsertHTML','    ');
26240 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26241 this.cleanUpPaste.defer(100, this);
26246 }else if(Roo.isSafari){
26247 return function(e){
26248 var k = e.getKey();
26252 this.execCmd('InsertText','\t');
26256 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26257 this.cleanUpPaste.defer(100, this);
26265 getAllAncestors: function()
26267 var p = this.getSelectedNode();
26270 a.push(p); // push blank onto stack..
26271 p = this.getParentElement();
26275 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26279 a.push(this.doc.body);
26283 lastSelNode : false,
26286 getSelection : function()
26288 this.assignDocWin();
26289 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26292 getSelectedNode: function()
26294 // this may only work on Gecko!!!
26296 // should we cache this!!!!
26301 var range = this.createRange(this.getSelection()).cloneRange();
26304 var parent = range.parentElement();
26306 var testRange = range.duplicate();
26307 testRange.moveToElementText(parent);
26308 if (testRange.inRange(range)) {
26311 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26314 parent = parent.parentElement;
26319 // is ancestor a text element.
26320 var ac = range.commonAncestorContainer;
26321 if (ac.nodeType == 3) {
26322 ac = ac.parentNode;
26325 var ar = ac.childNodes;
26328 var other_nodes = [];
26329 var has_other_nodes = false;
26330 for (var i=0;i<ar.length;i++) {
26331 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26334 // fullly contained node.
26336 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26341 // probably selected..
26342 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26343 other_nodes.push(ar[i]);
26347 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26352 has_other_nodes = true;
26354 if (!nodes.length && other_nodes.length) {
26355 nodes= other_nodes;
26357 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26363 createRange: function(sel)
26365 // this has strange effects when using with
26366 // top toolbar - not sure if it's a great idea.
26367 //this.editor.contentWindow.focus();
26368 if (typeof sel != "undefined") {
26370 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26372 return this.doc.createRange();
26375 return this.doc.createRange();
26378 getParentElement: function()
26381 this.assignDocWin();
26382 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26384 var range = this.createRange(sel);
26387 var p = range.commonAncestorContainer;
26388 while (p.nodeType == 3) { // text node
26399 * Range intersection.. the hard stuff...
26403 * [ -- selected range --- ]
26407 * if end is before start or hits it. fail.
26408 * if start is after end or hits it fail.
26410 * if either hits (but other is outside. - then it's not
26416 // @see http://www.thismuchiknow.co.uk/?p=64.
26417 rangeIntersectsNode : function(range, node)
26419 var nodeRange = node.ownerDocument.createRange();
26421 nodeRange.selectNode(node);
26423 nodeRange.selectNodeContents(node);
26426 var rangeStartRange = range.cloneRange();
26427 rangeStartRange.collapse(true);
26429 var rangeEndRange = range.cloneRange();
26430 rangeEndRange.collapse(false);
26432 var nodeStartRange = nodeRange.cloneRange();
26433 nodeStartRange.collapse(true);
26435 var nodeEndRange = nodeRange.cloneRange();
26436 nodeEndRange.collapse(false);
26438 return rangeStartRange.compareBoundaryPoints(
26439 Range.START_TO_START, nodeEndRange) == -1 &&
26440 rangeEndRange.compareBoundaryPoints(
26441 Range.START_TO_START, nodeStartRange) == 1;
26445 rangeCompareNode : function(range, node)
26447 var nodeRange = node.ownerDocument.createRange();
26449 nodeRange.selectNode(node);
26451 nodeRange.selectNodeContents(node);
26455 range.collapse(true);
26457 nodeRange.collapse(true);
26459 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26460 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26462 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26464 var nodeIsBefore = ss == 1;
26465 var nodeIsAfter = ee == -1;
26467 if (nodeIsBefore && nodeIsAfter) {
26470 if (!nodeIsBefore && nodeIsAfter) {
26471 return 1; //right trailed.
26474 if (nodeIsBefore && !nodeIsAfter) {
26475 return 2; // left trailed.
26481 // private? - in a new class?
26482 cleanUpPaste : function()
26484 // cleans up the whole document..
26485 Roo.log('cleanuppaste');
26487 this.cleanUpChildren(this.doc.body);
26488 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26489 if (clean != this.doc.body.innerHTML) {
26490 this.doc.body.innerHTML = clean;
26495 cleanWordChars : function(input) {// change the chars to hex code
26496 var he = Roo.HtmlEditorCore;
26498 var output = input;
26499 Roo.each(he.swapCodes, function(sw) {
26500 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26502 output = output.replace(swapper, sw[1]);
26509 cleanUpChildren : function (n)
26511 if (!n.childNodes.length) {
26514 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26515 this.cleanUpChild(n.childNodes[i]);
26522 cleanUpChild : function (node)
26525 //console.log(node);
26526 if (node.nodeName == "#text") {
26527 // clean up silly Windows -- stuff?
26530 if (node.nodeName == "#comment" && !this.allowComments) {
26531 node.parentNode.removeChild(node);
26532 // clean up silly Windows -- stuff?
26535 var lcname = node.tagName.toLowerCase();
26536 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26537 // whitelist of tags..
26539 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26541 node.parentNode.removeChild(node);
26546 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26548 // spans with no attributes - just remove them..
26549 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26550 remove_keep_children = true;
26553 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26554 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26556 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26557 // remove_keep_children = true;
26560 if (remove_keep_children) {
26561 this.cleanUpChildren(node);
26562 // inserts everything just before this node...
26563 while (node.childNodes.length) {
26564 var cn = node.childNodes[0];
26565 node.removeChild(cn);
26566 node.parentNode.insertBefore(cn, node);
26568 node.parentNode.removeChild(node);
26572 if (!node.attributes || !node.attributes.length) {
26577 this.cleanUpChildren(node);
26581 function cleanAttr(n,v)
26584 if (v.match(/^\./) || v.match(/^\//)) {
26587 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26590 if (v.match(/^#/)) {
26593 if (v.match(/^\{/)) { // allow template editing.
26596 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26597 node.removeAttribute(n);
26601 var cwhite = this.cwhite;
26602 var cblack = this.cblack;
26604 function cleanStyle(n,v)
26606 if (v.match(/expression/)) { //XSS?? should we even bother..
26607 node.removeAttribute(n);
26611 var parts = v.split(/;/);
26614 Roo.each(parts, function(p) {
26615 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26619 var l = p.split(':').shift().replace(/\s+/g,'');
26620 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26622 if ( cwhite.length && cblack.indexOf(l) > -1) {
26623 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26624 //node.removeAttribute(n);
26628 // only allow 'c whitelisted system attributes'
26629 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26630 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26631 //node.removeAttribute(n);
26641 if (clean.length) {
26642 node.setAttribute(n, clean.join(';'));
26644 node.removeAttribute(n);
26650 for (var i = node.attributes.length-1; i > -1 ; i--) {
26651 var a = node.attributes[i];
26654 if (a.name.toLowerCase().substr(0,2)=='on') {
26655 node.removeAttribute(a.name);
26658 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26659 node.removeAttribute(a.name);
26662 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26663 cleanAttr(a.name,a.value); // fixme..
26666 if (a.name == 'style') {
26667 cleanStyle(a.name,a.value);
26670 /// clean up MS crap..
26671 // tecnically this should be a list of valid class'es..
26674 if (a.name == 'class') {
26675 if (a.value.match(/^Mso/)) {
26676 node.removeAttribute('class');
26679 if (a.value.match(/^body$/)) {
26680 node.removeAttribute('class');
26691 this.cleanUpChildren(node);
26697 * Clean up MS wordisms...
26699 cleanWord : function(node)
26702 this.cleanWord(this.doc.body);
26707 node.nodeName == 'SPAN' &&
26708 !node.hasAttributes() &&
26709 node.childNodes.length == 1 &&
26710 node.firstChild.nodeName == "#text"
26712 var textNode = node.firstChild;
26713 node.removeChild(textNode);
26714 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26715 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26717 node.parentNode.insertBefore(textNode, node);
26718 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26719 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26721 node.parentNode.removeChild(node);
26724 if (node.nodeName == "#text") {
26725 // clean up silly Windows -- stuff?
26728 if (node.nodeName == "#comment") {
26729 node.parentNode.removeChild(node);
26730 // clean up silly Windows -- stuff?
26734 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26735 node.parentNode.removeChild(node);
26738 //Roo.log(node.tagName);
26739 // remove - but keep children..
26740 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26741 //Roo.log('-- removed');
26742 while (node.childNodes.length) {
26743 var cn = node.childNodes[0];
26744 node.removeChild(cn);
26745 node.parentNode.insertBefore(cn, node);
26746 // move node to parent - and clean it..
26747 this.cleanWord(cn);
26749 node.parentNode.removeChild(node);
26750 /// no need to iterate chidlren = it's got none..
26751 //this.iterateChildren(node, this.cleanWord);
26755 if (node.className.length) {
26757 var cn = node.className.split(/\W+/);
26759 Roo.each(cn, function(cls) {
26760 if (cls.match(/Mso[a-zA-Z]+/)) {
26765 node.className = cna.length ? cna.join(' ') : '';
26767 node.removeAttribute("class");
26771 if (node.hasAttribute("lang")) {
26772 node.removeAttribute("lang");
26775 if (node.hasAttribute("style")) {
26777 var styles = node.getAttribute("style").split(";");
26779 Roo.each(styles, function(s) {
26780 if (!s.match(/:/)) {
26783 var kv = s.split(":");
26784 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26787 // what ever is left... we allow.
26790 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26791 if (!nstyle.length) {
26792 node.removeAttribute('style');
26795 this.iterateChildren(node, this.cleanWord);
26801 * iterateChildren of a Node, calling fn each time, using this as the scole..
26802 * @param {DomNode} node node to iterate children of.
26803 * @param {Function} fn method of this class to call on each item.
26805 iterateChildren : function(node, fn)
26807 if (!node.childNodes.length) {
26810 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26811 fn.call(this, node.childNodes[i])
26817 * cleanTableWidths.
26819 * Quite often pasting from word etc.. results in tables with column and widths.
26820 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26823 cleanTableWidths : function(node)
26828 this.cleanTableWidths(this.doc.body);
26833 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26836 Roo.log(node.tagName);
26837 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26838 this.iterateChildren(node, this.cleanTableWidths);
26841 if (node.hasAttribute('width')) {
26842 node.removeAttribute('width');
26846 if (node.hasAttribute("style")) {
26849 var styles = node.getAttribute("style").split(";");
26851 Roo.each(styles, function(s) {
26852 if (!s.match(/:/)) {
26855 var kv = s.split(":");
26856 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26859 // what ever is left... we allow.
26862 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26863 if (!nstyle.length) {
26864 node.removeAttribute('style');
26868 this.iterateChildren(node, this.cleanTableWidths);
26876 domToHTML : function(currentElement, depth, nopadtext) {
26878 depth = depth || 0;
26879 nopadtext = nopadtext || false;
26881 if (!currentElement) {
26882 return this.domToHTML(this.doc.body);
26885 //Roo.log(currentElement);
26887 var allText = false;
26888 var nodeName = currentElement.nodeName;
26889 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26891 if (nodeName == '#text') {
26893 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26898 if (nodeName != 'BODY') {
26901 // Prints the node tagName, such as <A>, <IMG>, etc
26904 for(i = 0; i < currentElement.attributes.length;i++) {
26906 var aname = currentElement.attributes.item(i).name;
26907 if (!currentElement.attributes.item(i).value.length) {
26910 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26913 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26922 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26925 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26930 // Traverse the tree
26932 var currentElementChild = currentElement.childNodes.item(i);
26933 var allText = true;
26934 var innerHTML = '';
26936 while (currentElementChild) {
26937 // Formatting code (indent the tree so it looks nice on the screen)
26938 var nopad = nopadtext;
26939 if (lastnode == 'SPAN') {
26943 if (currentElementChild.nodeName == '#text') {
26944 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26945 toadd = nopadtext ? toadd : toadd.trim();
26946 if (!nopad && toadd.length > 80) {
26947 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26949 innerHTML += toadd;
26952 currentElementChild = currentElement.childNodes.item(i);
26958 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26960 // Recursively traverse the tree structure of the child node
26961 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26962 lastnode = currentElementChild.nodeName;
26964 currentElementChild=currentElement.childNodes.item(i);
26970 // The remaining code is mostly for formatting the tree
26971 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26976 ret+= "</"+tagName+">";
26982 applyBlacklists : function()
26984 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26985 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26989 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26990 if (b.indexOf(tag) > -1) {
26993 this.white.push(tag);
26997 Roo.each(w, function(tag) {
26998 if (b.indexOf(tag) > -1) {
27001 if (this.white.indexOf(tag) > -1) {
27004 this.white.push(tag);
27009 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27010 if (w.indexOf(tag) > -1) {
27013 this.black.push(tag);
27017 Roo.each(b, function(tag) {
27018 if (w.indexOf(tag) > -1) {
27021 if (this.black.indexOf(tag) > -1) {
27024 this.black.push(tag);
27029 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27030 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27034 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27035 if (b.indexOf(tag) > -1) {
27038 this.cwhite.push(tag);
27042 Roo.each(w, function(tag) {
27043 if (b.indexOf(tag) > -1) {
27046 if (this.cwhite.indexOf(tag) > -1) {
27049 this.cwhite.push(tag);
27054 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27055 if (w.indexOf(tag) > -1) {
27058 this.cblack.push(tag);
27062 Roo.each(b, function(tag) {
27063 if (w.indexOf(tag) > -1) {
27066 if (this.cblack.indexOf(tag) > -1) {
27069 this.cblack.push(tag);
27074 setStylesheets : function(stylesheets)
27076 if(typeof(stylesheets) == 'string'){
27077 Roo.get(this.iframe.contentDocument.head).createChild({
27079 rel : 'stylesheet',
27088 Roo.each(stylesheets, function(s) {
27093 Roo.get(_this.iframe.contentDocument.head).createChild({
27095 rel : 'stylesheet',
27104 removeStylesheets : function()
27108 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27113 setStyle : function(style)
27115 Roo.get(this.iframe.contentDocument.head).createChild({
27124 // hide stuff that is not compatible
27138 * @event specialkey
27142 * @cfg {String} fieldClass @hide
27145 * @cfg {String} focusClass @hide
27148 * @cfg {String} autoCreate @hide
27151 * @cfg {String} inputType @hide
27154 * @cfg {String} invalidClass @hide
27157 * @cfg {String} invalidText @hide
27160 * @cfg {String} msgFx @hide
27163 * @cfg {String} validateOnBlur @hide
27167 Roo.HtmlEditorCore.white = [
27168 'area', 'br', 'img', 'input', 'hr', 'wbr',
27170 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27171 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27172 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27173 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27174 'table', 'ul', 'xmp',
27176 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27179 'dir', 'menu', 'ol', 'ul', 'dl',
27185 Roo.HtmlEditorCore.black = [
27186 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27188 'base', 'basefont', 'bgsound', 'blink', 'body',
27189 'frame', 'frameset', 'head', 'html', 'ilayer',
27190 'iframe', 'layer', 'link', 'meta', 'object',
27191 'script', 'style' ,'title', 'xml' // clean later..
27193 Roo.HtmlEditorCore.clean = [
27194 'script', 'style', 'title', 'xml'
27196 Roo.HtmlEditorCore.remove = [
27201 Roo.HtmlEditorCore.ablack = [
27205 Roo.HtmlEditorCore.aclean = [
27206 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27210 Roo.HtmlEditorCore.pwhite= [
27211 'http', 'https', 'mailto'
27214 // white listed style attributes.
27215 Roo.HtmlEditorCore.cwhite= [
27216 // 'text-align', /// default is to allow most things..
27222 // black listed style attributes.
27223 Roo.HtmlEditorCore.cblack= [
27224 // 'font-size' -- this can be set by the project
27228 Roo.HtmlEditorCore.swapCodes =[
27229 [ 8211, "–" ],
27230 [ 8212, "—" ],
27247 * @class Roo.bootstrap.HtmlEditor
27248 * @extends Roo.bootstrap.TextArea
27249 * Bootstrap HtmlEditor class
27252 * Create a new HtmlEditor
27253 * @param {Object} config The config object
27256 Roo.bootstrap.HtmlEditor = function(config){
27257 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27258 if (!this.toolbars) {
27259 this.toolbars = [];
27262 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27265 * @event initialize
27266 * Fires when the editor is fully initialized (including the iframe)
27267 * @param {HtmlEditor} this
27272 * Fires when the editor is first receives the focus. Any insertion must wait
27273 * until after this event.
27274 * @param {HtmlEditor} this
27278 * @event beforesync
27279 * Fires before the textarea is updated with content from the editor iframe. Return false
27280 * to cancel the sync.
27281 * @param {HtmlEditor} this
27282 * @param {String} html
27286 * @event beforepush
27287 * Fires before the iframe editor is updated with content from the textarea. Return false
27288 * to cancel the push.
27289 * @param {HtmlEditor} this
27290 * @param {String} html
27295 * Fires when the textarea is updated with content from the editor iframe.
27296 * @param {HtmlEditor} this
27297 * @param {String} html
27302 * Fires when the iframe editor is updated with content from the textarea.
27303 * @param {HtmlEditor} this
27304 * @param {String} html
27308 * @event editmodechange
27309 * Fires when the editor switches edit modes
27310 * @param {HtmlEditor} this
27311 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27313 editmodechange: true,
27315 * @event editorevent
27316 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27317 * @param {HtmlEditor} this
27321 * @event firstfocus
27322 * Fires when on first focus - needed by toolbars..
27323 * @param {HtmlEditor} this
27328 * Auto save the htmlEditor value as a file into Events
27329 * @param {HtmlEditor} this
27333 * @event savedpreview
27334 * preview the saved version of htmlEditor
27335 * @param {HtmlEditor} this
27342 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27346 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27351 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27356 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27361 * @cfg {Number} height (in pixels)
27365 * @cfg {Number} width (in pixels)
27370 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27373 stylesheets: false,
27378 // private properties
27379 validationEvent : false,
27381 initialized : false,
27384 onFocus : Roo.emptyFn,
27386 hideMode:'offsets',
27388 tbContainer : false,
27392 toolbarContainer :function() {
27393 return this.wrap.select('.x-html-editor-tb',true).first();
27397 * Protected method that will not generally be called directly. It
27398 * is called when the editor creates its toolbar. Override this method if you need to
27399 * add custom toolbar buttons.
27400 * @param {HtmlEditor} editor
27402 createToolbar : function(){
27403 Roo.log('renewing');
27404 Roo.log("create toolbars");
27406 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27407 this.toolbars[0].render(this.toolbarContainer());
27411 // if (!editor.toolbars || !editor.toolbars.length) {
27412 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27415 // for (var i =0 ; i < editor.toolbars.length;i++) {
27416 // editor.toolbars[i] = Roo.factory(
27417 // typeof(editor.toolbars[i]) == 'string' ?
27418 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27419 // Roo.bootstrap.HtmlEditor);
27420 // editor.toolbars[i].init(editor);
27426 onRender : function(ct, position)
27428 // Roo.log("Call onRender: " + this.xtype);
27430 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27432 this.wrap = this.inputEl().wrap({
27433 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27436 this.editorcore.onRender(ct, position);
27438 if (this.resizable) {
27439 this.resizeEl = new Roo.Resizable(this.wrap, {
27443 minHeight : this.height,
27444 height: this.height,
27445 handles : this.resizable,
27448 resize : function(r, w, h) {
27449 _t.onResize(w,h); // -something
27455 this.createToolbar(this);
27458 if(!this.width && this.resizable){
27459 this.setSize(this.wrap.getSize());
27461 if (this.resizeEl) {
27462 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27463 // should trigger onReize..
27469 onResize : function(w, h)
27471 Roo.log('resize: ' +w + ',' + h );
27472 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27476 if(this.inputEl() ){
27477 if(typeof w == 'number'){
27478 var aw = w - this.wrap.getFrameWidth('lr');
27479 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27482 if(typeof h == 'number'){
27483 var tbh = -11; // fixme it needs to tool bar size!
27484 for (var i =0; i < this.toolbars.length;i++) {
27485 // fixme - ask toolbars for heights?
27486 tbh += this.toolbars[i].el.getHeight();
27487 //if (this.toolbars[i].footer) {
27488 // tbh += this.toolbars[i].footer.el.getHeight();
27496 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27497 ah -= 5; // knock a few pixes off for look..
27498 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27502 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27503 this.editorcore.onResize(ew,eh);
27508 * Toggles the editor between standard and source edit mode.
27509 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27511 toggleSourceEdit : function(sourceEditMode)
27513 this.editorcore.toggleSourceEdit(sourceEditMode);
27515 if(this.editorcore.sourceEditMode){
27516 Roo.log('editor - showing textarea');
27519 // Roo.log(this.syncValue());
27521 this.inputEl().removeClass(['hide', 'x-hidden']);
27522 this.inputEl().dom.removeAttribute('tabIndex');
27523 this.inputEl().focus();
27525 Roo.log('editor - hiding textarea');
27527 // Roo.log(this.pushValue());
27530 this.inputEl().addClass(['hide', 'x-hidden']);
27531 this.inputEl().dom.setAttribute('tabIndex', -1);
27532 //this.deferFocus();
27535 if(this.resizable){
27536 this.setSize(this.wrap.getSize());
27539 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27542 // private (for BoxComponent)
27543 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27545 // private (for BoxComponent)
27546 getResizeEl : function(){
27550 // private (for BoxComponent)
27551 getPositionEl : function(){
27556 initEvents : function(){
27557 this.originalValue = this.getValue();
27561 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27564 // markInvalid : Roo.emptyFn,
27566 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27569 // clearInvalid : Roo.emptyFn,
27571 setValue : function(v){
27572 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27573 this.editorcore.pushValue();
27578 deferFocus : function(){
27579 this.focus.defer(10, this);
27583 focus : function(){
27584 this.editorcore.focus();
27590 onDestroy : function(){
27596 for (var i =0; i < this.toolbars.length;i++) {
27597 // fixme - ask toolbars for heights?
27598 this.toolbars[i].onDestroy();
27601 this.wrap.dom.innerHTML = '';
27602 this.wrap.remove();
27607 onFirstFocus : function(){
27608 //Roo.log("onFirstFocus");
27609 this.editorcore.onFirstFocus();
27610 for (var i =0; i < this.toolbars.length;i++) {
27611 this.toolbars[i].onFirstFocus();
27617 syncValue : function()
27619 this.editorcore.syncValue();
27622 pushValue : function()
27624 this.editorcore.pushValue();
27628 // hide stuff that is not compatible
27642 * @event specialkey
27646 * @cfg {String} fieldClass @hide
27649 * @cfg {String} focusClass @hide
27652 * @cfg {String} autoCreate @hide
27655 * @cfg {String} inputType @hide
27659 * @cfg {String} invalidText @hide
27662 * @cfg {String} msgFx @hide
27665 * @cfg {String} validateOnBlur @hide
27674 Roo.namespace('Roo.bootstrap.htmleditor');
27676 * @class Roo.bootstrap.HtmlEditorToolbar1
27682 new Roo.bootstrap.HtmlEditor({
27685 new Roo.bootstrap.HtmlEditorToolbar1({
27686 disable : { fonts: 1 , format: 1, ..., ... , ...],
27692 * @cfg {Object} disable List of elements to disable..
27693 * @cfg {Array} btns List of additional buttons.
27697 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27700 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27703 Roo.apply(this, config);
27705 // default disabled, based on 'good practice'..
27706 this.disable = this.disable || {};
27707 Roo.applyIf(this.disable, {
27710 specialElements : true
27712 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27714 this.editor = config.editor;
27715 this.editorcore = config.editor.editorcore;
27717 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27719 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27720 // dont call parent... till later.
27722 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27727 editorcore : false,
27732 "h1","h2","h3","h4","h5","h6",
27734 "abbr", "acronym", "address", "cite", "samp", "var",
27738 onRender : function(ct, position)
27740 // Roo.log("Call onRender: " + this.xtype);
27742 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27744 this.el.dom.style.marginBottom = '0';
27746 var editorcore = this.editorcore;
27747 var editor= this.editor;
27750 var btn = function(id,cmd , toggle, handler, html){
27752 var event = toggle ? 'toggle' : 'click';
27757 xns: Roo.bootstrap,
27761 enableToggle:toggle !== false,
27763 pressed : toggle ? false : null,
27766 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27767 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27773 // var cb_box = function...
27778 xns: Roo.bootstrap,
27783 xns: Roo.bootstrap,
27787 Roo.each(this.formats, function(f) {
27788 style.menu.items.push({
27790 xns: Roo.bootstrap,
27791 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27796 editorcore.insertTag(this.tagname);
27803 children.push(style);
27805 btn('bold',false,true);
27806 btn('italic',false,true);
27807 btn('align-left', 'justifyleft',true);
27808 btn('align-center', 'justifycenter',true);
27809 btn('align-right' , 'justifyright',true);
27810 btn('link', false, false, function(btn) {
27811 //Roo.log("create link?");
27812 var url = prompt(this.createLinkText, this.defaultLinkValue);
27813 if(url && url != 'http:/'+'/'){
27814 this.editorcore.relayCmd('createlink', url);
27817 btn('list','insertunorderedlist',true);
27818 btn('pencil', false,true, function(btn){
27820 this.toggleSourceEdit(btn.pressed);
27823 if (this.editor.btns.length > 0) {
27824 for (var i = 0; i<this.editor.btns.length; i++) {
27825 children.push(this.editor.btns[i]);
27833 xns: Roo.bootstrap,
27838 xns: Roo.bootstrap,
27843 cog.menu.items.push({
27845 xns: Roo.bootstrap,
27846 html : Clean styles,
27851 editorcore.insertTag(this.tagname);
27860 this.xtype = 'NavSimplebar';
27862 for(var i=0;i< children.length;i++) {
27864 this.buttons.add(this.addxtypeChild(children[i]));
27868 editor.on('editorevent', this.updateToolbar, this);
27870 onBtnClick : function(id)
27872 this.editorcore.relayCmd(id);
27873 this.editorcore.focus();
27877 * Protected method that will not generally be called directly. It triggers
27878 * a toolbar update by reading the markup state of the current selection in the editor.
27880 updateToolbar: function(){
27882 if(!this.editorcore.activated){
27883 this.editor.onFirstFocus(); // is this neeed?
27887 var btns = this.buttons;
27888 var doc = this.editorcore.doc;
27889 btns.get('bold').setActive(doc.queryCommandState('bold'));
27890 btns.get('italic').setActive(doc.queryCommandState('italic'));
27891 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27893 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27894 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27895 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27897 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27898 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27901 var ans = this.editorcore.getAllAncestors();
27902 if (this.formatCombo) {
27905 var store = this.formatCombo.store;
27906 this.formatCombo.setValue("");
27907 for (var i =0; i < ans.length;i++) {
27908 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27910 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27918 // hides menus... - so this cant be on a menu...
27919 Roo.bootstrap.MenuMgr.hideAll();
27921 Roo.bootstrap.MenuMgr.hideAll();
27922 //this.editorsyncValue();
27924 onFirstFocus: function() {
27925 this.buttons.each(function(item){
27929 toggleSourceEdit : function(sourceEditMode){
27932 if(sourceEditMode){
27933 Roo.log("disabling buttons");
27934 this.buttons.each( function(item){
27935 if(item.cmd != 'pencil'){
27941 Roo.log("enabling buttons");
27942 if(this.editorcore.initialized){
27943 this.buttons.each( function(item){
27949 Roo.log("calling toggole on editor");
27950 // tell the editor that it's been pressed..
27951 this.editor.toggleSourceEdit(sourceEditMode);
27965 * @class Roo.bootstrap.Markdown
27966 * @extends Roo.bootstrap.TextArea
27967 * Bootstrap Showdown editable area
27968 * @cfg {string} content
27971 * Create a new Showdown
27974 Roo.bootstrap.Markdown = function(config){
27975 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27979 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27983 initEvents : function()
27986 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27987 this.markdownEl = this.el.createChild({
27988 cls : 'roo-markdown-area'
27990 this.inputEl().addClass('d-none');
27991 if (this.getValue() == '') {
27992 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27995 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27997 this.markdownEl.on('click', this.toggleTextEdit, this);
27998 this.on('blur', this.toggleTextEdit, this);
27999 this.on('specialkey', this.resizeTextArea, this);
28002 toggleTextEdit : function()
28004 var sh = this.markdownEl.getHeight();
28005 this.inputEl().addClass('d-none');
28006 this.markdownEl.addClass('d-none');
28007 if (!this.editing) {
28009 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28010 this.inputEl().removeClass('d-none');
28011 this.inputEl().focus();
28012 this.editing = true;
28015 // show showdown...
28016 this.updateMarkdown();
28017 this.markdownEl.removeClass('d-none');
28018 this.editing = false;
28021 updateMarkdown : function()
28023 if (this.getValue() == '') {
28024 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28028 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28031 resizeTextArea: function () {
28034 Roo.log([sh, this.getValue().split("\n").length * 30]);
28035 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28037 setValue : function(val)
28039 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28040 if (!this.editing) {
28041 this.updateMarkdown();
28047 if (!this.editing) {
28048 this.toggleTextEdit();
28056 * Ext JS Library 1.1.1
28057 * Copyright(c) 2006-2007, Ext JS, LLC.
28059 * Originally Released Under LGPL - original licence link has changed is not relivant.
28062 * <script type="text/javascript">
28066 * @class Roo.bootstrap.PagingToolbar
28067 * @extends Roo.bootstrap.NavSimplebar
28068 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28070 * Create a new PagingToolbar
28071 * @param {Object} config The config object
28072 * @param {Roo.data.Store} store
28074 Roo.bootstrap.PagingToolbar = function(config)
28076 // old args format still supported... - xtype is prefered..
28077 // created from xtype...
28079 this.ds = config.dataSource;
28081 if (config.store && !this.ds) {
28082 this.store= Roo.factory(config.store, Roo.data);
28083 this.ds = this.store;
28084 this.ds.xmodule = this.xmodule || false;
28087 this.toolbarItems = [];
28088 if (config.items) {
28089 this.toolbarItems = config.items;
28092 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28097 this.bind(this.ds);
28100 if (Roo.bootstrap.version == 4) {
28101 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28103 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28108 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28110 * @cfg {Roo.data.Store} dataSource
28111 * The underlying data store providing the paged data
28114 * @cfg {String/HTMLElement/Element} container
28115 * container The id or element that will contain the toolbar
28118 * @cfg {Boolean} displayInfo
28119 * True to display the displayMsg (defaults to false)
28122 * @cfg {Number} pageSize
28123 * The number of records to display per page (defaults to 20)
28127 * @cfg {String} displayMsg
28128 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28130 displayMsg : 'Displaying {0} - {1} of {2}',
28132 * @cfg {String} emptyMsg
28133 * The message to display when no records are found (defaults to "No data to display")
28135 emptyMsg : 'No data to display',
28137 * Customizable piece of the default paging text (defaults to "Page")
28140 beforePageText : "Page",
28142 * Customizable piece of the default paging text (defaults to "of %0")
28145 afterPageText : "of {0}",
28147 * Customizable piece of the default paging text (defaults to "First Page")
28150 firstText : "First Page",
28152 * Customizable piece of the default paging text (defaults to "Previous Page")
28155 prevText : "Previous Page",
28157 * Customizable piece of the default paging text (defaults to "Next Page")
28160 nextText : "Next Page",
28162 * Customizable piece of the default paging text (defaults to "Last Page")
28165 lastText : "Last Page",
28167 * Customizable piece of the default paging text (defaults to "Refresh")
28170 refreshText : "Refresh",
28174 onRender : function(ct, position)
28176 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28177 this.navgroup.parentId = this.id;
28178 this.navgroup.onRender(this.el, null);
28179 // add the buttons to the navgroup
28181 if(this.displayInfo){
28182 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28183 this.displayEl = this.el.select('.x-paging-info', true).first();
28184 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28185 // this.displayEl = navel.el.select('span',true).first();
28191 Roo.each(_this.buttons, function(e){ // this might need to use render????
28192 Roo.factory(e).render(_this.el);
28196 Roo.each(_this.toolbarItems, function(e) {
28197 _this.navgroup.addItem(e);
28201 this.first = this.navgroup.addItem({
28202 tooltip: this.firstText,
28203 cls: "prev btn-outline-secondary",
28204 html : ' <i class="fa fa-step-backward"></i>',
28206 preventDefault: true,
28207 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28210 this.prev = this.navgroup.addItem({
28211 tooltip: this.prevText,
28212 cls: "prev btn-outline-secondary",
28213 html : ' <i class="fa fa-backward"></i>',
28215 preventDefault: true,
28216 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28218 //this.addSeparator();
28221 var field = this.navgroup.addItem( {
28223 cls : 'x-paging-position btn-outline-secondary',
28225 html : this.beforePageText +
28226 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28227 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28230 this.field = field.el.select('input', true).first();
28231 this.field.on("keydown", this.onPagingKeydown, this);
28232 this.field.on("focus", function(){this.dom.select();});
28235 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28236 //this.field.setHeight(18);
28237 //this.addSeparator();
28238 this.next = this.navgroup.addItem({
28239 tooltip: this.nextText,
28240 cls: "next btn-outline-secondary",
28241 html : ' <i class="fa fa-forward"></i>',
28243 preventDefault: true,
28244 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28246 this.last = this.navgroup.addItem({
28247 tooltip: this.lastText,
28248 html : ' <i class="fa fa-step-forward"></i>',
28249 cls: "next btn-outline-secondary",
28251 preventDefault: true,
28252 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28254 //this.addSeparator();
28255 this.loading = this.navgroup.addItem({
28256 tooltip: this.refreshText,
28257 cls: "btn-outline-secondary",
28258 html : ' <i class="fa fa-refresh"></i>',
28259 preventDefault: true,
28260 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28266 updateInfo : function(){
28267 if(this.displayEl){
28268 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28269 var msg = count == 0 ?
28273 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28275 this.displayEl.update(msg);
28280 onLoad : function(ds, r, o)
28282 this.cursor = o.params && o.params.start ? o.params.start : 0;
28284 var d = this.getPageData(),
28289 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28290 this.field.dom.value = ap;
28291 this.first.setDisabled(ap == 1);
28292 this.prev.setDisabled(ap == 1);
28293 this.next.setDisabled(ap == ps);
28294 this.last.setDisabled(ap == ps);
28295 this.loading.enable();
28300 getPageData : function(){
28301 var total = this.ds.getTotalCount();
28304 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28305 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28310 onLoadError : function(){
28311 this.loading.enable();
28315 onPagingKeydown : function(e){
28316 var k = e.getKey();
28317 var d = this.getPageData();
28319 var v = this.field.dom.value, pageNum;
28320 if(!v || isNaN(pageNum = parseInt(v, 10))){
28321 this.field.dom.value = d.activePage;
28324 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28325 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28328 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))
28330 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28331 this.field.dom.value = pageNum;
28332 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28335 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28337 var v = this.field.dom.value, pageNum;
28338 var increment = (e.shiftKey) ? 10 : 1;
28339 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28342 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28343 this.field.dom.value = d.activePage;
28346 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28348 this.field.dom.value = parseInt(v, 10) + increment;
28349 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28350 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28357 beforeLoad : function(){
28359 this.loading.disable();
28364 onClick : function(which){
28373 ds.load({params:{start: 0, limit: this.pageSize}});
28376 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28379 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28382 var total = ds.getTotalCount();
28383 var extra = total % this.pageSize;
28384 var lastStart = extra ? (total - extra) : total-this.pageSize;
28385 ds.load({params:{start: lastStart, limit: this.pageSize}});
28388 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28394 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28395 * @param {Roo.data.Store} store The data store to unbind
28397 unbind : function(ds){
28398 ds.un("beforeload", this.beforeLoad, this);
28399 ds.un("load", this.onLoad, this);
28400 ds.un("loadexception", this.onLoadError, this);
28401 ds.un("remove", this.updateInfo, this);
28402 ds.un("add", this.updateInfo, this);
28403 this.ds = undefined;
28407 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28408 * @param {Roo.data.Store} store The data store to bind
28410 bind : function(ds){
28411 ds.on("beforeload", this.beforeLoad, this);
28412 ds.on("load", this.onLoad, this);
28413 ds.on("loadexception", this.onLoadError, this);
28414 ds.on("remove", this.updateInfo, this);
28415 ds.on("add", this.updateInfo, this);
28426 * @class Roo.bootstrap.MessageBar
28427 * @extends Roo.bootstrap.Component
28428 * Bootstrap MessageBar class
28429 * @cfg {String} html contents of the MessageBar
28430 * @cfg {String} weight (info | success | warning | danger) default info
28431 * @cfg {String} beforeClass insert the bar before the given class
28432 * @cfg {Boolean} closable (true | false) default false
28433 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28436 * Create a new Element
28437 * @param {Object} config The config object
28440 Roo.bootstrap.MessageBar = function(config){
28441 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28444 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28450 beforeClass: 'bootstrap-sticky-wrap',
28452 getAutoCreate : function(){
28456 cls: 'alert alert-dismissable alert-' + this.weight,
28461 html: this.html || ''
28467 cfg.cls += ' alert-messages-fixed';
28481 onRender : function(ct, position)
28483 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28486 var cfg = Roo.apply({}, this.getAutoCreate());
28490 cfg.cls += ' ' + this.cls;
28493 cfg.style = this.style;
28495 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28497 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28500 this.el.select('>button.close').on('click', this.hide, this);
28506 if (!this.rendered) {
28512 this.fireEvent('show', this);
28518 if (!this.rendered) {
28524 this.fireEvent('hide', this);
28527 update : function()
28529 // var e = this.el.dom.firstChild;
28531 // if(this.closable){
28532 // e = e.nextSibling;
28535 // e.data = this.html || '';
28537 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28553 * @class Roo.bootstrap.Graph
28554 * @extends Roo.bootstrap.Component
28555 * Bootstrap Graph class
28559 @cfg {String} graphtype bar | vbar | pie
28560 @cfg {number} g_x coodinator | centre x (pie)
28561 @cfg {number} g_y coodinator | centre y (pie)
28562 @cfg {number} g_r radius (pie)
28563 @cfg {number} g_height height of the chart (respected by all elements in the set)
28564 @cfg {number} g_width width of the chart (respected by all elements in the set)
28565 @cfg {Object} title The title of the chart
28568 -opts (object) options for the chart
28570 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28571 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28573 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.
28574 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28576 o stretch (boolean)
28578 -opts (object) options for the pie
28581 o startAngle (number)
28582 o endAngle (number)
28586 * Create a new Input
28587 * @param {Object} config The config object
28590 Roo.bootstrap.Graph = function(config){
28591 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28597 * The img click event for the img.
28598 * @param {Roo.EventObject} e
28604 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28615 //g_colors: this.colors,
28622 getAutoCreate : function(){
28633 onRender : function(ct,position){
28636 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28638 if (typeof(Raphael) == 'undefined') {
28639 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28643 this.raphael = Raphael(this.el.dom);
28645 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28646 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28647 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28648 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28650 r.text(160, 10, "Single Series Chart").attr(txtattr);
28651 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28652 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28653 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28655 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28656 r.barchart(330, 10, 300, 220, data1);
28657 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28658 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28661 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28662 // r.barchart(30, 30, 560, 250, xdata, {
28663 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28664 // axis : "0 0 1 1",
28665 // axisxlabels : xdata
28666 // //yvalues : cols,
28669 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28671 // this.load(null,xdata,{
28672 // axis : "0 0 1 1",
28673 // axisxlabels : xdata
28678 load : function(graphtype,xdata,opts)
28680 this.raphael.clear();
28682 graphtype = this.graphtype;
28687 var r = this.raphael,
28688 fin = function () {
28689 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28691 fout = function () {
28692 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28694 pfin = function() {
28695 this.sector.stop();
28696 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28699 this.label[0].stop();
28700 this.label[0].attr({ r: 7.5 });
28701 this.label[1].attr({ "font-weight": 800 });
28704 pfout = function() {
28705 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28708 this.label[0].animate({ r: 5 }, 500, "bounce");
28709 this.label[1].attr({ "font-weight": 400 });
28715 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28718 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28721 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28722 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28724 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28731 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28736 setTitle: function(o)
28741 initEvents: function() {
28744 this.el.on('click', this.onClick, this);
28748 onClick : function(e)
28750 Roo.log('img onclick');
28751 this.fireEvent('click', this, e);
28763 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28766 * @class Roo.bootstrap.dash.NumberBox
28767 * @extends Roo.bootstrap.Component
28768 * Bootstrap NumberBox class
28769 * @cfg {String} headline Box headline
28770 * @cfg {String} content Box content
28771 * @cfg {String} icon Box icon
28772 * @cfg {String} footer Footer text
28773 * @cfg {String} fhref Footer href
28776 * Create a new NumberBox
28777 * @param {Object} config The config object
28781 Roo.bootstrap.dash.NumberBox = function(config){
28782 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28786 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28795 getAutoCreate : function(){
28799 cls : 'small-box ',
28807 cls : 'roo-headline',
28808 html : this.headline
28812 cls : 'roo-content',
28813 html : this.content
28827 cls : 'ion ' + this.icon
28836 cls : 'small-box-footer',
28837 href : this.fhref || '#',
28841 cfg.cn.push(footer);
28848 onRender : function(ct,position){
28849 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28856 setHeadline: function (value)
28858 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28861 setFooter: function (value, href)
28863 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28866 this.el.select('a.small-box-footer',true).first().attr('href', href);
28871 setContent: function (value)
28873 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28876 initEvents: function()
28890 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28893 * @class Roo.bootstrap.dash.TabBox
28894 * @extends Roo.bootstrap.Component
28895 * Bootstrap TabBox class
28896 * @cfg {String} title Title of the TabBox
28897 * @cfg {String} icon Icon of the TabBox
28898 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28899 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28902 * Create a new TabBox
28903 * @param {Object} config The config object
28907 Roo.bootstrap.dash.TabBox = function(config){
28908 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28913 * When a pane is added
28914 * @param {Roo.bootstrap.dash.TabPane} pane
28918 * @event activatepane
28919 * When a pane is activated
28920 * @param {Roo.bootstrap.dash.TabPane} pane
28922 "activatepane" : true
28930 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28935 tabScrollable : false,
28937 getChildContainer : function()
28939 return this.el.select('.tab-content', true).first();
28942 getAutoCreate : function(){
28946 cls: 'pull-left header',
28954 cls: 'fa ' + this.icon
28960 cls: 'nav nav-tabs pull-right',
28966 if(this.tabScrollable){
28973 cls: 'nav nav-tabs pull-right',
28984 cls: 'nav-tabs-custom',
28989 cls: 'tab-content no-padding',
28997 initEvents : function()
28999 //Roo.log('add add pane handler');
29000 this.on('addpane', this.onAddPane, this);
29003 * Updates the box title
29004 * @param {String} html to set the title to.
29006 setTitle : function(value)
29008 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29010 onAddPane : function(pane)
29012 this.panes.push(pane);
29013 //Roo.log('addpane');
29015 // tabs are rendere left to right..
29016 if(!this.showtabs){
29020 var ctr = this.el.select('.nav-tabs', true).first();
29023 var existing = ctr.select('.nav-tab',true);
29024 var qty = existing.getCount();;
29027 var tab = ctr.createChild({
29029 cls : 'nav-tab' + (qty ? '' : ' active'),
29037 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29040 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29042 pane.el.addClass('active');
29047 onTabClick : function(ev,un,ob,pane)
29049 //Roo.log('tab - prev default');
29050 ev.preventDefault();
29053 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29054 pane.tab.addClass('active');
29055 //Roo.log(pane.title);
29056 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29057 // technically we should have a deactivate event.. but maybe add later.
29058 // and it should not de-activate the selected tab...
29059 this.fireEvent('activatepane', pane);
29060 pane.el.addClass('active');
29061 pane.fireEvent('activate');
29066 getActivePane : function()
29069 Roo.each(this.panes, function(p) {
29070 if(p.el.hasClass('active')){
29091 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29093 * @class Roo.bootstrap.TabPane
29094 * @extends Roo.bootstrap.Component
29095 * Bootstrap TabPane class
29096 * @cfg {Boolean} active (false | true) Default false
29097 * @cfg {String} title title of panel
29101 * Create a new TabPane
29102 * @param {Object} config The config object
29105 Roo.bootstrap.dash.TabPane = function(config){
29106 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29112 * When a pane is activated
29113 * @param {Roo.bootstrap.dash.TabPane} pane
29120 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29125 // the tabBox that this is attached to.
29128 getAutoCreate : function()
29136 cfg.cls += ' active';
29141 initEvents : function()
29143 //Roo.log('trigger add pane handler');
29144 this.parent().fireEvent('addpane', this)
29148 * Updates the tab title
29149 * @param {String} html to set the title to.
29151 setTitle: function(str)
29157 this.tab.select('a', true).first().dom.innerHTML = str;
29174 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29177 * @class Roo.bootstrap.menu.Menu
29178 * @extends Roo.bootstrap.Component
29179 * Bootstrap Menu class - container for Menu
29180 * @cfg {String} html Text of the menu
29181 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29182 * @cfg {String} icon Font awesome icon
29183 * @cfg {String} pos Menu align to (top | bottom) default bottom
29187 * Create a new Menu
29188 * @param {Object} config The config object
29192 Roo.bootstrap.menu.Menu = function(config){
29193 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29197 * @event beforeshow
29198 * Fires before this menu is displayed
29199 * @param {Roo.bootstrap.menu.Menu} this
29203 * @event beforehide
29204 * Fires before this menu is hidden
29205 * @param {Roo.bootstrap.menu.Menu} this
29210 * Fires after this menu is displayed
29211 * @param {Roo.bootstrap.menu.Menu} this
29216 * Fires after this menu is hidden
29217 * @param {Roo.bootstrap.menu.Menu} this
29222 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29223 * @param {Roo.bootstrap.menu.Menu} this
29224 * @param {Roo.EventObject} e
29231 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29235 weight : 'default',
29240 getChildContainer : function() {
29241 if(this.isSubMenu){
29245 return this.el.select('ul.dropdown-menu', true).first();
29248 getAutoCreate : function()
29253 cls : 'roo-menu-text',
29261 cls : 'fa ' + this.icon
29272 cls : 'dropdown-button btn btn-' + this.weight,
29277 cls : 'dropdown-toggle btn btn-' + this.weight,
29287 cls : 'dropdown-menu'
29293 if(this.pos == 'top'){
29294 cfg.cls += ' dropup';
29297 if(this.isSubMenu){
29300 cls : 'dropdown-menu'
29307 onRender : function(ct, position)
29309 this.isSubMenu = ct.hasClass('dropdown-submenu');
29311 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29314 initEvents : function()
29316 if(this.isSubMenu){
29320 this.hidden = true;
29322 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29323 this.triggerEl.on('click', this.onTriggerPress, this);
29325 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29326 this.buttonEl.on('click', this.onClick, this);
29332 if(this.isSubMenu){
29336 return this.el.select('ul.dropdown-menu', true).first();
29339 onClick : function(e)
29341 this.fireEvent("click", this, e);
29344 onTriggerPress : function(e)
29346 if (this.isVisible()) {
29353 isVisible : function(){
29354 return !this.hidden;
29359 this.fireEvent("beforeshow", this);
29361 this.hidden = false;
29362 this.el.addClass('open');
29364 Roo.get(document).on("mouseup", this.onMouseUp, this);
29366 this.fireEvent("show", this);
29373 this.fireEvent("beforehide", this);
29375 this.hidden = true;
29376 this.el.removeClass('open');
29378 Roo.get(document).un("mouseup", this.onMouseUp);
29380 this.fireEvent("hide", this);
29383 onMouseUp : function()
29397 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29400 * @class Roo.bootstrap.menu.Item
29401 * @extends Roo.bootstrap.Component
29402 * Bootstrap MenuItem class
29403 * @cfg {Boolean} submenu (true | false) default false
29404 * @cfg {String} html text of the item
29405 * @cfg {String} href the link
29406 * @cfg {Boolean} disable (true | false) default false
29407 * @cfg {Boolean} preventDefault (true | false) default true
29408 * @cfg {String} icon Font awesome icon
29409 * @cfg {String} pos Submenu align to (left | right) default right
29413 * Create a new Item
29414 * @param {Object} config The config object
29418 Roo.bootstrap.menu.Item = function(config){
29419 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29423 * Fires when the mouse is hovering over this menu
29424 * @param {Roo.bootstrap.menu.Item} this
29425 * @param {Roo.EventObject} e
29430 * Fires when the mouse exits this menu
29431 * @param {Roo.bootstrap.menu.Item} this
29432 * @param {Roo.EventObject} e
29438 * The raw click event for the entire grid.
29439 * @param {Roo.EventObject} e
29445 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29450 preventDefault: true,
29455 getAutoCreate : function()
29460 cls : 'roo-menu-item-text',
29468 cls : 'fa ' + this.icon
29477 href : this.href || '#',
29484 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29488 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29490 if(this.pos == 'left'){
29491 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29498 initEvents : function()
29500 this.el.on('mouseover', this.onMouseOver, this);
29501 this.el.on('mouseout', this.onMouseOut, this);
29503 this.el.select('a', true).first().on('click', this.onClick, this);
29507 onClick : function(e)
29509 if(this.preventDefault){
29510 e.preventDefault();
29513 this.fireEvent("click", this, e);
29516 onMouseOver : function(e)
29518 if(this.submenu && this.pos == 'left'){
29519 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29522 this.fireEvent("mouseover", this, e);
29525 onMouseOut : function(e)
29527 this.fireEvent("mouseout", this, e);
29539 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29542 * @class Roo.bootstrap.menu.Separator
29543 * @extends Roo.bootstrap.Component
29544 * Bootstrap Separator class
29547 * Create a new Separator
29548 * @param {Object} config The config object
29552 Roo.bootstrap.menu.Separator = function(config){
29553 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29556 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29558 getAutoCreate : function(){
29561 cls: 'dropdown-divider divider'
29579 * @class Roo.bootstrap.Tooltip
29580 * Bootstrap Tooltip class
29581 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29582 * to determine which dom element triggers the tooltip.
29584 * It needs to add support for additional attributes like tooltip-position
29587 * Create a new Toolti
29588 * @param {Object} config The config object
29591 Roo.bootstrap.Tooltip = function(config){
29592 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29594 this.alignment = Roo.bootstrap.Tooltip.alignment;
29596 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29597 this.alignment = config.alignment;
29602 Roo.apply(Roo.bootstrap.Tooltip, {
29604 * @function init initialize tooltip monitoring.
29608 currentTip : false,
29609 currentRegion : false,
29615 Roo.get(document).on('mouseover', this.enter ,this);
29616 Roo.get(document).on('mouseout', this.leave, this);
29619 this.currentTip = new Roo.bootstrap.Tooltip();
29622 enter : function(ev)
29624 var dom = ev.getTarget();
29626 //Roo.log(['enter',dom]);
29627 var el = Roo.fly(dom);
29628 if (this.currentEl) {
29630 //Roo.log(this.currentEl);
29631 //Roo.log(this.currentEl.contains(dom));
29632 if (this.currentEl == el) {
29635 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29641 if (this.currentTip.el) {
29642 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29646 if(!el || el.dom == document){
29652 if (!el.attr('tooltip')) {
29653 pel = el.findParent("[tooltip]");
29655 bindEl = Roo.get(pel);
29661 // you can not look for children, as if el is the body.. then everythign is the child..
29662 if (!pel && !el.attr('tooltip')) { //
29663 if (!el.select("[tooltip]").elements.length) {
29666 // is the mouse over this child...?
29667 bindEl = el.select("[tooltip]").first();
29668 var xy = ev.getXY();
29669 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29670 //Roo.log("not in region.");
29673 //Roo.log("child element over..");
29676 this.currentEl = el;
29677 this.currentTip.bind(bindEl);
29678 this.currentRegion = Roo.lib.Region.getRegion(dom);
29679 this.currentTip.enter();
29682 leave : function(ev)
29684 var dom = ev.getTarget();
29685 //Roo.log(['leave',dom]);
29686 if (!this.currentEl) {
29691 if (dom != this.currentEl.dom) {
29694 var xy = ev.getXY();
29695 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29698 // only activate leave if mouse cursor is outside... bounding box..
29703 if (this.currentTip) {
29704 this.currentTip.leave();
29706 //Roo.log('clear currentEl');
29707 this.currentEl = false;
29712 'left' : ['r-l', [-2,0], 'right'],
29713 'right' : ['l-r', [2,0], 'left'],
29714 'bottom' : ['t-b', [0,2], 'top'],
29715 'top' : [ 'b-t', [0,-2], 'bottom']
29721 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29726 delay : null, // can be { show : 300 , hide: 500}
29730 hoverState : null, //???
29732 placement : 'bottom',
29736 getAutoCreate : function(){
29743 cls : 'tooltip-arrow arrow'
29746 cls : 'tooltip-inner'
29753 bind : function(el)
29758 initEvents : function()
29760 this.arrowEl = this.el.select('.arrow', true).first();
29761 this.innerEl = this.el.select('.tooltip-inner', true).first();
29764 enter : function () {
29766 if (this.timeout != null) {
29767 clearTimeout(this.timeout);
29770 this.hoverState = 'in';
29771 //Roo.log("enter - show");
29772 if (!this.delay || !this.delay.show) {
29777 this.timeout = setTimeout(function () {
29778 if (_t.hoverState == 'in') {
29781 }, this.delay.show);
29785 clearTimeout(this.timeout);
29787 this.hoverState = 'out';
29788 if (!this.delay || !this.delay.hide) {
29794 this.timeout = setTimeout(function () {
29795 //Roo.log("leave - timeout");
29797 if (_t.hoverState == 'out') {
29799 Roo.bootstrap.Tooltip.currentEl = false;
29804 show : function (msg)
29807 this.render(document.body);
29810 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29812 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29814 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29816 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29817 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29819 var placement = typeof this.placement == 'function' ?
29820 this.placement.call(this, this.el, on_el) :
29823 var autoToken = /\s?auto?\s?/i;
29824 var autoPlace = autoToken.test(placement);
29826 placement = placement.replace(autoToken, '') || 'top';
29830 //this.el.setXY([0,0]);
29832 //this.el.dom.style.display='block';
29834 //this.el.appendTo(on_el);
29836 var p = this.getPosition();
29837 var box = this.el.getBox();
29843 var align = this.alignment[placement];
29845 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29847 if(placement == 'top' || placement == 'bottom'){
29849 placement = 'right';
29852 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29853 placement = 'left';
29856 var scroll = Roo.select('body', true).first().getScroll();
29858 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29862 align = this.alignment[placement];
29864 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29868 var elems = document.getElementsByTagName('div');
29869 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29870 for (var i = 0; i < elems.length; i++) {
29871 var zindex = Number.parseInt(
29872 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29875 if (zindex > highest) {
29882 this.el.dom.style.zIndex = highest;
29884 this.el.alignTo(this.bindEl, align[0],align[1]);
29885 //var arrow = this.el.select('.arrow',true).first();
29886 //arrow.set(align[2],
29888 this.el.addClass(placement);
29889 this.el.addClass("bs-tooltip-"+ placement);
29891 this.el.addClass('in fade show');
29893 this.hoverState = null;
29895 if (this.el.hasClass('fade')) {
29910 //this.el.setXY([0,0]);
29911 this.el.removeClass(['show', 'in']);
29927 * @class Roo.bootstrap.LocationPicker
29928 * @extends Roo.bootstrap.Component
29929 * Bootstrap LocationPicker class
29930 * @cfg {Number} latitude Position when init default 0
29931 * @cfg {Number} longitude Position when init default 0
29932 * @cfg {Number} zoom default 15
29933 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29934 * @cfg {Boolean} mapTypeControl default false
29935 * @cfg {Boolean} disableDoubleClickZoom default false
29936 * @cfg {Boolean} scrollwheel default true
29937 * @cfg {Boolean} streetViewControl default false
29938 * @cfg {Number} radius default 0
29939 * @cfg {String} locationName
29940 * @cfg {Boolean} draggable default true
29941 * @cfg {Boolean} enableAutocomplete default false
29942 * @cfg {Boolean} enableReverseGeocode default true
29943 * @cfg {String} markerTitle
29946 * Create a new LocationPicker
29947 * @param {Object} config The config object
29951 Roo.bootstrap.LocationPicker = function(config){
29953 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29958 * Fires when the picker initialized.
29959 * @param {Roo.bootstrap.LocationPicker} this
29960 * @param {Google Location} location
29964 * @event positionchanged
29965 * Fires when the picker position changed.
29966 * @param {Roo.bootstrap.LocationPicker} this
29967 * @param {Google Location} location
29969 positionchanged : true,
29972 * Fires when the map resize.
29973 * @param {Roo.bootstrap.LocationPicker} this
29978 * Fires when the map show.
29979 * @param {Roo.bootstrap.LocationPicker} this
29984 * Fires when the map hide.
29985 * @param {Roo.bootstrap.LocationPicker} this
29990 * Fires when click the map.
29991 * @param {Roo.bootstrap.LocationPicker} this
29992 * @param {Map event} e
29996 * @event mapRightClick
29997 * Fires when right click the map.
29998 * @param {Roo.bootstrap.LocationPicker} this
29999 * @param {Map event} e
30001 mapRightClick : true,
30003 * @event markerClick
30004 * Fires when click the marker.
30005 * @param {Roo.bootstrap.LocationPicker} this
30006 * @param {Map event} e
30008 markerClick : true,
30010 * @event markerRightClick
30011 * Fires when right click the marker.
30012 * @param {Roo.bootstrap.LocationPicker} this
30013 * @param {Map event} e
30015 markerRightClick : true,
30017 * @event OverlayViewDraw
30018 * Fires when OverlayView Draw
30019 * @param {Roo.bootstrap.LocationPicker} this
30021 OverlayViewDraw : true,
30023 * @event OverlayViewOnAdd
30024 * Fires when OverlayView Draw
30025 * @param {Roo.bootstrap.LocationPicker} this
30027 OverlayViewOnAdd : true,
30029 * @event OverlayViewOnRemove
30030 * Fires when OverlayView Draw
30031 * @param {Roo.bootstrap.LocationPicker} this
30033 OverlayViewOnRemove : true,
30035 * @event OverlayViewShow
30036 * Fires when OverlayView Draw
30037 * @param {Roo.bootstrap.LocationPicker} this
30038 * @param {Pixel} cpx
30040 OverlayViewShow : true,
30042 * @event OverlayViewHide
30043 * Fires when OverlayView Draw
30044 * @param {Roo.bootstrap.LocationPicker} this
30046 OverlayViewHide : true,
30048 * @event loadexception
30049 * Fires when load google lib failed.
30050 * @param {Roo.bootstrap.LocationPicker} this
30052 loadexception : true
30057 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30059 gMapContext: false,
30065 mapTypeControl: false,
30066 disableDoubleClickZoom: false,
30068 streetViewControl: false,
30072 enableAutocomplete: false,
30073 enableReverseGeocode: true,
30076 getAutoCreate: function()
30081 cls: 'roo-location-picker'
30087 initEvents: function(ct, position)
30089 if(!this.el.getWidth() || this.isApplied()){
30093 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30098 initial: function()
30100 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30101 this.fireEvent('loadexception', this);
30105 if(!this.mapTypeId){
30106 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30109 this.gMapContext = this.GMapContext();
30111 this.initOverlayView();
30113 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30117 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30118 _this.setPosition(_this.gMapContext.marker.position);
30121 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30122 _this.fireEvent('mapClick', this, event);
30126 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30127 _this.fireEvent('mapRightClick', this, event);
30131 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30132 _this.fireEvent('markerClick', this, event);
30136 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30137 _this.fireEvent('markerRightClick', this, event);
30141 this.setPosition(this.gMapContext.location);
30143 this.fireEvent('initial', this, this.gMapContext.location);
30146 initOverlayView: function()
30150 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30154 _this.fireEvent('OverlayViewDraw', _this);
30159 _this.fireEvent('OverlayViewOnAdd', _this);
30162 onRemove: function()
30164 _this.fireEvent('OverlayViewOnRemove', _this);
30167 show: function(cpx)
30169 _this.fireEvent('OverlayViewShow', _this, cpx);
30174 _this.fireEvent('OverlayViewHide', _this);
30180 fromLatLngToContainerPixel: function(event)
30182 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30185 isApplied: function()
30187 return this.getGmapContext() == false ? false : true;
30190 getGmapContext: function()
30192 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30195 GMapContext: function()
30197 var position = new google.maps.LatLng(this.latitude, this.longitude);
30199 var _map = new google.maps.Map(this.el.dom, {
30202 mapTypeId: this.mapTypeId,
30203 mapTypeControl: this.mapTypeControl,
30204 disableDoubleClickZoom: this.disableDoubleClickZoom,
30205 scrollwheel: this.scrollwheel,
30206 streetViewControl: this.streetViewControl,
30207 locationName: this.locationName,
30208 draggable: this.draggable,
30209 enableAutocomplete: this.enableAutocomplete,
30210 enableReverseGeocode: this.enableReverseGeocode
30213 var _marker = new google.maps.Marker({
30214 position: position,
30216 title: this.markerTitle,
30217 draggable: this.draggable
30224 location: position,
30225 radius: this.radius,
30226 locationName: this.locationName,
30227 addressComponents: {
30228 formatted_address: null,
30229 addressLine1: null,
30230 addressLine2: null,
30232 streetNumber: null,
30236 stateOrProvince: null
30239 domContainer: this.el.dom,
30240 geodecoder: new google.maps.Geocoder()
30244 drawCircle: function(center, radius, options)
30246 if (this.gMapContext.circle != null) {
30247 this.gMapContext.circle.setMap(null);
30251 options = Roo.apply({}, options, {
30252 strokeColor: "#0000FF",
30253 strokeOpacity: .35,
30255 fillColor: "#0000FF",
30259 options.map = this.gMapContext.map;
30260 options.radius = radius;
30261 options.center = center;
30262 this.gMapContext.circle = new google.maps.Circle(options);
30263 return this.gMapContext.circle;
30269 setPosition: function(location)
30271 this.gMapContext.location = location;
30272 this.gMapContext.marker.setPosition(location);
30273 this.gMapContext.map.panTo(location);
30274 this.drawCircle(location, this.gMapContext.radius, {});
30278 if (this.gMapContext.settings.enableReverseGeocode) {
30279 this.gMapContext.geodecoder.geocode({
30280 latLng: this.gMapContext.location
30281 }, function(results, status) {
30283 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30284 _this.gMapContext.locationName = results[0].formatted_address;
30285 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30287 _this.fireEvent('positionchanged', this, location);
30294 this.fireEvent('positionchanged', this, location);
30299 google.maps.event.trigger(this.gMapContext.map, "resize");
30301 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30303 this.fireEvent('resize', this);
30306 setPositionByLatLng: function(latitude, longitude)
30308 this.setPosition(new google.maps.LatLng(latitude, longitude));
30311 getCurrentPosition: function()
30314 latitude: this.gMapContext.location.lat(),
30315 longitude: this.gMapContext.location.lng()
30319 getAddressName: function()
30321 return this.gMapContext.locationName;
30324 getAddressComponents: function()
30326 return this.gMapContext.addressComponents;
30329 address_component_from_google_geocode: function(address_components)
30333 for (var i = 0; i < address_components.length; i++) {
30334 var component = address_components[i];
30335 if (component.types.indexOf("postal_code") >= 0) {
30336 result.postalCode = component.short_name;
30337 } else if (component.types.indexOf("street_number") >= 0) {
30338 result.streetNumber = component.short_name;
30339 } else if (component.types.indexOf("route") >= 0) {
30340 result.streetName = component.short_name;
30341 } else if (component.types.indexOf("neighborhood") >= 0) {
30342 result.city = component.short_name;
30343 } else if (component.types.indexOf("locality") >= 0) {
30344 result.city = component.short_name;
30345 } else if (component.types.indexOf("sublocality") >= 0) {
30346 result.district = component.short_name;
30347 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30348 result.stateOrProvince = component.short_name;
30349 } else if (component.types.indexOf("country") >= 0) {
30350 result.country = component.short_name;
30354 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30355 result.addressLine2 = "";
30359 setZoomLevel: function(zoom)
30361 this.gMapContext.map.setZoom(zoom);
30374 this.fireEvent('show', this);
30385 this.fireEvent('hide', this);
30390 Roo.apply(Roo.bootstrap.LocationPicker, {
30392 OverlayView : function(map, options)
30394 options = options || {};
30401 * @class Roo.bootstrap.Alert
30402 * @extends Roo.bootstrap.Component
30403 * Bootstrap Alert class - shows an alert area box
30405 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30406 Enter a valid email address
30409 * @cfg {String} title The title of alert
30410 * @cfg {String} html The content of alert
30411 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30412 * @cfg {String} fa font-awesomeicon
30413 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30414 * @cfg {Boolean} close true to show a x closer
30418 * Create a new alert
30419 * @param {Object} config The config object
30423 Roo.bootstrap.Alert = function(config){
30424 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30428 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30434 faicon: false, // BC
30438 getAutoCreate : function()
30450 style : this.close ? '' : 'display:none'
30454 cls : 'roo-alert-icon'
30459 cls : 'roo-alert-title',
30464 cls : 'roo-alert-text',
30471 cfg.cn[0].cls += ' fa ' + this.faicon;
30474 cfg.cn[0].cls += ' fa ' + this.fa;
30478 cfg.cls += ' alert-' + this.weight;
30484 initEvents: function()
30486 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30487 this.titleEl = this.el.select('.roo-alert-title',true).first();
30488 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30489 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30490 if (this.seconds > 0) {
30491 this.hide.defer(this.seconds, this);
30495 * Set the Title Message HTML
30496 * @param {String} html
30498 setTitle : function(str)
30500 this.titleEl.dom.innerHTML = str;
30504 * Set the Body Message HTML
30505 * @param {String} html
30507 setHtml : function(str)
30509 this.htmlEl.dom.innerHTML = str;
30512 * Set the Weight of the alert
30513 * @param {String} (success|info|warning|danger) weight
30516 setWeight : function(weight)
30519 this.el.removeClass('alert-' + this.weight);
30522 this.weight = weight;
30524 this.el.addClass('alert-' + this.weight);
30527 * Set the Icon of the alert
30528 * @param {String} see fontawsome names (name without the 'fa-' bit)
30530 setIcon : function(icon)
30533 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30536 this.faicon = icon;
30538 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30563 * @class Roo.bootstrap.UploadCropbox
30564 * @extends Roo.bootstrap.Component
30565 * Bootstrap UploadCropbox class
30566 * @cfg {String} emptyText show when image has been loaded
30567 * @cfg {String} rotateNotify show when image too small to rotate
30568 * @cfg {Number} errorTimeout default 3000
30569 * @cfg {Number} minWidth default 300
30570 * @cfg {Number} minHeight default 300
30571 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30572 * @cfg {Boolean} isDocument (true|false) default false
30573 * @cfg {String} url action url
30574 * @cfg {String} paramName default 'imageUpload'
30575 * @cfg {String} method default POST
30576 * @cfg {Boolean} loadMask (true|false) default true
30577 * @cfg {Boolean} loadingText default 'Loading...'
30580 * Create a new UploadCropbox
30581 * @param {Object} config The config object
30584 Roo.bootstrap.UploadCropbox = function(config){
30585 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30589 * @event beforeselectfile
30590 * Fire before select file
30591 * @param {Roo.bootstrap.UploadCropbox} this
30593 "beforeselectfile" : true,
30596 * Fire after initEvent
30597 * @param {Roo.bootstrap.UploadCropbox} this
30602 * Fire after initEvent
30603 * @param {Roo.bootstrap.UploadCropbox} this
30604 * @param {String} data
30609 * Fire when preparing the file data
30610 * @param {Roo.bootstrap.UploadCropbox} this
30611 * @param {Object} file
30616 * Fire when get exception
30617 * @param {Roo.bootstrap.UploadCropbox} this
30618 * @param {XMLHttpRequest} xhr
30620 "exception" : true,
30622 * @event beforeloadcanvas
30623 * Fire before load the canvas
30624 * @param {Roo.bootstrap.UploadCropbox} this
30625 * @param {String} src
30627 "beforeloadcanvas" : true,
30630 * Fire when trash image
30631 * @param {Roo.bootstrap.UploadCropbox} this
30636 * Fire when download the image
30637 * @param {Roo.bootstrap.UploadCropbox} this
30641 * @event footerbuttonclick
30642 * Fire when footerbuttonclick
30643 * @param {Roo.bootstrap.UploadCropbox} this
30644 * @param {String} type
30646 "footerbuttonclick" : true,
30650 * @param {Roo.bootstrap.UploadCropbox} this
30655 * Fire when rotate the image
30656 * @param {Roo.bootstrap.UploadCropbox} this
30657 * @param {String} pos
30662 * Fire when inspect the file
30663 * @param {Roo.bootstrap.UploadCropbox} this
30664 * @param {Object} file
30669 * Fire when xhr upload the file
30670 * @param {Roo.bootstrap.UploadCropbox} this
30671 * @param {Object} data
30676 * Fire when arrange the file data
30677 * @param {Roo.bootstrap.UploadCropbox} this
30678 * @param {Object} formData
30683 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30686 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30688 emptyText : 'Click to upload image',
30689 rotateNotify : 'Image is too small to rotate',
30690 errorTimeout : 3000,
30704 cropType : 'image/jpeg',
30706 canvasLoaded : false,
30707 isDocument : false,
30709 paramName : 'imageUpload',
30711 loadingText : 'Loading...',
30714 getAutoCreate : function()
30718 cls : 'roo-upload-cropbox',
30722 cls : 'roo-upload-cropbox-selector',
30727 cls : 'roo-upload-cropbox-body',
30728 style : 'cursor:pointer',
30732 cls : 'roo-upload-cropbox-preview'
30736 cls : 'roo-upload-cropbox-thumb'
30740 cls : 'roo-upload-cropbox-empty-notify',
30741 html : this.emptyText
30745 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30746 html : this.rotateNotify
30752 cls : 'roo-upload-cropbox-footer',
30755 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30765 onRender : function(ct, position)
30767 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30769 if (this.buttons.length) {
30771 Roo.each(this.buttons, function(bb) {
30773 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30775 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30781 this.maskEl = this.el;
30785 initEvents : function()
30787 this.urlAPI = (window.createObjectURL && window) ||
30788 (window.URL && URL.revokeObjectURL && URL) ||
30789 (window.webkitURL && webkitURL);
30791 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30792 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30794 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30795 this.selectorEl.hide();
30797 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30798 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30800 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30801 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30802 this.thumbEl.hide();
30804 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30805 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30807 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30808 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30809 this.errorEl.hide();
30811 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30812 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30813 this.footerEl.hide();
30815 this.setThumbBoxSize();
30821 this.fireEvent('initial', this);
30828 window.addEventListener("resize", function() { _this.resize(); } );
30830 this.bodyEl.on('click', this.beforeSelectFile, this);
30833 this.bodyEl.on('touchstart', this.onTouchStart, this);
30834 this.bodyEl.on('touchmove', this.onTouchMove, this);
30835 this.bodyEl.on('touchend', this.onTouchEnd, this);
30839 this.bodyEl.on('mousedown', this.onMouseDown, this);
30840 this.bodyEl.on('mousemove', this.onMouseMove, this);
30841 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30842 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30843 Roo.get(document).on('mouseup', this.onMouseUp, this);
30846 this.selectorEl.on('change', this.onFileSelected, this);
30852 this.baseScale = 1;
30854 this.baseRotate = 1;
30855 this.dragable = false;
30856 this.pinching = false;
30859 this.cropData = false;
30860 this.notifyEl.dom.innerHTML = this.emptyText;
30862 this.selectorEl.dom.value = '';
30866 resize : function()
30868 if(this.fireEvent('resize', this) != false){
30869 this.setThumbBoxPosition();
30870 this.setCanvasPosition();
30874 onFooterButtonClick : function(e, el, o, type)
30877 case 'rotate-left' :
30878 this.onRotateLeft(e);
30880 case 'rotate-right' :
30881 this.onRotateRight(e);
30884 this.beforeSelectFile(e);
30899 this.fireEvent('footerbuttonclick', this, type);
30902 beforeSelectFile : function(e)
30904 e.preventDefault();
30906 if(this.fireEvent('beforeselectfile', this) != false){
30907 this.selectorEl.dom.click();
30911 onFileSelected : function(e)
30913 e.preventDefault();
30915 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30919 var file = this.selectorEl.dom.files[0];
30921 if(this.fireEvent('inspect', this, file) != false){
30922 this.prepare(file);
30927 trash : function(e)
30929 this.fireEvent('trash', this);
30932 download : function(e)
30934 this.fireEvent('download', this);
30937 loadCanvas : function(src)
30939 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30943 this.imageEl = document.createElement('img');
30947 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30949 this.imageEl.src = src;
30953 onLoadCanvas : function()
30955 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30956 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30958 this.bodyEl.un('click', this.beforeSelectFile, this);
30960 this.notifyEl.hide();
30961 this.thumbEl.show();
30962 this.footerEl.show();
30964 this.baseRotateLevel();
30966 if(this.isDocument){
30967 this.setThumbBoxSize();
30970 this.setThumbBoxPosition();
30972 this.baseScaleLevel();
30978 this.canvasLoaded = true;
30981 this.maskEl.unmask();
30986 setCanvasPosition : function()
30988 if(!this.canvasEl){
30992 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30993 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30995 this.previewEl.setLeft(pw);
30996 this.previewEl.setTop(ph);
31000 onMouseDown : function(e)
31004 this.dragable = true;
31005 this.pinching = false;
31007 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31008 this.dragable = false;
31012 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31013 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31017 onMouseMove : function(e)
31021 if(!this.canvasLoaded){
31025 if (!this.dragable){
31029 var minX = Math.ceil(this.thumbEl.getLeft(true));
31030 var minY = Math.ceil(this.thumbEl.getTop(true));
31032 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31033 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31035 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31036 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31038 x = x - this.mouseX;
31039 y = y - this.mouseY;
31041 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31042 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31044 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31045 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31047 this.previewEl.setLeft(bgX);
31048 this.previewEl.setTop(bgY);
31050 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31051 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31054 onMouseUp : function(e)
31058 this.dragable = false;
31061 onMouseWheel : function(e)
31065 this.startScale = this.scale;
31067 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31069 if(!this.zoomable()){
31070 this.scale = this.startScale;
31079 zoomable : function()
31081 var minScale = this.thumbEl.getWidth() / this.minWidth;
31083 if(this.minWidth < this.minHeight){
31084 minScale = this.thumbEl.getHeight() / this.minHeight;
31087 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31088 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31092 (this.rotate == 0 || this.rotate == 180) &&
31094 width > this.imageEl.OriginWidth ||
31095 height > this.imageEl.OriginHeight ||
31096 (width < this.minWidth && height < this.minHeight)
31104 (this.rotate == 90 || this.rotate == 270) &&
31106 width > this.imageEl.OriginWidth ||
31107 height > this.imageEl.OriginHeight ||
31108 (width < this.minHeight && height < this.minWidth)
31115 !this.isDocument &&
31116 (this.rotate == 0 || this.rotate == 180) &&
31118 width < this.minWidth ||
31119 width > this.imageEl.OriginWidth ||
31120 height < this.minHeight ||
31121 height > this.imageEl.OriginHeight
31128 !this.isDocument &&
31129 (this.rotate == 90 || this.rotate == 270) &&
31131 width < this.minHeight ||
31132 width > this.imageEl.OriginWidth ||
31133 height < this.minWidth ||
31134 height > this.imageEl.OriginHeight
31144 onRotateLeft : function(e)
31146 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31148 var minScale = this.thumbEl.getWidth() / this.minWidth;
31150 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31151 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31153 this.startScale = this.scale;
31155 while (this.getScaleLevel() < minScale){
31157 this.scale = this.scale + 1;
31159 if(!this.zoomable()){
31164 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31165 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31170 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31177 this.scale = this.startScale;
31179 this.onRotateFail();
31184 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31186 if(this.isDocument){
31187 this.setThumbBoxSize();
31188 this.setThumbBoxPosition();
31189 this.setCanvasPosition();
31194 this.fireEvent('rotate', this, 'left');
31198 onRotateRight : function(e)
31200 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31202 var minScale = this.thumbEl.getWidth() / this.minWidth;
31204 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31205 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31207 this.startScale = this.scale;
31209 while (this.getScaleLevel() < minScale){
31211 this.scale = this.scale + 1;
31213 if(!this.zoomable()){
31218 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31219 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31224 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31231 this.scale = this.startScale;
31233 this.onRotateFail();
31238 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31240 if(this.isDocument){
31241 this.setThumbBoxSize();
31242 this.setThumbBoxPosition();
31243 this.setCanvasPosition();
31248 this.fireEvent('rotate', this, 'right');
31251 onRotateFail : function()
31253 this.errorEl.show(true);
31257 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31262 this.previewEl.dom.innerHTML = '';
31264 var canvasEl = document.createElement("canvas");
31266 var contextEl = canvasEl.getContext("2d");
31268 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31269 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31270 var center = this.imageEl.OriginWidth / 2;
31272 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31273 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31274 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31275 center = this.imageEl.OriginHeight / 2;
31278 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31280 contextEl.translate(center, center);
31281 contextEl.rotate(this.rotate * Math.PI / 180);
31283 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31285 this.canvasEl = document.createElement("canvas");
31287 this.contextEl = this.canvasEl.getContext("2d");
31289 switch (this.rotate) {
31292 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31293 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31295 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31300 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31301 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31303 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31304 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);
31308 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31313 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31314 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31316 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31317 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);
31321 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);
31326 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31327 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31329 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31330 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31334 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);
31341 this.previewEl.appendChild(this.canvasEl);
31343 this.setCanvasPosition();
31348 if(!this.canvasLoaded){
31352 var imageCanvas = document.createElement("canvas");
31354 var imageContext = imageCanvas.getContext("2d");
31356 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31357 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31359 var center = imageCanvas.width / 2;
31361 imageContext.translate(center, center);
31363 imageContext.rotate(this.rotate * Math.PI / 180);
31365 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31367 var canvas = document.createElement("canvas");
31369 var context = canvas.getContext("2d");
31371 canvas.width = this.minWidth;
31372 canvas.height = this.minHeight;
31374 switch (this.rotate) {
31377 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31378 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31380 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31381 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31383 var targetWidth = this.minWidth - 2 * x;
31384 var targetHeight = this.minHeight - 2 * y;
31388 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31389 scale = targetWidth / width;
31392 if(x > 0 && y == 0){
31393 scale = targetHeight / height;
31396 if(x > 0 && y > 0){
31397 scale = targetWidth / width;
31399 if(width < height){
31400 scale = targetHeight / height;
31404 context.scale(scale, scale);
31406 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31407 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31409 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31410 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31412 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31417 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31418 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31420 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31421 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31423 var targetWidth = this.minWidth - 2 * x;
31424 var targetHeight = this.minHeight - 2 * y;
31428 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31429 scale = targetWidth / width;
31432 if(x > 0 && y == 0){
31433 scale = targetHeight / height;
31436 if(x > 0 && y > 0){
31437 scale = targetWidth / width;
31439 if(width < height){
31440 scale = targetHeight / height;
31444 context.scale(scale, scale);
31446 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31447 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31449 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31450 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31452 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31454 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31459 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31460 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31462 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31463 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31465 var targetWidth = this.minWidth - 2 * x;
31466 var targetHeight = this.minHeight - 2 * y;
31470 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31471 scale = targetWidth / width;
31474 if(x > 0 && y == 0){
31475 scale = targetHeight / height;
31478 if(x > 0 && y > 0){
31479 scale = targetWidth / width;
31481 if(width < height){
31482 scale = targetHeight / height;
31486 context.scale(scale, scale);
31488 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31489 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31491 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31492 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31494 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31495 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31497 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31502 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31503 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31505 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31506 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31508 var targetWidth = this.minWidth - 2 * x;
31509 var targetHeight = this.minHeight - 2 * y;
31513 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31514 scale = targetWidth / width;
31517 if(x > 0 && y == 0){
31518 scale = targetHeight / height;
31521 if(x > 0 && y > 0){
31522 scale = targetWidth / width;
31524 if(width < height){
31525 scale = targetHeight / height;
31529 context.scale(scale, scale);
31531 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31532 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31534 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31535 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31537 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31539 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31546 this.cropData = canvas.toDataURL(this.cropType);
31548 if(this.fireEvent('crop', this, this.cropData) !== false){
31549 this.process(this.file, this.cropData);
31556 setThumbBoxSize : function()
31560 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31561 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31562 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31564 this.minWidth = width;
31565 this.minHeight = height;
31567 if(this.rotate == 90 || this.rotate == 270){
31568 this.minWidth = height;
31569 this.minHeight = width;
31574 width = Math.ceil(this.minWidth * height / this.minHeight);
31576 if(this.minWidth > this.minHeight){
31578 height = Math.ceil(this.minHeight * width / this.minWidth);
31581 this.thumbEl.setStyle({
31582 width : width + 'px',
31583 height : height + 'px'
31590 setThumbBoxPosition : function()
31592 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31593 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31595 this.thumbEl.setLeft(x);
31596 this.thumbEl.setTop(y);
31600 baseRotateLevel : function()
31602 this.baseRotate = 1;
31605 typeof(this.exif) != 'undefined' &&
31606 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31607 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31609 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31612 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31616 baseScaleLevel : function()
31620 if(this.isDocument){
31622 if(this.baseRotate == 6 || this.baseRotate == 8){
31624 height = this.thumbEl.getHeight();
31625 this.baseScale = height / this.imageEl.OriginWidth;
31627 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31628 width = this.thumbEl.getWidth();
31629 this.baseScale = width / this.imageEl.OriginHeight;
31635 height = this.thumbEl.getHeight();
31636 this.baseScale = height / this.imageEl.OriginHeight;
31638 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31639 width = this.thumbEl.getWidth();
31640 this.baseScale = width / this.imageEl.OriginWidth;
31646 if(this.baseRotate == 6 || this.baseRotate == 8){
31648 width = this.thumbEl.getHeight();
31649 this.baseScale = width / this.imageEl.OriginHeight;
31651 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31652 height = this.thumbEl.getWidth();
31653 this.baseScale = height / this.imageEl.OriginHeight;
31656 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31657 height = this.thumbEl.getWidth();
31658 this.baseScale = height / this.imageEl.OriginHeight;
31660 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31661 width = this.thumbEl.getHeight();
31662 this.baseScale = width / this.imageEl.OriginWidth;
31669 width = this.thumbEl.getWidth();
31670 this.baseScale = width / this.imageEl.OriginWidth;
31672 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31673 height = this.thumbEl.getHeight();
31674 this.baseScale = height / this.imageEl.OriginHeight;
31677 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31679 height = this.thumbEl.getHeight();
31680 this.baseScale = height / this.imageEl.OriginHeight;
31682 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31683 width = this.thumbEl.getWidth();
31684 this.baseScale = width / this.imageEl.OriginWidth;
31692 getScaleLevel : function()
31694 return this.baseScale * Math.pow(1.1, this.scale);
31697 onTouchStart : function(e)
31699 if(!this.canvasLoaded){
31700 this.beforeSelectFile(e);
31704 var touches = e.browserEvent.touches;
31710 if(touches.length == 1){
31711 this.onMouseDown(e);
31715 if(touches.length != 2){
31721 for(var i = 0, finger; finger = touches[i]; i++){
31722 coords.push(finger.pageX, finger.pageY);
31725 var x = Math.pow(coords[0] - coords[2], 2);
31726 var y = Math.pow(coords[1] - coords[3], 2);
31728 this.startDistance = Math.sqrt(x + y);
31730 this.startScale = this.scale;
31732 this.pinching = true;
31733 this.dragable = false;
31737 onTouchMove : function(e)
31739 if(!this.pinching && !this.dragable){
31743 var touches = e.browserEvent.touches;
31750 this.onMouseMove(e);
31756 for(var i = 0, finger; finger = touches[i]; i++){
31757 coords.push(finger.pageX, finger.pageY);
31760 var x = Math.pow(coords[0] - coords[2], 2);
31761 var y = Math.pow(coords[1] - coords[3], 2);
31763 this.endDistance = Math.sqrt(x + y);
31765 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31767 if(!this.zoomable()){
31768 this.scale = this.startScale;
31776 onTouchEnd : function(e)
31778 this.pinching = false;
31779 this.dragable = false;
31783 process : function(file, crop)
31786 this.maskEl.mask(this.loadingText);
31789 this.xhr = new XMLHttpRequest();
31791 file.xhr = this.xhr;
31793 this.xhr.open(this.method, this.url, true);
31796 "Accept": "application/json",
31797 "Cache-Control": "no-cache",
31798 "X-Requested-With": "XMLHttpRequest"
31801 for (var headerName in headers) {
31802 var headerValue = headers[headerName];
31804 this.xhr.setRequestHeader(headerName, headerValue);
31810 this.xhr.onload = function()
31812 _this.xhrOnLoad(_this.xhr);
31815 this.xhr.onerror = function()
31817 _this.xhrOnError(_this.xhr);
31820 var formData = new FormData();
31822 formData.append('returnHTML', 'NO');
31825 formData.append('crop', crop);
31828 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31829 formData.append(this.paramName, file, file.name);
31832 if(typeof(file.filename) != 'undefined'){
31833 formData.append('filename', file.filename);
31836 if(typeof(file.mimetype) != 'undefined'){
31837 formData.append('mimetype', file.mimetype);
31840 if(this.fireEvent('arrange', this, formData) != false){
31841 this.xhr.send(formData);
31845 xhrOnLoad : function(xhr)
31848 this.maskEl.unmask();
31851 if (xhr.readyState !== 4) {
31852 this.fireEvent('exception', this, xhr);
31856 var response = Roo.decode(xhr.responseText);
31858 if(!response.success){
31859 this.fireEvent('exception', this, xhr);
31863 var response = Roo.decode(xhr.responseText);
31865 this.fireEvent('upload', this, response);
31869 xhrOnError : function()
31872 this.maskEl.unmask();
31875 Roo.log('xhr on error');
31877 var response = Roo.decode(xhr.responseText);
31883 prepare : function(file)
31886 this.maskEl.mask(this.loadingText);
31892 if(typeof(file) === 'string'){
31893 this.loadCanvas(file);
31897 if(!file || !this.urlAPI){
31902 this.cropType = file.type;
31906 if(this.fireEvent('prepare', this, this.file) != false){
31908 var reader = new FileReader();
31910 reader.onload = function (e) {
31911 if (e.target.error) {
31912 Roo.log(e.target.error);
31916 var buffer = e.target.result,
31917 dataView = new DataView(buffer),
31919 maxOffset = dataView.byteLength - 4,
31923 if (dataView.getUint16(0) === 0xffd8) {
31924 while (offset < maxOffset) {
31925 markerBytes = dataView.getUint16(offset);
31927 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31928 markerLength = dataView.getUint16(offset + 2) + 2;
31929 if (offset + markerLength > dataView.byteLength) {
31930 Roo.log('Invalid meta data: Invalid segment size.');
31934 if(markerBytes == 0xffe1){
31935 _this.parseExifData(
31942 offset += markerLength;
31952 var url = _this.urlAPI.createObjectURL(_this.file);
31954 _this.loadCanvas(url);
31959 reader.readAsArrayBuffer(this.file);
31965 parseExifData : function(dataView, offset, length)
31967 var tiffOffset = offset + 10,
31971 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31972 // No Exif data, might be XMP data instead
31976 // Check for the ASCII code for "Exif" (0x45786966):
31977 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31978 // No Exif data, might be XMP data instead
31981 if (tiffOffset + 8 > dataView.byteLength) {
31982 Roo.log('Invalid Exif data: Invalid segment size.');
31985 // Check for the two null bytes:
31986 if (dataView.getUint16(offset + 8) !== 0x0000) {
31987 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31990 // Check the byte alignment:
31991 switch (dataView.getUint16(tiffOffset)) {
31993 littleEndian = true;
31996 littleEndian = false;
31999 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32002 // Check for the TIFF tag marker (0x002A):
32003 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32004 Roo.log('Invalid Exif data: Missing TIFF marker.');
32007 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32008 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32010 this.parseExifTags(
32013 tiffOffset + dirOffset,
32018 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32023 if (dirOffset + 6 > dataView.byteLength) {
32024 Roo.log('Invalid Exif data: Invalid directory offset.');
32027 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32028 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32029 if (dirEndOffset + 4 > dataView.byteLength) {
32030 Roo.log('Invalid Exif data: Invalid directory size.');
32033 for (i = 0; i < tagsNumber; i += 1) {
32037 dirOffset + 2 + 12 * i, // tag offset
32041 // Return the offset to the next directory:
32042 return dataView.getUint32(dirEndOffset, littleEndian);
32045 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32047 var tag = dataView.getUint16(offset, littleEndian);
32049 this.exif[tag] = this.getExifValue(
32053 dataView.getUint16(offset + 2, littleEndian), // tag type
32054 dataView.getUint32(offset + 4, littleEndian), // tag length
32059 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32061 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32070 Roo.log('Invalid Exif data: Invalid tag type.');
32074 tagSize = tagType.size * length;
32075 // Determine if the value is contained in the dataOffset bytes,
32076 // or if the value at the dataOffset is a pointer to the actual data:
32077 dataOffset = tagSize > 4 ?
32078 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32079 if (dataOffset + tagSize > dataView.byteLength) {
32080 Roo.log('Invalid Exif data: Invalid data offset.');
32083 if (length === 1) {
32084 return tagType.getValue(dataView, dataOffset, littleEndian);
32087 for (i = 0; i < length; i += 1) {
32088 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32091 if (tagType.ascii) {
32093 // Concatenate the chars:
32094 for (i = 0; i < values.length; i += 1) {
32096 // Ignore the terminating NULL byte(s):
32097 if (c === '\u0000') {
32109 Roo.apply(Roo.bootstrap.UploadCropbox, {
32111 'Orientation': 0x0112
32115 1: 0, //'top-left',
32117 3: 180, //'bottom-right',
32118 // 4: 'bottom-left',
32120 6: 90, //'right-top',
32121 // 7: 'right-bottom',
32122 8: 270 //'left-bottom'
32126 // byte, 8-bit unsigned int:
32128 getValue: function (dataView, dataOffset) {
32129 return dataView.getUint8(dataOffset);
32133 // ascii, 8-bit byte:
32135 getValue: function (dataView, dataOffset) {
32136 return String.fromCharCode(dataView.getUint8(dataOffset));
32141 // short, 16 bit int:
32143 getValue: function (dataView, dataOffset, littleEndian) {
32144 return dataView.getUint16(dataOffset, littleEndian);
32148 // long, 32 bit int:
32150 getValue: function (dataView, dataOffset, littleEndian) {
32151 return dataView.getUint32(dataOffset, littleEndian);
32155 // rational = two long values, first is numerator, second is denominator:
32157 getValue: function (dataView, dataOffset, littleEndian) {
32158 return dataView.getUint32(dataOffset, littleEndian) /
32159 dataView.getUint32(dataOffset + 4, littleEndian);
32163 // slong, 32 bit signed int:
32165 getValue: function (dataView, dataOffset, littleEndian) {
32166 return dataView.getInt32(dataOffset, littleEndian);
32170 // srational, two slongs, first is numerator, second is denominator:
32172 getValue: function (dataView, dataOffset, littleEndian) {
32173 return dataView.getInt32(dataOffset, littleEndian) /
32174 dataView.getInt32(dataOffset + 4, littleEndian);
32184 cls : 'btn-group roo-upload-cropbox-rotate-left',
32185 action : 'rotate-left',
32189 cls : 'btn btn-default',
32190 html : '<i class="fa fa-undo"></i>'
32196 cls : 'btn-group roo-upload-cropbox-picture',
32197 action : 'picture',
32201 cls : 'btn btn-default',
32202 html : '<i class="fa fa-picture-o"></i>'
32208 cls : 'btn-group roo-upload-cropbox-rotate-right',
32209 action : 'rotate-right',
32213 cls : 'btn btn-default',
32214 html : '<i class="fa fa-repeat"></i>'
32222 cls : 'btn-group roo-upload-cropbox-rotate-left',
32223 action : 'rotate-left',
32227 cls : 'btn btn-default',
32228 html : '<i class="fa fa-undo"></i>'
32234 cls : 'btn-group roo-upload-cropbox-download',
32235 action : 'download',
32239 cls : 'btn btn-default',
32240 html : '<i class="fa fa-download"></i>'
32246 cls : 'btn-group roo-upload-cropbox-crop',
32251 cls : 'btn btn-default',
32252 html : '<i class="fa fa-crop"></i>'
32258 cls : 'btn-group roo-upload-cropbox-trash',
32263 cls : 'btn btn-default',
32264 html : '<i class="fa fa-trash"></i>'
32270 cls : 'btn-group roo-upload-cropbox-rotate-right',
32271 action : 'rotate-right',
32275 cls : 'btn btn-default',
32276 html : '<i class="fa fa-repeat"></i>'
32284 cls : 'btn-group roo-upload-cropbox-rotate-left',
32285 action : 'rotate-left',
32289 cls : 'btn btn-default',
32290 html : '<i class="fa fa-undo"></i>'
32296 cls : 'btn-group roo-upload-cropbox-rotate-right',
32297 action : 'rotate-right',
32301 cls : 'btn btn-default',
32302 html : '<i class="fa fa-repeat"></i>'
32315 * @class Roo.bootstrap.DocumentManager
32316 * @extends Roo.bootstrap.Component
32317 * Bootstrap DocumentManager class
32318 * @cfg {String} paramName default 'imageUpload'
32319 * @cfg {String} toolTipName default 'filename'
32320 * @cfg {String} method default POST
32321 * @cfg {String} url action url
32322 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32323 * @cfg {Boolean} multiple multiple upload default true
32324 * @cfg {Number} thumbSize default 300
32325 * @cfg {String} fieldLabel
32326 * @cfg {Number} labelWidth default 4
32327 * @cfg {String} labelAlign (left|top) default left
32328 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32329 * @cfg {Number} labellg set the width of label (1-12)
32330 * @cfg {Number} labelmd set the width of label (1-12)
32331 * @cfg {Number} labelsm set the width of label (1-12)
32332 * @cfg {Number} labelxs set the width of label (1-12)
32335 * Create a new DocumentManager
32336 * @param {Object} config The config object
32339 Roo.bootstrap.DocumentManager = function(config){
32340 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32343 this.delegates = [];
32348 * Fire when initial the DocumentManager
32349 * @param {Roo.bootstrap.DocumentManager} this
32354 * inspect selected file
32355 * @param {Roo.bootstrap.DocumentManager} this
32356 * @param {File} file
32361 * Fire when xhr load exception
32362 * @param {Roo.bootstrap.DocumentManager} this
32363 * @param {XMLHttpRequest} xhr
32365 "exception" : true,
32367 * @event afterupload
32368 * Fire when xhr load exception
32369 * @param {Roo.bootstrap.DocumentManager} this
32370 * @param {XMLHttpRequest} xhr
32372 "afterupload" : true,
32375 * prepare the form data
32376 * @param {Roo.bootstrap.DocumentManager} this
32377 * @param {Object} formData
32382 * Fire when remove the file
32383 * @param {Roo.bootstrap.DocumentManager} this
32384 * @param {Object} file
32389 * Fire after refresh the file
32390 * @param {Roo.bootstrap.DocumentManager} this
32395 * Fire after click the image
32396 * @param {Roo.bootstrap.DocumentManager} this
32397 * @param {Object} file
32402 * Fire when upload a image and editable set to true
32403 * @param {Roo.bootstrap.DocumentManager} this
32404 * @param {Object} file
32408 * @event beforeselectfile
32409 * Fire before select file
32410 * @param {Roo.bootstrap.DocumentManager} this
32412 "beforeselectfile" : true,
32415 * Fire before process file
32416 * @param {Roo.bootstrap.DocumentManager} this
32417 * @param {Object} file
32421 * @event previewrendered
32422 * Fire when preview rendered
32423 * @param {Roo.bootstrap.DocumentManager} this
32424 * @param {Object} file
32426 "previewrendered" : true,
32429 "previewResize" : true
32434 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32443 paramName : 'imageUpload',
32444 toolTipName : 'filename',
32447 labelAlign : 'left',
32457 getAutoCreate : function()
32459 var managerWidget = {
32461 cls : 'roo-document-manager',
32465 cls : 'roo-document-manager-selector',
32470 cls : 'roo-document-manager-uploader',
32474 cls : 'roo-document-manager-upload-btn',
32475 html : '<i class="fa fa-plus"></i>'
32486 cls : 'column col-md-12',
32491 if(this.fieldLabel.length){
32496 cls : 'column col-md-12',
32497 html : this.fieldLabel
32501 cls : 'column col-md-12',
32506 if(this.labelAlign == 'left'){
32511 html : this.fieldLabel
32520 if(this.labelWidth > 12){
32521 content[0].style = "width: " + this.labelWidth + 'px';
32524 if(this.labelWidth < 13 && this.labelmd == 0){
32525 this.labelmd = this.labelWidth;
32528 if(this.labellg > 0){
32529 content[0].cls += ' col-lg-' + this.labellg;
32530 content[1].cls += ' col-lg-' + (12 - this.labellg);
32533 if(this.labelmd > 0){
32534 content[0].cls += ' col-md-' + this.labelmd;
32535 content[1].cls += ' col-md-' + (12 - this.labelmd);
32538 if(this.labelsm > 0){
32539 content[0].cls += ' col-sm-' + this.labelsm;
32540 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32543 if(this.labelxs > 0){
32544 content[0].cls += ' col-xs-' + this.labelxs;
32545 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32553 cls : 'row clearfix',
32561 initEvents : function()
32563 this.managerEl = this.el.select('.roo-document-manager', true).first();
32564 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32566 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32567 this.selectorEl.hide();
32570 this.selectorEl.attr('multiple', 'multiple');
32573 this.selectorEl.on('change', this.onFileSelected, this);
32575 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32576 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32578 this.uploader.on('click', this.onUploaderClick, this);
32580 this.renderProgressDialog();
32584 window.addEventListener("resize", function() { _this.refresh(); } );
32586 this.fireEvent('initial', this);
32589 renderProgressDialog : function()
32593 this.progressDialog = new Roo.bootstrap.Modal({
32594 cls : 'roo-document-manager-progress-dialog',
32595 allow_close : false,
32606 btnclick : function() {
32607 _this.uploadCancel();
32613 this.progressDialog.render(Roo.get(document.body));
32615 this.progress = new Roo.bootstrap.Progress({
32616 cls : 'roo-document-manager-progress',
32621 this.progress.render(this.progressDialog.getChildContainer());
32623 this.progressBar = new Roo.bootstrap.ProgressBar({
32624 cls : 'roo-document-manager-progress-bar',
32627 aria_valuemax : 12,
32631 this.progressBar.render(this.progress.getChildContainer());
32634 onUploaderClick : function(e)
32636 e.preventDefault();
32638 if(this.fireEvent('beforeselectfile', this) != false){
32639 this.selectorEl.dom.click();
32644 onFileSelected : function(e)
32646 e.preventDefault();
32648 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32652 Roo.each(this.selectorEl.dom.files, function(file){
32653 if(this.fireEvent('inspect', this, file) != false){
32654 this.files.push(file);
32664 this.selectorEl.dom.value = '';
32666 if(!this.files || !this.files.length){
32670 if(this.boxes > 0 && this.files.length > this.boxes){
32671 this.files = this.files.slice(0, this.boxes);
32674 this.uploader.show();
32676 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32677 this.uploader.hide();
32686 Roo.each(this.files, function(file){
32688 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32689 var f = this.renderPreview(file);
32694 if(file.type.indexOf('image') != -1){
32695 this.delegates.push(
32697 _this.process(file);
32698 }).createDelegate(this)
32706 _this.process(file);
32707 }).createDelegate(this)
32712 this.files = files;
32714 this.delegates = this.delegates.concat(docs);
32716 if(!this.delegates.length){
32721 this.progressBar.aria_valuemax = this.delegates.length;
32728 arrange : function()
32730 if(!this.delegates.length){
32731 this.progressDialog.hide();
32736 var delegate = this.delegates.shift();
32738 this.progressDialog.show();
32740 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32742 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32747 refresh : function()
32749 this.uploader.show();
32751 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32752 this.uploader.hide();
32755 Roo.isTouch ? this.closable(false) : this.closable(true);
32757 this.fireEvent('refresh', this);
32760 onRemove : function(e, el, o)
32762 e.preventDefault();
32764 this.fireEvent('remove', this, o);
32768 remove : function(o)
32772 Roo.each(this.files, function(file){
32773 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32782 this.files = files;
32789 Roo.each(this.files, function(file){
32794 file.target.remove();
32803 onClick : function(e, el, o)
32805 e.preventDefault();
32807 this.fireEvent('click', this, o);
32811 closable : function(closable)
32813 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32815 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32827 xhrOnLoad : function(xhr)
32829 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32833 if (xhr.readyState !== 4) {
32835 this.fireEvent('exception', this, xhr);
32839 var response = Roo.decode(xhr.responseText);
32841 if(!response.success){
32843 this.fireEvent('exception', this, xhr);
32847 var file = this.renderPreview(response.data);
32849 this.files.push(file);
32853 this.fireEvent('afterupload', this, xhr);
32857 xhrOnError : function(xhr)
32859 Roo.log('xhr on error');
32861 var response = Roo.decode(xhr.responseText);
32868 process : function(file)
32870 if(this.fireEvent('process', this, file) !== false){
32871 if(this.editable && file.type.indexOf('image') != -1){
32872 this.fireEvent('edit', this, file);
32876 this.uploadStart(file, false);
32883 uploadStart : function(file, crop)
32885 this.xhr = new XMLHttpRequest();
32887 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32892 file.xhr = this.xhr;
32894 this.managerEl.createChild({
32896 cls : 'roo-document-manager-loading',
32900 tooltip : file.name,
32901 cls : 'roo-document-manager-thumb',
32902 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32908 this.xhr.open(this.method, this.url, true);
32911 "Accept": "application/json",
32912 "Cache-Control": "no-cache",
32913 "X-Requested-With": "XMLHttpRequest"
32916 for (var headerName in headers) {
32917 var headerValue = headers[headerName];
32919 this.xhr.setRequestHeader(headerName, headerValue);
32925 this.xhr.onload = function()
32927 _this.xhrOnLoad(_this.xhr);
32930 this.xhr.onerror = function()
32932 _this.xhrOnError(_this.xhr);
32935 var formData = new FormData();
32937 formData.append('returnHTML', 'NO');
32940 formData.append('crop', crop);
32943 formData.append(this.paramName, file, file.name);
32950 if(this.fireEvent('prepare', this, formData, options) != false){
32952 if(options.manually){
32956 this.xhr.send(formData);
32960 this.uploadCancel();
32963 uploadCancel : function()
32969 this.delegates = [];
32971 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32978 renderPreview : function(file)
32980 if(typeof(file.target) != 'undefined' && file.target){
32984 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32986 var previewEl = this.managerEl.createChild({
32988 cls : 'roo-document-manager-preview',
32992 tooltip : file[this.toolTipName],
32993 cls : 'roo-document-manager-thumb',
32994 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32999 html : '<i class="fa fa-times-circle"></i>'
33004 var close = previewEl.select('button.close', true).first();
33006 close.on('click', this.onRemove, this, file);
33008 file.target = previewEl;
33010 var image = previewEl.select('img', true).first();
33014 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33016 image.on('click', this.onClick, this, file);
33018 this.fireEvent('previewrendered', this, file);
33024 onPreviewLoad : function(file, image)
33026 if(typeof(file.target) == 'undefined' || !file.target){
33030 var width = image.dom.naturalWidth || image.dom.width;
33031 var height = image.dom.naturalHeight || image.dom.height;
33033 if(!this.previewResize) {
33037 if(width > height){
33038 file.target.addClass('wide');
33042 file.target.addClass('tall');
33047 uploadFromSource : function(file, crop)
33049 this.xhr = new XMLHttpRequest();
33051 this.managerEl.createChild({
33053 cls : 'roo-document-manager-loading',
33057 tooltip : file.name,
33058 cls : 'roo-document-manager-thumb',
33059 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33065 this.xhr.open(this.method, this.url, true);
33068 "Accept": "application/json",
33069 "Cache-Control": "no-cache",
33070 "X-Requested-With": "XMLHttpRequest"
33073 for (var headerName in headers) {
33074 var headerValue = headers[headerName];
33076 this.xhr.setRequestHeader(headerName, headerValue);
33082 this.xhr.onload = function()
33084 _this.xhrOnLoad(_this.xhr);
33087 this.xhr.onerror = function()
33089 _this.xhrOnError(_this.xhr);
33092 var formData = new FormData();
33094 formData.append('returnHTML', 'NO');
33096 formData.append('crop', crop);
33098 if(typeof(file.filename) != 'undefined'){
33099 formData.append('filename', file.filename);
33102 if(typeof(file.mimetype) != 'undefined'){
33103 formData.append('mimetype', file.mimetype);
33108 if(this.fireEvent('prepare', this, formData) != false){
33109 this.xhr.send(formData);
33119 * @class Roo.bootstrap.DocumentViewer
33120 * @extends Roo.bootstrap.Component
33121 * Bootstrap DocumentViewer class
33122 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33123 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33126 * Create a new DocumentViewer
33127 * @param {Object} config The config object
33130 Roo.bootstrap.DocumentViewer = function(config){
33131 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33136 * Fire after initEvent
33137 * @param {Roo.bootstrap.DocumentViewer} this
33143 * @param {Roo.bootstrap.DocumentViewer} this
33148 * Fire after download button
33149 * @param {Roo.bootstrap.DocumentViewer} this
33154 * Fire after trash button
33155 * @param {Roo.bootstrap.DocumentViewer} this
33162 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33164 showDownload : true,
33168 getAutoCreate : function()
33172 cls : 'roo-document-viewer',
33176 cls : 'roo-document-viewer-body',
33180 cls : 'roo-document-viewer-thumb',
33184 cls : 'roo-document-viewer-image'
33192 cls : 'roo-document-viewer-footer',
33195 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33199 cls : 'btn-group roo-document-viewer-download',
33203 cls : 'btn btn-default',
33204 html : '<i class="fa fa-download"></i>'
33210 cls : 'btn-group roo-document-viewer-trash',
33214 cls : 'btn btn-default',
33215 html : '<i class="fa fa-trash"></i>'
33228 initEvents : function()
33230 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33231 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33233 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33234 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33236 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33237 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33239 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33240 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33242 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33243 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33245 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33246 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33248 this.bodyEl.on('click', this.onClick, this);
33249 this.downloadBtn.on('click', this.onDownload, this);
33250 this.trashBtn.on('click', this.onTrash, this);
33252 this.downloadBtn.hide();
33253 this.trashBtn.hide();
33255 if(this.showDownload){
33256 this.downloadBtn.show();
33259 if(this.showTrash){
33260 this.trashBtn.show();
33263 if(!this.showDownload && !this.showTrash) {
33264 this.footerEl.hide();
33269 initial : function()
33271 this.fireEvent('initial', this);
33275 onClick : function(e)
33277 e.preventDefault();
33279 this.fireEvent('click', this);
33282 onDownload : function(e)
33284 e.preventDefault();
33286 this.fireEvent('download', this);
33289 onTrash : function(e)
33291 e.preventDefault();
33293 this.fireEvent('trash', this);
33305 * @class Roo.bootstrap.NavProgressBar
33306 * @extends Roo.bootstrap.Component
33307 * Bootstrap NavProgressBar class
33310 * Create a new nav progress bar
33311 * @param {Object} config The config object
33314 Roo.bootstrap.NavProgressBar = function(config){
33315 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33317 this.bullets = this.bullets || [];
33319 // Roo.bootstrap.NavProgressBar.register(this);
33323 * Fires when the active item changes
33324 * @param {Roo.bootstrap.NavProgressBar} this
33325 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33326 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33333 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33338 getAutoCreate : function()
33340 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33344 cls : 'roo-navigation-bar-group',
33348 cls : 'roo-navigation-top-bar'
33352 cls : 'roo-navigation-bullets-bar',
33356 cls : 'roo-navigation-bar'
33363 cls : 'roo-navigation-bottom-bar'
33373 initEvents: function()
33378 onRender : function(ct, position)
33380 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33382 if(this.bullets.length){
33383 Roo.each(this.bullets, function(b){
33392 addItem : function(cfg)
33394 var item = new Roo.bootstrap.NavProgressItem(cfg);
33396 item.parentId = this.id;
33397 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33400 var top = new Roo.bootstrap.Element({
33402 cls : 'roo-navigation-bar-text'
33405 var bottom = new Roo.bootstrap.Element({
33407 cls : 'roo-navigation-bar-text'
33410 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33411 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33413 var topText = new Roo.bootstrap.Element({
33415 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33418 var bottomText = new Roo.bootstrap.Element({
33420 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33423 topText.onRender(top.el, null);
33424 bottomText.onRender(bottom.el, null);
33427 item.bottomEl = bottom;
33430 this.barItems.push(item);
33435 getActive : function()
33437 var active = false;
33439 Roo.each(this.barItems, function(v){
33441 if (!v.isActive()) {
33453 setActiveItem : function(item)
33457 Roo.each(this.barItems, function(v){
33458 if (v.rid == item.rid) {
33462 if (v.isActive()) {
33463 v.setActive(false);
33468 item.setActive(true);
33470 this.fireEvent('changed', this, item, prev);
33473 getBarItem: function(rid)
33477 Roo.each(this.barItems, function(e) {
33478 if (e.rid != rid) {
33489 indexOfItem : function(item)
33493 Roo.each(this.barItems, function(v, i){
33495 if (v.rid != item.rid) {
33506 setActiveNext : function()
33508 var i = this.indexOfItem(this.getActive());
33510 if (i > this.barItems.length) {
33514 this.setActiveItem(this.barItems[i+1]);
33517 setActivePrev : function()
33519 var i = this.indexOfItem(this.getActive());
33525 this.setActiveItem(this.barItems[i-1]);
33528 format : function()
33530 if(!this.barItems.length){
33534 var width = 100 / this.barItems.length;
33536 Roo.each(this.barItems, function(i){
33537 i.el.setStyle('width', width + '%');
33538 i.topEl.el.setStyle('width', width + '%');
33539 i.bottomEl.el.setStyle('width', width + '%');
33548 * Nav Progress Item
33553 * @class Roo.bootstrap.NavProgressItem
33554 * @extends Roo.bootstrap.Component
33555 * Bootstrap NavProgressItem class
33556 * @cfg {String} rid the reference id
33557 * @cfg {Boolean} active (true|false) Is item active default false
33558 * @cfg {Boolean} disabled (true|false) Is item active default false
33559 * @cfg {String} html
33560 * @cfg {String} position (top|bottom) text position default bottom
33561 * @cfg {String} icon show icon instead of number
33564 * Create a new NavProgressItem
33565 * @param {Object} config The config object
33567 Roo.bootstrap.NavProgressItem = function(config){
33568 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33573 * The raw click event for the entire grid.
33574 * @param {Roo.bootstrap.NavProgressItem} this
33575 * @param {Roo.EventObject} e
33582 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33588 position : 'bottom',
33591 getAutoCreate : function()
33593 var iconCls = 'roo-navigation-bar-item-icon';
33595 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33599 cls: 'roo-navigation-bar-item',
33609 cfg.cls += ' active';
33612 cfg.cls += ' disabled';
33618 disable : function()
33620 this.setDisabled(true);
33623 enable : function()
33625 this.setDisabled(false);
33628 initEvents: function()
33630 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33632 this.iconEl.on('click', this.onClick, this);
33635 onClick : function(e)
33637 e.preventDefault();
33643 if(this.fireEvent('click', this, e) === false){
33647 this.parent().setActiveItem(this);
33650 isActive: function ()
33652 return this.active;
33655 setActive : function(state)
33657 if(this.active == state){
33661 this.active = state;
33664 this.el.addClass('active');
33668 this.el.removeClass('active');
33673 setDisabled : function(state)
33675 if(this.disabled == state){
33679 this.disabled = state;
33682 this.el.addClass('disabled');
33686 this.el.removeClass('disabled');
33689 tooltipEl : function()
33691 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33704 * @class Roo.bootstrap.FieldLabel
33705 * @extends Roo.bootstrap.Component
33706 * Bootstrap FieldLabel class
33707 * @cfg {String} html contents of the element
33708 * @cfg {String} tag tag of the element default label
33709 * @cfg {String} cls class of the element
33710 * @cfg {String} target label target
33711 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33712 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33713 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33714 * @cfg {String} iconTooltip default "This field is required"
33715 * @cfg {String} indicatorpos (left|right) default left
33718 * Create a new FieldLabel
33719 * @param {Object} config The config object
33722 Roo.bootstrap.FieldLabel = function(config){
33723 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33728 * Fires after the field has been marked as invalid.
33729 * @param {Roo.form.FieldLabel} this
33730 * @param {String} msg The validation message
33735 * Fires after the field has been validated with no errors.
33736 * @param {Roo.form.FieldLabel} this
33742 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33749 invalidClass : 'has-warning',
33750 validClass : 'has-success',
33751 iconTooltip : 'This field is required',
33752 indicatorpos : 'left',
33754 getAutoCreate : function(){
33757 if (!this.allowBlank) {
33763 cls : 'roo-bootstrap-field-label ' + this.cls,
33768 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33769 tooltip : this.iconTooltip
33778 if(this.indicatorpos == 'right'){
33781 cls : 'roo-bootstrap-field-label ' + this.cls,
33790 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33791 tooltip : this.iconTooltip
33800 initEvents: function()
33802 Roo.bootstrap.Element.superclass.initEvents.call(this);
33804 this.indicator = this.indicatorEl();
33806 if(this.indicator){
33807 this.indicator.removeClass('visible');
33808 this.indicator.addClass('invisible');
33811 Roo.bootstrap.FieldLabel.register(this);
33814 indicatorEl : function()
33816 var indicator = this.el.select('i.roo-required-indicator',true).first();
33827 * Mark this field as valid
33829 markValid : function()
33831 if(this.indicator){
33832 this.indicator.removeClass('visible');
33833 this.indicator.addClass('invisible');
33835 if (Roo.bootstrap.version == 3) {
33836 this.el.removeClass(this.invalidClass);
33837 this.el.addClass(this.validClass);
33839 this.el.removeClass('is-invalid');
33840 this.el.addClass('is-valid');
33844 this.fireEvent('valid', this);
33848 * Mark this field as invalid
33849 * @param {String} msg The validation message
33851 markInvalid : function(msg)
33853 if(this.indicator){
33854 this.indicator.removeClass('invisible');
33855 this.indicator.addClass('visible');
33857 if (Roo.bootstrap.version == 3) {
33858 this.el.removeClass(this.validClass);
33859 this.el.addClass(this.invalidClass);
33861 this.el.removeClass('is-valid');
33862 this.el.addClass('is-invalid');
33866 this.fireEvent('invalid', this, msg);
33872 Roo.apply(Roo.bootstrap.FieldLabel, {
33877 * register a FieldLabel Group
33878 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33880 register : function(label)
33882 if(this.groups.hasOwnProperty(label.target)){
33886 this.groups[label.target] = label;
33890 * fetch a FieldLabel Group based on the target
33891 * @param {string} target
33892 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33894 get: function(target) {
33895 if (typeof(this.groups[target]) == 'undefined') {
33899 return this.groups[target] ;
33908 * page DateSplitField.
33914 * @class Roo.bootstrap.DateSplitField
33915 * @extends Roo.bootstrap.Component
33916 * Bootstrap DateSplitField class
33917 * @cfg {string} fieldLabel - the label associated
33918 * @cfg {Number} labelWidth set the width of label (0-12)
33919 * @cfg {String} labelAlign (top|left)
33920 * @cfg {Boolean} dayAllowBlank (true|false) default false
33921 * @cfg {Boolean} monthAllowBlank (true|false) default false
33922 * @cfg {Boolean} yearAllowBlank (true|false) default false
33923 * @cfg {string} dayPlaceholder
33924 * @cfg {string} monthPlaceholder
33925 * @cfg {string} yearPlaceholder
33926 * @cfg {string} dayFormat default 'd'
33927 * @cfg {string} monthFormat default 'm'
33928 * @cfg {string} yearFormat default 'Y'
33929 * @cfg {Number} labellg set the width of label (1-12)
33930 * @cfg {Number} labelmd set the width of label (1-12)
33931 * @cfg {Number} labelsm set the width of label (1-12)
33932 * @cfg {Number} labelxs set the width of label (1-12)
33936 * Create a new DateSplitField
33937 * @param {Object} config The config object
33940 Roo.bootstrap.DateSplitField = function(config){
33941 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33947 * getting the data of years
33948 * @param {Roo.bootstrap.DateSplitField} this
33949 * @param {Object} years
33954 * getting the data of days
33955 * @param {Roo.bootstrap.DateSplitField} this
33956 * @param {Object} days
33961 * Fires after the field has been marked as invalid.
33962 * @param {Roo.form.Field} this
33963 * @param {String} msg The validation message
33968 * Fires after the field has been validated with no errors.
33969 * @param {Roo.form.Field} this
33975 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33978 labelAlign : 'top',
33980 dayAllowBlank : false,
33981 monthAllowBlank : false,
33982 yearAllowBlank : false,
33983 dayPlaceholder : '',
33984 monthPlaceholder : '',
33985 yearPlaceholder : '',
33989 isFormField : true,
33995 getAutoCreate : function()
33999 cls : 'row roo-date-split-field-group',
34004 cls : 'form-hidden-field roo-date-split-field-group-value',
34010 var labelCls = 'col-md-12';
34011 var contentCls = 'col-md-4';
34013 if(this.fieldLabel){
34017 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34021 html : this.fieldLabel
34026 if(this.labelAlign == 'left'){
34028 if(this.labelWidth > 12){
34029 label.style = "width: " + this.labelWidth + 'px';
34032 if(this.labelWidth < 13 && this.labelmd == 0){
34033 this.labelmd = this.labelWidth;
34036 if(this.labellg > 0){
34037 labelCls = ' col-lg-' + this.labellg;
34038 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34041 if(this.labelmd > 0){
34042 labelCls = ' col-md-' + this.labelmd;
34043 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34046 if(this.labelsm > 0){
34047 labelCls = ' col-sm-' + this.labelsm;
34048 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34051 if(this.labelxs > 0){
34052 labelCls = ' col-xs-' + this.labelxs;
34053 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34057 label.cls += ' ' + labelCls;
34059 cfg.cn.push(label);
34062 Roo.each(['day', 'month', 'year'], function(t){
34065 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34072 inputEl: function ()
34074 return this.el.select('.roo-date-split-field-group-value', true).first();
34077 onRender : function(ct, position)
34081 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34083 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34085 this.dayField = new Roo.bootstrap.ComboBox({
34086 allowBlank : this.dayAllowBlank,
34087 alwaysQuery : true,
34088 displayField : 'value',
34091 forceSelection : true,
34093 placeholder : this.dayPlaceholder,
34094 selectOnFocus : true,
34095 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34096 triggerAction : 'all',
34098 valueField : 'value',
34099 store : new Roo.data.SimpleStore({
34100 data : (function() {
34102 _this.fireEvent('days', _this, days);
34105 fields : [ 'value' ]
34108 select : function (_self, record, index)
34110 _this.setValue(_this.getValue());
34115 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34117 this.monthField = new Roo.bootstrap.MonthField({
34118 after : '<i class=\"fa fa-calendar\"></i>',
34119 allowBlank : this.monthAllowBlank,
34120 placeholder : this.monthPlaceholder,
34123 render : function (_self)
34125 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34126 e.preventDefault();
34130 select : function (_self, oldvalue, newvalue)
34132 _this.setValue(_this.getValue());
34137 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34139 this.yearField = new Roo.bootstrap.ComboBox({
34140 allowBlank : this.yearAllowBlank,
34141 alwaysQuery : true,
34142 displayField : 'value',
34145 forceSelection : true,
34147 placeholder : this.yearPlaceholder,
34148 selectOnFocus : true,
34149 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34150 triggerAction : 'all',
34152 valueField : 'value',
34153 store : new Roo.data.SimpleStore({
34154 data : (function() {
34156 _this.fireEvent('years', _this, years);
34159 fields : [ 'value' ]
34162 select : function (_self, record, index)
34164 _this.setValue(_this.getValue());
34169 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34172 setValue : function(v, format)
34174 this.inputEl.dom.value = v;
34176 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34178 var d = Date.parseDate(v, f);
34185 this.setDay(d.format(this.dayFormat));
34186 this.setMonth(d.format(this.monthFormat));
34187 this.setYear(d.format(this.yearFormat));
34194 setDay : function(v)
34196 this.dayField.setValue(v);
34197 this.inputEl.dom.value = this.getValue();
34202 setMonth : function(v)
34204 this.monthField.setValue(v, true);
34205 this.inputEl.dom.value = this.getValue();
34210 setYear : function(v)
34212 this.yearField.setValue(v);
34213 this.inputEl.dom.value = this.getValue();
34218 getDay : function()
34220 return this.dayField.getValue();
34223 getMonth : function()
34225 return this.monthField.getValue();
34228 getYear : function()
34230 return this.yearField.getValue();
34233 getValue : function()
34235 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34237 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34247 this.inputEl.dom.value = '';
34252 validate : function()
34254 var d = this.dayField.validate();
34255 var m = this.monthField.validate();
34256 var y = this.yearField.validate();
34261 (!this.dayAllowBlank && !d) ||
34262 (!this.monthAllowBlank && !m) ||
34263 (!this.yearAllowBlank && !y)
34268 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34277 this.markInvalid();
34282 markValid : function()
34285 var label = this.el.select('label', true).first();
34286 var icon = this.el.select('i.fa-star', true).first();
34292 this.fireEvent('valid', this);
34296 * Mark this field as invalid
34297 * @param {String} msg The validation message
34299 markInvalid : function(msg)
34302 var label = this.el.select('label', true).first();
34303 var icon = this.el.select('i.fa-star', true).first();
34305 if(label && !icon){
34306 this.el.select('.roo-date-split-field-label', true).createChild({
34308 cls : 'text-danger fa fa-lg fa-star',
34309 tooltip : 'This field is required',
34310 style : 'margin-right:5px;'
34314 this.fireEvent('invalid', this, msg);
34317 clearInvalid : function()
34319 var label = this.el.select('label', true).first();
34320 var icon = this.el.select('i.fa-star', true).first();
34326 this.fireEvent('valid', this);
34329 getName: function()
34339 * http://masonry.desandro.com
34341 * The idea is to render all the bricks based on vertical width...
34343 * The original code extends 'outlayer' - we might need to use that....
34349 * @class Roo.bootstrap.LayoutMasonry
34350 * @extends Roo.bootstrap.Component
34351 * Bootstrap Layout Masonry class
34354 * Create a new Element
34355 * @param {Object} config The config object
34358 Roo.bootstrap.LayoutMasonry = function(config){
34360 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34364 Roo.bootstrap.LayoutMasonry.register(this);
34370 * Fire after layout the items
34371 * @param {Roo.bootstrap.LayoutMasonry} this
34372 * @param {Roo.EventObject} e
34379 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34382 * @cfg {Boolean} isLayoutInstant = no animation?
34384 isLayoutInstant : false, // needed?
34387 * @cfg {Number} boxWidth width of the columns
34392 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34397 * @cfg {Number} padWidth padding below box..
34402 * @cfg {Number} gutter gutter width..
34407 * @cfg {Number} maxCols maximum number of columns
34413 * @cfg {Boolean} isAutoInitial defalut true
34415 isAutoInitial : true,
34420 * @cfg {Boolean} isHorizontal defalut false
34422 isHorizontal : false,
34424 currentSize : null,
34430 bricks: null, //CompositeElement
34434 _isLayoutInited : false,
34436 // isAlternative : false, // only use for vertical layout...
34439 * @cfg {Number} alternativePadWidth padding below box..
34441 alternativePadWidth : 50,
34443 selectedBrick : [],
34445 getAutoCreate : function(){
34447 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34451 cls: 'blog-masonary-wrapper ' + this.cls,
34453 cls : 'mas-boxes masonary'
34460 getChildContainer: function( )
34462 if (this.boxesEl) {
34463 return this.boxesEl;
34466 this.boxesEl = this.el.select('.mas-boxes').first();
34468 return this.boxesEl;
34472 initEvents : function()
34476 if(this.isAutoInitial){
34477 Roo.log('hook children rendered');
34478 this.on('childrenrendered', function() {
34479 Roo.log('children rendered');
34485 initial : function()
34487 this.selectedBrick = [];
34489 this.currentSize = this.el.getBox(true);
34491 Roo.EventManager.onWindowResize(this.resize, this);
34493 if(!this.isAutoInitial){
34501 //this.layout.defer(500,this);
34505 resize : function()
34507 var cs = this.el.getBox(true);
34510 this.currentSize.width == cs.width &&
34511 this.currentSize.x == cs.x &&
34512 this.currentSize.height == cs.height &&
34513 this.currentSize.y == cs.y
34515 Roo.log("no change in with or X or Y");
34519 this.currentSize = cs;
34525 layout : function()
34527 this._resetLayout();
34529 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34531 this.layoutItems( isInstant );
34533 this._isLayoutInited = true;
34535 this.fireEvent('layout', this);
34539 _resetLayout : function()
34541 if(this.isHorizontal){
34542 this.horizontalMeasureColumns();
34546 this.verticalMeasureColumns();
34550 verticalMeasureColumns : function()
34552 this.getContainerWidth();
34554 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34555 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34559 var boxWidth = this.boxWidth + this.padWidth;
34561 if(this.containerWidth < this.boxWidth){
34562 boxWidth = this.containerWidth
34565 var containerWidth = this.containerWidth;
34567 var cols = Math.floor(containerWidth / boxWidth);
34569 this.cols = Math.max( cols, 1 );
34571 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34573 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34575 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34577 this.colWidth = boxWidth + avail - this.padWidth;
34579 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34580 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34583 horizontalMeasureColumns : function()
34585 this.getContainerWidth();
34587 var boxWidth = this.boxWidth;
34589 if(this.containerWidth < boxWidth){
34590 boxWidth = this.containerWidth;
34593 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34595 this.el.setHeight(boxWidth);
34599 getContainerWidth : function()
34601 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34604 layoutItems : function( isInstant )
34606 Roo.log(this.bricks);
34608 var items = Roo.apply([], this.bricks);
34610 if(this.isHorizontal){
34611 this._horizontalLayoutItems( items , isInstant );
34615 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34616 // this._verticalAlternativeLayoutItems( items , isInstant );
34620 this._verticalLayoutItems( items , isInstant );
34624 _verticalLayoutItems : function ( items , isInstant)
34626 if ( !items || !items.length ) {
34631 ['xs', 'xs', 'xs', 'tall'],
34632 ['xs', 'xs', 'tall'],
34633 ['xs', 'xs', 'sm'],
34634 ['xs', 'xs', 'xs'],
34640 ['sm', 'xs', 'xs'],
34644 ['tall', 'xs', 'xs', 'xs'],
34645 ['tall', 'xs', 'xs'],
34657 Roo.each(items, function(item, k){
34659 switch (item.size) {
34660 // these layouts take up a full box,
34671 boxes.push([item]);
34694 var filterPattern = function(box, length)
34702 var pattern = box.slice(0, length);
34706 Roo.each(pattern, function(i){
34707 format.push(i.size);
34710 Roo.each(standard, function(s){
34712 if(String(s) != String(format)){
34721 if(!match && length == 1){
34726 filterPattern(box, length - 1);
34730 queue.push(pattern);
34732 box = box.slice(length, box.length);
34734 filterPattern(box, 4);
34740 Roo.each(boxes, function(box, k){
34746 if(box.length == 1){
34751 filterPattern(box, 4);
34755 this._processVerticalLayoutQueue( queue, isInstant );
34759 // _verticalAlternativeLayoutItems : function( items , isInstant )
34761 // if ( !items || !items.length ) {
34765 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34769 _horizontalLayoutItems : function ( items , isInstant)
34771 if ( !items || !items.length || items.length < 3) {
34777 var eItems = items.slice(0, 3);
34779 items = items.slice(3, items.length);
34782 ['xs', 'xs', 'xs', 'wide'],
34783 ['xs', 'xs', 'wide'],
34784 ['xs', 'xs', 'sm'],
34785 ['xs', 'xs', 'xs'],
34791 ['sm', 'xs', 'xs'],
34795 ['wide', 'xs', 'xs', 'xs'],
34796 ['wide', 'xs', 'xs'],
34809 Roo.each(items, function(item, k){
34811 switch (item.size) {
34822 boxes.push([item]);
34846 var filterPattern = function(box, length)
34854 var pattern = box.slice(0, length);
34858 Roo.each(pattern, function(i){
34859 format.push(i.size);
34862 Roo.each(standard, function(s){
34864 if(String(s) != String(format)){
34873 if(!match && length == 1){
34878 filterPattern(box, length - 1);
34882 queue.push(pattern);
34884 box = box.slice(length, box.length);
34886 filterPattern(box, 4);
34892 Roo.each(boxes, function(box, k){
34898 if(box.length == 1){
34903 filterPattern(box, 4);
34910 var pos = this.el.getBox(true);
34914 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34916 var hit_end = false;
34918 Roo.each(queue, function(box){
34922 Roo.each(box, function(b){
34924 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34934 Roo.each(box, function(b){
34936 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34939 mx = Math.max(mx, b.x);
34943 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34947 Roo.each(box, function(b){
34949 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34963 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34966 /** Sets position of item in DOM
34967 * @param {Element} item
34968 * @param {Number} x - horizontal position
34969 * @param {Number} y - vertical position
34970 * @param {Boolean} isInstant - disables transitions
34972 _processVerticalLayoutQueue : function( queue, isInstant )
34974 var pos = this.el.getBox(true);
34979 for (var i = 0; i < this.cols; i++){
34983 Roo.each(queue, function(box, k){
34985 var col = k % this.cols;
34987 Roo.each(box, function(b,kk){
34989 b.el.position('absolute');
34991 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34992 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34994 if(b.size == 'md-left' || b.size == 'md-right'){
34995 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34996 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34999 b.el.setWidth(width);
35000 b.el.setHeight(height);
35002 b.el.select('iframe',true).setSize(width,height);
35006 for (var i = 0; i < this.cols; i++){
35008 if(maxY[i] < maxY[col]){
35013 col = Math.min(col, i);
35017 x = pos.x + col * (this.colWidth + this.padWidth);
35021 var positions = [];
35023 switch (box.length){
35025 positions = this.getVerticalOneBoxColPositions(x, y, box);
35028 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35031 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35034 positions = this.getVerticalFourBoxColPositions(x, y, box);
35040 Roo.each(box, function(b,kk){
35042 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35044 var sz = b.el.getSize();
35046 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35054 for (var i = 0; i < this.cols; i++){
35055 mY = Math.max(mY, maxY[i]);
35058 this.el.setHeight(mY - pos.y);
35062 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35064 // var pos = this.el.getBox(true);
35067 // var maxX = pos.right;
35069 // var maxHeight = 0;
35071 // Roo.each(items, function(item, k){
35075 // item.el.position('absolute');
35077 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35079 // item.el.setWidth(width);
35081 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35083 // item.el.setHeight(height);
35086 // item.el.setXY([x, y], isInstant ? false : true);
35088 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35091 // y = y + height + this.alternativePadWidth;
35093 // maxHeight = maxHeight + height + this.alternativePadWidth;
35097 // this.el.setHeight(maxHeight);
35101 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35103 var pos = this.el.getBox(true);
35108 var maxX = pos.right;
35110 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35112 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35114 Roo.each(queue, function(box, k){
35116 Roo.each(box, function(b, kk){
35118 b.el.position('absolute');
35120 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35121 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35123 if(b.size == 'md-left' || b.size == 'md-right'){
35124 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35125 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35128 b.el.setWidth(width);
35129 b.el.setHeight(height);
35137 var positions = [];
35139 switch (box.length){
35141 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35144 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35147 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35150 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35156 Roo.each(box, function(b,kk){
35158 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35160 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35168 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35170 Roo.each(eItems, function(b,k){
35172 b.size = (k == 0) ? 'sm' : 'xs';
35173 b.x = (k == 0) ? 2 : 1;
35174 b.y = (k == 0) ? 2 : 1;
35176 b.el.position('absolute');
35178 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35180 b.el.setWidth(width);
35182 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35184 b.el.setHeight(height);
35188 var positions = [];
35191 x : maxX - this.unitWidth * 2 - this.gutter,
35196 x : maxX - this.unitWidth,
35197 y : minY + (this.unitWidth + this.gutter) * 2
35201 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35205 Roo.each(eItems, function(b,k){
35207 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35213 getVerticalOneBoxColPositions : function(x, y, box)
35217 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35219 if(box[0].size == 'md-left'){
35223 if(box[0].size == 'md-right'){
35228 x : x + (this.unitWidth + this.gutter) * rand,
35235 getVerticalTwoBoxColPositions : function(x, y, box)
35239 if(box[0].size == 'xs'){
35243 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35247 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35261 x : x + (this.unitWidth + this.gutter) * 2,
35262 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35269 getVerticalThreeBoxColPositions : function(x, y, box)
35273 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35281 x : x + (this.unitWidth + this.gutter) * 1,
35286 x : x + (this.unitWidth + this.gutter) * 2,
35294 if(box[0].size == 'xs' && box[1].size == 'xs'){
35303 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35307 x : x + (this.unitWidth + this.gutter) * 1,
35321 x : x + (this.unitWidth + this.gutter) * 2,
35326 x : x + (this.unitWidth + this.gutter) * 2,
35327 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35334 getVerticalFourBoxColPositions : function(x, y, box)
35338 if(box[0].size == 'xs'){
35347 y : y + (this.unitHeight + this.gutter) * 1
35352 y : y + (this.unitHeight + this.gutter) * 2
35356 x : x + (this.unitWidth + this.gutter) * 1,
35370 x : x + (this.unitWidth + this.gutter) * 2,
35375 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35376 y : y + (this.unitHeight + this.gutter) * 1
35380 x : x + (this.unitWidth + this.gutter) * 2,
35381 y : y + (this.unitWidth + this.gutter) * 2
35388 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35392 if(box[0].size == 'md-left'){
35394 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35401 if(box[0].size == 'md-right'){
35403 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35404 y : minY + (this.unitWidth + this.gutter) * 1
35410 var rand = Math.floor(Math.random() * (4 - box[0].y));
35413 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35414 y : minY + (this.unitWidth + this.gutter) * rand
35421 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35425 if(box[0].size == 'xs'){
35428 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35433 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35434 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35442 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35447 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35448 y : minY + (this.unitWidth + this.gutter) * 2
35455 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35459 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35462 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35467 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35468 y : minY + (this.unitWidth + this.gutter) * 1
35472 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35473 y : minY + (this.unitWidth + this.gutter) * 2
35480 if(box[0].size == 'xs' && box[1].size == 'xs'){
35483 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35488 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35493 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35494 y : minY + (this.unitWidth + this.gutter) * 1
35502 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35507 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35508 y : minY + (this.unitWidth + this.gutter) * 2
35512 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35513 y : minY + (this.unitWidth + this.gutter) * 2
35520 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35524 if(box[0].size == 'xs'){
35527 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35532 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35537 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),
35542 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35543 y : minY + (this.unitWidth + this.gutter) * 1
35551 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35556 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35557 y : minY + (this.unitWidth + this.gutter) * 2
35561 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35562 y : minY + (this.unitWidth + this.gutter) * 2
35566 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),
35567 y : minY + (this.unitWidth + this.gutter) * 2
35575 * remove a Masonry Brick
35576 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35578 removeBrick : function(brick_id)
35584 for (var i = 0; i<this.bricks.length; i++) {
35585 if (this.bricks[i].id == brick_id) {
35586 this.bricks.splice(i,1);
35587 this.el.dom.removeChild(Roo.get(brick_id).dom);
35594 * adds a Masonry Brick
35595 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35597 addBrick : function(cfg)
35599 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35600 //this.register(cn);
35601 cn.parentId = this.id;
35602 cn.render(this.el);
35607 * register a Masonry Brick
35608 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35611 register : function(brick)
35613 this.bricks.push(brick);
35614 brick.masonryId = this.id;
35618 * clear all the Masonry Brick
35620 clearAll : function()
35623 //this.getChildContainer().dom.innerHTML = "";
35624 this.el.dom.innerHTML = '';
35627 getSelected : function()
35629 if (!this.selectedBrick) {
35633 return this.selectedBrick;
35637 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35641 * register a Masonry Layout
35642 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35645 register : function(layout)
35647 this.groups[layout.id] = layout;
35650 * fetch a Masonry Layout based on the masonry layout ID
35651 * @param {string} the masonry layout to add
35652 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35655 get: function(layout_id) {
35656 if (typeof(this.groups[layout_id]) == 'undefined') {
35659 return this.groups[layout_id] ;
35671 * http://masonry.desandro.com
35673 * The idea is to render all the bricks based on vertical width...
35675 * The original code extends 'outlayer' - we might need to use that....
35681 * @class Roo.bootstrap.LayoutMasonryAuto
35682 * @extends Roo.bootstrap.Component
35683 * Bootstrap Layout Masonry class
35686 * Create a new Element
35687 * @param {Object} config The config object
35690 Roo.bootstrap.LayoutMasonryAuto = function(config){
35691 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35694 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35697 * @cfg {Boolean} isFitWidth - resize the width..
35699 isFitWidth : false, // options..
35701 * @cfg {Boolean} isOriginLeft = left align?
35703 isOriginLeft : true,
35705 * @cfg {Boolean} isOriginTop = top align?
35707 isOriginTop : false,
35709 * @cfg {Boolean} isLayoutInstant = no animation?
35711 isLayoutInstant : false, // needed?
35713 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35715 isResizingContainer : true,
35717 * @cfg {Number} columnWidth width of the columns
35723 * @cfg {Number} maxCols maximum number of columns
35728 * @cfg {Number} padHeight padding below box..
35734 * @cfg {Boolean} isAutoInitial defalut true
35737 isAutoInitial : true,
35743 initialColumnWidth : 0,
35744 currentSize : null,
35746 colYs : null, // array.
35753 bricks: null, //CompositeElement
35754 cols : 0, // array?
35755 // element : null, // wrapped now this.el
35756 _isLayoutInited : null,
35759 getAutoCreate : function(){
35763 cls: 'blog-masonary-wrapper ' + this.cls,
35765 cls : 'mas-boxes masonary'
35772 getChildContainer: function( )
35774 if (this.boxesEl) {
35775 return this.boxesEl;
35778 this.boxesEl = this.el.select('.mas-boxes').first();
35780 return this.boxesEl;
35784 initEvents : function()
35788 if(this.isAutoInitial){
35789 Roo.log('hook children rendered');
35790 this.on('childrenrendered', function() {
35791 Roo.log('children rendered');
35798 initial : function()
35800 this.reloadItems();
35802 this.currentSize = this.el.getBox(true);
35804 /// was window resize... - let's see if this works..
35805 Roo.EventManager.onWindowResize(this.resize, this);
35807 if(!this.isAutoInitial){
35812 this.layout.defer(500,this);
35815 reloadItems: function()
35817 this.bricks = this.el.select('.masonry-brick', true);
35819 this.bricks.each(function(b) {
35820 //Roo.log(b.getSize());
35821 if (!b.attr('originalwidth')) {
35822 b.attr('originalwidth', b.getSize().width);
35827 Roo.log(this.bricks.elements.length);
35830 resize : function()
35833 var cs = this.el.getBox(true);
35835 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35836 Roo.log("no change in with or X");
35839 this.currentSize = cs;
35843 layout : function()
35846 this._resetLayout();
35847 //this._manageStamps();
35849 // don't animate first layout
35850 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35851 this.layoutItems( isInstant );
35853 // flag for initalized
35854 this._isLayoutInited = true;
35857 layoutItems : function( isInstant )
35859 //var items = this._getItemsForLayout( this.items );
35860 // original code supports filtering layout items.. we just ignore it..
35862 this._layoutItems( this.bricks , isInstant );
35864 this._postLayout();
35866 _layoutItems : function ( items , isInstant)
35868 //this.fireEvent( 'layout', this, items );
35871 if ( !items || !items.elements.length ) {
35872 // no items, emit event with empty array
35877 items.each(function(item) {
35878 Roo.log("layout item");
35880 // get x/y object from method
35881 var position = this._getItemLayoutPosition( item );
35883 position.item = item;
35884 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35885 queue.push( position );
35888 this._processLayoutQueue( queue );
35890 /** Sets position of item in DOM
35891 * @param {Element} item
35892 * @param {Number} x - horizontal position
35893 * @param {Number} y - vertical position
35894 * @param {Boolean} isInstant - disables transitions
35896 _processLayoutQueue : function( queue )
35898 for ( var i=0, len = queue.length; i < len; i++ ) {
35899 var obj = queue[i];
35900 obj.item.position('absolute');
35901 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35907 * Any logic you want to do after each layout,
35908 * i.e. size the container
35910 _postLayout : function()
35912 this.resizeContainer();
35915 resizeContainer : function()
35917 if ( !this.isResizingContainer ) {
35920 var size = this._getContainerSize();
35922 this.el.setSize(size.width,size.height);
35923 this.boxesEl.setSize(size.width,size.height);
35929 _resetLayout : function()
35931 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35932 this.colWidth = this.el.getWidth();
35933 //this.gutter = this.el.getWidth();
35935 this.measureColumns();
35941 this.colYs.push( 0 );
35947 measureColumns : function()
35949 this.getContainerWidth();
35950 // if columnWidth is 0, default to outerWidth of first item
35951 if ( !this.columnWidth ) {
35952 var firstItem = this.bricks.first();
35953 Roo.log(firstItem);
35954 this.columnWidth = this.containerWidth;
35955 if (firstItem && firstItem.attr('originalwidth') ) {
35956 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35958 // columnWidth fall back to item of first element
35959 Roo.log("set column width?");
35960 this.initialColumnWidth = this.columnWidth ;
35962 // if first elem has no width, default to size of container
35967 if (this.initialColumnWidth) {
35968 this.columnWidth = this.initialColumnWidth;
35973 // column width is fixed at the top - however if container width get's smaller we should
35976 // this bit calcs how man columns..
35978 var columnWidth = this.columnWidth += this.gutter;
35980 // calculate columns
35981 var containerWidth = this.containerWidth + this.gutter;
35983 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35984 // fix rounding errors, typically with gutters
35985 var excess = columnWidth - containerWidth % columnWidth;
35988 // if overshoot is less than a pixel, round up, otherwise floor it
35989 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35990 cols = Math[ mathMethod ]( cols );
35991 this.cols = Math.max( cols, 1 );
35992 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35994 // padding positioning..
35995 var totalColWidth = this.cols * this.columnWidth;
35996 var padavail = this.containerWidth - totalColWidth;
35997 // so for 2 columns - we need 3 'pads'
35999 var padNeeded = (1+this.cols) * this.padWidth;
36001 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36003 this.columnWidth += padExtra
36004 //this.padWidth = Math.floor(padavail / ( this.cols));
36006 // adjust colum width so that padding is fixed??
36008 // we have 3 columns ... total = width * 3
36009 // we have X left over... that should be used by
36011 //if (this.expandC) {
36019 getContainerWidth : function()
36021 /* // container is parent if fit width
36022 var container = this.isFitWidth ? this.element.parentNode : this.element;
36023 // check that this.size and size are there
36024 // IE8 triggers resize on body size change, so they might not be
36026 var size = getSize( container ); //FIXME
36027 this.containerWidth = size && size.innerWidth; //FIXME
36030 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36034 _getItemLayoutPosition : function( item ) // what is item?
36036 // we resize the item to our columnWidth..
36038 item.setWidth(this.columnWidth);
36039 item.autoBoxAdjust = false;
36041 var sz = item.getSize();
36043 // how many columns does this brick span
36044 var remainder = this.containerWidth % this.columnWidth;
36046 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36047 // round if off by 1 pixel, otherwise use ceil
36048 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36049 colSpan = Math.min( colSpan, this.cols );
36051 // normally this should be '1' as we dont' currently allow multi width columns..
36053 var colGroup = this._getColGroup( colSpan );
36054 // get the minimum Y value from the columns
36055 var minimumY = Math.min.apply( Math, colGroup );
36056 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36058 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36060 // position the brick
36062 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36063 y: this.currentSize.y + minimumY + this.padHeight
36067 // apply setHeight to necessary columns
36068 var setHeight = minimumY + sz.height + this.padHeight;
36069 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36071 var setSpan = this.cols + 1 - colGroup.length;
36072 for ( var i = 0; i < setSpan; i++ ) {
36073 this.colYs[ shortColIndex + i ] = setHeight ;
36080 * @param {Number} colSpan - number of columns the element spans
36081 * @returns {Array} colGroup
36083 _getColGroup : function( colSpan )
36085 if ( colSpan < 2 ) {
36086 // if brick spans only one column, use all the column Ys
36091 // how many different places could this brick fit horizontally
36092 var groupCount = this.cols + 1 - colSpan;
36093 // for each group potential horizontal position
36094 for ( var i = 0; i < groupCount; i++ ) {
36095 // make an array of colY values for that one group
36096 var groupColYs = this.colYs.slice( i, i + colSpan );
36097 // and get the max value of the array
36098 colGroup[i] = Math.max.apply( Math, groupColYs );
36103 _manageStamp : function( stamp )
36105 var stampSize = stamp.getSize();
36106 var offset = stamp.getBox();
36107 // get the columns that this stamp affects
36108 var firstX = this.isOriginLeft ? offset.x : offset.right;
36109 var lastX = firstX + stampSize.width;
36110 var firstCol = Math.floor( firstX / this.columnWidth );
36111 firstCol = Math.max( 0, firstCol );
36113 var lastCol = Math.floor( lastX / this.columnWidth );
36114 // lastCol should not go over if multiple of columnWidth #425
36115 lastCol -= lastX % this.columnWidth ? 0 : 1;
36116 lastCol = Math.min( this.cols - 1, lastCol );
36118 // set colYs to bottom of the stamp
36119 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36122 for ( var i = firstCol; i <= lastCol; i++ ) {
36123 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36128 _getContainerSize : function()
36130 this.maxY = Math.max.apply( Math, this.colYs );
36135 if ( this.isFitWidth ) {
36136 size.width = this._getContainerFitWidth();
36142 _getContainerFitWidth : function()
36144 var unusedCols = 0;
36145 // count unused columns
36148 if ( this.colYs[i] !== 0 ) {
36153 // fit container to columns that have been used
36154 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36157 needsResizeLayout : function()
36159 var previousWidth = this.containerWidth;
36160 this.getContainerWidth();
36161 return previousWidth !== this.containerWidth;
36176 * @class Roo.bootstrap.MasonryBrick
36177 * @extends Roo.bootstrap.Component
36178 * Bootstrap MasonryBrick class
36181 * Create a new MasonryBrick
36182 * @param {Object} config The config object
36185 Roo.bootstrap.MasonryBrick = function(config){
36187 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36189 Roo.bootstrap.MasonryBrick.register(this);
36195 * When a MasonryBrick is clcik
36196 * @param {Roo.bootstrap.MasonryBrick} this
36197 * @param {Roo.EventObject} e
36203 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36206 * @cfg {String} title
36210 * @cfg {String} html
36214 * @cfg {String} bgimage
36218 * @cfg {String} videourl
36222 * @cfg {String} cls
36226 * @cfg {String} href
36230 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36235 * @cfg {String} placetitle (center|bottom)
36240 * @cfg {Boolean} isFitContainer defalut true
36242 isFitContainer : true,
36245 * @cfg {Boolean} preventDefault defalut false
36247 preventDefault : false,
36250 * @cfg {Boolean} inverse defalut false
36252 maskInverse : false,
36254 getAutoCreate : function()
36256 if(!this.isFitContainer){
36257 return this.getSplitAutoCreate();
36260 var cls = 'masonry-brick masonry-brick-full';
36262 if(this.href.length){
36263 cls += ' masonry-brick-link';
36266 if(this.bgimage.length){
36267 cls += ' masonry-brick-image';
36270 if(this.maskInverse){
36271 cls += ' mask-inverse';
36274 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36275 cls += ' enable-mask';
36279 cls += ' masonry-' + this.size + '-brick';
36282 if(this.placetitle.length){
36284 switch (this.placetitle) {
36286 cls += ' masonry-center-title';
36289 cls += ' masonry-bottom-title';
36296 if(!this.html.length && !this.bgimage.length){
36297 cls += ' masonry-center-title';
36300 if(!this.html.length && this.bgimage.length){
36301 cls += ' masonry-bottom-title';
36306 cls += ' ' + this.cls;
36310 tag: (this.href.length) ? 'a' : 'div',
36315 cls: 'masonry-brick-mask'
36319 cls: 'masonry-brick-paragraph',
36325 if(this.href.length){
36326 cfg.href = this.href;
36329 var cn = cfg.cn[1].cn;
36331 if(this.title.length){
36334 cls: 'masonry-brick-title',
36339 if(this.html.length){
36342 cls: 'masonry-brick-text',
36347 if (!this.title.length && !this.html.length) {
36348 cfg.cn[1].cls += ' hide';
36351 if(this.bgimage.length){
36354 cls: 'masonry-brick-image-view',
36359 if(this.videourl.length){
36360 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36361 // youtube support only?
36364 cls: 'masonry-brick-image-view',
36367 allowfullscreen : true
36375 getSplitAutoCreate : function()
36377 var cls = 'masonry-brick masonry-brick-split';
36379 if(this.href.length){
36380 cls += ' masonry-brick-link';
36383 if(this.bgimage.length){
36384 cls += ' masonry-brick-image';
36388 cls += ' masonry-' + this.size + '-brick';
36391 switch (this.placetitle) {
36393 cls += ' masonry-center-title';
36396 cls += ' masonry-bottom-title';
36399 if(!this.bgimage.length){
36400 cls += ' masonry-center-title';
36403 if(this.bgimage.length){
36404 cls += ' masonry-bottom-title';
36410 cls += ' ' + this.cls;
36414 tag: (this.href.length) ? 'a' : 'div',
36419 cls: 'masonry-brick-split-head',
36423 cls: 'masonry-brick-paragraph',
36430 cls: 'masonry-brick-split-body',
36436 if(this.href.length){
36437 cfg.href = this.href;
36440 if(this.title.length){
36441 cfg.cn[0].cn[0].cn.push({
36443 cls: 'masonry-brick-title',
36448 if(this.html.length){
36449 cfg.cn[1].cn.push({
36451 cls: 'masonry-brick-text',
36456 if(this.bgimage.length){
36457 cfg.cn[0].cn.push({
36459 cls: 'masonry-brick-image-view',
36464 if(this.videourl.length){
36465 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36466 // youtube support only?
36467 cfg.cn[0].cn.cn.push({
36469 cls: 'masonry-brick-image-view',
36472 allowfullscreen : true
36479 initEvents: function()
36481 switch (this.size) {
36514 this.el.on('touchstart', this.onTouchStart, this);
36515 this.el.on('touchmove', this.onTouchMove, this);
36516 this.el.on('touchend', this.onTouchEnd, this);
36517 this.el.on('contextmenu', this.onContextMenu, this);
36519 this.el.on('mouseenter' ,this.enter, this);
36520 this.el.on('mouseleave', this.leave, this);
36521 this.el.on('click', this.onClick, this);
36524 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36525 this.parent().bricks.push(this);
36530 onClick: function(e, el)
36532 var time = this.endTimer - this.startTimer;
36533 // Roo.log(e.preventDefault());
36536 e.preventDefault();
36541 if(!this.preventDefault){
36545 e.preventDefault();
36547 if (this.activeClass != '') {
36548 this.selectBrick();
36551 this.fireEvent('click', this, e);
36554 enter: function(e, el)
36556 e.preventDefault();
36558 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36562 if(this.bgimage.length && this.html.length){
36563 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36567 leave: function(e, el)
36569 e.preventDefault();
36571 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36575 if(this.bgimage.length && this.html.length){
36576 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36580 onTouchStart: function(e, el)
36582 // e.preventDefault();
36584 this.touchmoved = false;
36586 if(!this.isFitContainer){
36590 if(!this.bgimage.length || !this.html.length){
36594 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36596 this.timer = new Date().getTime();
36600 onTouchMove: function(e, el)
36602 this.touchmoved = true;
36605 onContextMenu : function(e,el)
36607 e.preventDefault();
36608 e.stopPropagation();
36612 onTouchEnd: function(e, el)
36614 // e.preventDefault();
36616 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36623 if(!this.bgimage.length || !this.html.length){
36625 if(this.href.length){
36626 window.location.href = this.href;
36632 if(!this.isFitContainer){
36636 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36638 window.location.href = this.href;
36641 //selection on single brick only
36642 selectBrick : function() {
36644 if (!this.parentId) {
36648 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36649 var index = m.selectedBrick.indexOf(this.id);
36652 m.selectedBrick.splice(index,1);
36653 this.el.removeClass(this.activeClass);
36657 for(var i = 0; i < m.selectedBrick.length; i++) {
36658 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36659 b.el.removeClass(b.activeClass);
36662 m.selectedBrick = [];
36664 m.selectedBrick.push(this.id);
36665 this.el.addClass(this.activeClass);
36669 isSelected : function(){
36670 return this.el.hasClass(this.activeClass);
36675 Roo.apply(Roo.bootstrap.MasonryBrick, {
36678 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36680 * register a Masonry Brick
36681 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36684 register : function(brick)
36686 //this.groups[brick.id] = brick;
36687 this.groups.add(brick.id, brick);
36690 * fetch a masonry brick based on the masonry brick ID
36691 * @param {string} the masonry brick to add
36692 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36695 get: function(brick_id)
36697 // if (typeof(this.groups[brick_id]) == 'undefined') {
36700 // return this.groups[brick_id] ;
36702 if(this.groups.key(brick_id)) {
36703 return this.groups.key(brick_id);
36721 * @class Roo.bootstrap.Brick
36722 * @extends Roo.bootstrap.Component
36723 * Bootstrap Brick class
36726 * Create a new Brick
36727 * @param {Object} config The config object
36730 Roo.bootstrap.Brick = function(config){
36731 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36737 * When a Brick is click
36738 * @param {Roo.bootstrap.Brick} this
36739 * @param {Roo.EventObject} e
36745 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36748 * @cfg {String} title
36752 * @cfg {String} html
36756 * @cfg {String} bgimage
36760 * @cfg {String} cls
36764 * @cfg {String} href
36768 * @cfg {String} video
36772 * @cfg {Boolean} square
36776 getAutoCreate : function()
36778 var cls = 'roo-brick';
36780 if(this.href.length){
36781 cls += ' roo-brick-link';
36784 if(this.bgimage.length){
36785 cls += ' roo-brick-image';
36788 if(!this.html.length && !this.bgimage.length){
36789 cls += ' roo-brick-center-title';
36792 if(!this.html.length && this.bgimage.length){
36793 cls += ' roo-brick-bottom-title';
36797 cls += ' ' + this.cls;
36801 tag: (this.href.length) ? 'a' : 'div',
36806 cls: 'roo-brick-paragraph',
36812 if(this.href.length){
36813 cfg.href = this.href;
36816 var cn = cfg.cn[0].cn;
36818 if(this.title.length){
36821 cls: 'roo-brick-title',
36826 if(this.html.length){
36829 cls: 'roo-brick-text',
36836 if(this.bgimage.length){
36839 cls: 'roo-brick-image-view',
36847 initEvents: function()
36849 if(this.title.length || this.html.length){
36850 this.el.on('mouseenter' ,this.enter, this);
36851 this.el.on('mouseleave', this.leave, this);
36854 Roo.EventManager.onWindowResize(this.resize, this);
36856 if(this.bgimage.length){
36857 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36858 this.imageEl.on('load', this.onImageLoad, this);
36865 onImageLoad : function()
36870 resize : function()
36872 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36874 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36876 if(this.bgimage.length){
36877 var image = this.el.select('.roo-brick-image-view', true).first();
36879 image.setWidth(paragraph.getWidth());
36882 image.setHeight(paragraph.getWidth());
36885 this.el.setHeight(image.getHeight());
36886 paragraph.setHeight(image.getHeight());
36892 enter: function(e, el)
36894 e.preventDefault();
36896 if(this.bgimage.length){
36897 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36898 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36902 leave: function(e, el)
36904 e.preventDefault();
36906 if(this.bgimage.length){
36907 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36908 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36923 * @class Roo.bootstrap.NumberField
36924 * @extends Roo.bootstrap.Input
36925 * Bootstrap NumberField class
36931 * Create a new NumberField
36932 * @param {Object} config The config object
36935 Roo.bootstrap.NumberField = function(config){
36936 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36939 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36942 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36944 allowDecimals : true,
36946 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36948 decimalSeparator : ".",
36950 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36952 decimalPrecision : 2,
36954 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36956 allowNegative : true,
36959 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36963 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36965 minValue : Number.NEGATIVE_INFINITY,
36967 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36969 maxValue : Number.MAX_VALUE,
36971 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36973 minText : "The minimum value for this field is {0}",
36975 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36977 maxText : "The maximum value for this field is {0}",
36979 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36980 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36982 nanText : "{0} is not a valid number",
36984 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36986 thousandsDelimiter : false,
36988 * @cfg {String} valueAlign alignment of value
36990 valueAlign : "left",
36992 getAutoCreate : function()
36994 var hiddenInput = {
36998 cls: 'hidden-number-input'
37002 hiddenInput.name = this.name;
37007 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37009 this.name = hiddenInput.name;
37011 if(cfg.cn.length > 0) {
37012 cfg.cn.push(hiddenInput);
37019 initEvents : function()
37021 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37023 var allowed = "0123456789";
37025 if(this.allowDecimals){
37026 allowed += this.decimalSeparator;
37029 if(this.allowNegative){
37033 if(this.thousandsDelimiter) {
37037 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37039 var keyPress = function(e){
37041 var k = e.getKey();
37043 var c = e.getCharCode();
37046 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37047 allowed.indexOf(String.fromCharCode(c)) === -1
37053 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37057 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37062 this.el.on("keypress", keyPress, this);
37065 validateValue : function(value)
37068 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37072 var num = this.parseValue(value);
37075 this.markInvalid(String.format(this.nanText, value));
37079 if(num < this.minValue){
37080 this.markInvalid(String.format(this.minText, this.minValue));
37084 if(num > this.maxValue){
37085 this.markInvalid(String.format(this.maxText, this.maxValue));
37092 getValue : function()
37094 var v = this.hiddenEl().getValue();
37096 return this.fixPrecision(this.parseValue(v));
37099 parseValue : function(value)
37101 if(this.thousandsDelimiter) {
37103 r = new RegExp(",", "g");
37104 value = value.replace(r, "");
37107 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37108 return isNaN(value) ? '' : value;
37111 fixPrecision : function(value)
37113 if(this.thousandsDelimiter) {
37115 r = new RegExp(",", "g");
37116 value = value.replace(r, "");
37119 var nan = isNaN(value);
37121 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37122 return nan ? '' : value;
37124 return parseFloat(value).toFixed(this.decimalPrecision);
37127 setValue : function(v)
37129 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37135 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37137 this.inputEl().dom.value = (v == '') ? '' :
37138 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37140 if(!this.allowZero && v === '0') {
37141 this.hiddenEl().dom.value = '';
37142 this.inputEl().dom.value = '';
37149 decimalPrecisionFcn : function(v)
37151 return Math.floor(v);
37154 beforeBlur : function()
37156 var v = this.parseValue(this.getRawValue());
37158 if(v || v === 0 || v === ''){
37163 hiddenEl : function()
37165 return this.el.select('input.hidden-number-input',true).first();
37177 * @class Roo.bootstrap.DocumentSlider
37178 * @extends Roo.bootstrap.Component
37179 * Bootstrap DocumentSlider class
37182 * Create a new DocumentViewer
37183 * @param {Object} config The config object
37186 Roo.bootstrap.DocumentSlider = function(config){
37187 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37194 * Fire after initEvent
37195 * @param {Roo.bootstrap.DocumentSlider} this
37200 * Fire after update
37201 * @param {Roo.bootstrap.DocumentSlider} this
37207 * @param {Roo.bootstrap.DocumentSlider} this
37213 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37219 getAutoCreate : function()
37223 cls : 'roo-document-slider',
37227 cls : 'roo-document-slider-header',
37231 cls : 'roo-document-slider-header-title'
37237 cls : 'roo-document-slider-body',
37241 cls : 'roo-document-slider-prev',
37245 cls : 'fa fa-chevron-left'
37251 cls : 'roo-document-slider-thumb',
37255 cls : 'roo-document-slider-image'
37261 cls : 'roo-document-slider-next',
37265 cls : 'fa fa-chevron-right'
37277 initEvents : function()
37279 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37280 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37282 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37283 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37285 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37286 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37288 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37289 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37291 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37292 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37294 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37295 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37297 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37298 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37300 this.thumbEl.on('click', this.onClick, this);
37302 this.prevIndicator.on('click', this.prev, this);
37304 this.nextIndicator.on('click', this.next, this);
37308 initial : function()
37310 if(this.files.length){
37311 this.indicator = 1;
37315 this.fireEvent('initial', this);
37318 update : function()
37320 this.imageEl.attr('src', this.files[this.indicator - 1]);
37322 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37324 this.prevIndicator.show();
37326 if(this.indicator == 1){
37327 this.prevIndicator.hide();
37330 this.nextIndicator.show();
37332 if(this.indicator == this.files.length){
37333 this.nextIndicator.hide();
37336 this.thumbEl.scrollTo('top');
37338 this.fireEvent('update', this);
37341 onClick : function(e)
37343 e.preventDefault();
37345 this.fireEvent('click', this);
37350 e.preventDefault();
37352 this.indicator = Math.max(1, this.indicator - 1);
37359 e.preventDefault();
37361 this.indicator = Math.min(this.files.length, this.indicator + 1);
37375 * @class Roo.bootstrap.RadioSet
37376 * @extends Roo.bootstrap.Input
37377 * Bootstrap RadioSet class
37378 * @cfg {String} indicatorpos (left|right) default left
37379 * @cfg {Boolean} inline (true|false) inline the element (default true)
37380 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37382 * Create a new RadioSet
37383 * @param {Object} config The config object
37386 Roo.bootstrap.RadioSet = function(config){
37388 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37392 Roo.bootstrap.RadioSet.register(this);
37397 * Fires when the element is checked or unchecked.
37398 * @param {Roo.bootstrap.RadioSet} this This radio
37399 * @param {Roo.bootstrap.Radio} item The checked item
37404 * Fires when the element is click.
37405 * @param {Roo.bootstrap.RadioSet} this This radio set
37406 * @param {Roo.bootstrap.Radio} item The checked item
37407 * @param {Roo.EventObject} e The event object
37414 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37422 indicatorpos : 'left',
37424 getAutoCreate : function()
37428 cls : 'roo-radio-set-label',
37432 html : this.fieldLabel
37436 if (Roo.bootstrap.version == 3) {
37439 if(this.indicatorpos == 'left'){
37442 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37443 tooltip : 'This field is required'
37448 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37449 tooltip : 'This field is required'
37455 cls : 'roo-radio-set-items'
37458 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37460 if (align === 'left' && this.fieldLabel.length) {
37463 cls : "roo-radio-set-right",
37469 if(this.labelWidth > 12){
37470 label.style = "width: " + this.labelWidth + 'px';
37473 if(this.labelWidth < 13 && this.labelmd == 0){
37474 this.labelmd = this.labelWidth;
37477 if(this.labellg > 0){
37478 label.cls += ' col-lg-' + this.labellg;
37479 items.cls += ' col-lg-' + (12 - this.labellg);
37482 if(this.labelmd > 0){
37483 label.cls += ' col-md-' + this.labelmd;
37484 items.cls += ' col-md-' + (12 - this.labelmd);
37487 if(this.labelsm > 0){
37488 label.cls += ' col-sm-' + this.labelsm;
37489 items.cls += ' col-sm-' + (12 - this.labelsm);
37492 if(this.labelxs > 0){
37493 label.cls += ' col-xs-' + this.labelxs;
37494 items.cls += ' col-xs-' + (12 - this.labelxs);
37500 cls : 'roo-radio-set',
37504 cls : 'roo-radio-set-input',
37507 value : this.value ? this.value : ''
37514 if(this.weight.length){
37515 cfg.cls += ' roo-radio-' + this.weight;
37519 cfg.cls += ' roo-radio-set-inline';
37523 ['xs','sm','md','lg'].map(function(size){
37524 if (settings[size]) {
37525 cfg.cls += ' col-' + size + '-' + settings[size];
37533 initEvents : function()
37535 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37536 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37538 if(!this.fieldLabel.length){
37539 this.labelEl.hide();
37542 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37543 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37545 this.indicator = this.indicatorEl();
37547 if(this.indicator){
37548 this.indicator.addClass('invisible');
37551 this.originalValue = this.getValue();
37555 inputEl: function ()
37557 return this.el.select('.roo-radio-set-input', true).first();
37560 getChildContainer : function()
37562 return this.itemsEl;
37565 register : function(item)
37567 this.radioes.push(item);
37571 validate : function()
37573 if(this.getVisibilityEl().hasClass('hidden')){
37579 Roo.each(this.radioes, function(i){
37588 if(this.allowBlank) {
37592 if(this.disabled || valid){
37597 this.markInvalid();
37602 markValid : function()
37604 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37605 this.indicatorEl().removeClass('visible');
37606 this.indicatorEl().addClass('invisible');
37610 if (Roo.bootstrap.version == 3) {
37611 this.el.removeClass([this.invalidClass, this.validClass]);
37612 this.el.addClass(this.validClass);
37614 this.el.removeClass(['is-invalid','is-valid']);
37615 this.el.addClass(['is-valid']);
37617 this.fireEvent('valid', this);
37620 markInvalid : function(msg)
37622 if(this.allowBlank || this.disabled){
37626 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37627 this.indicatorEl().removeClass('invisible');
37628 this.indicatorEl().addClass('visible');
37630 if (Roo.bootstrap.version == 3) {
37631 this.el.removeClass([this.invalidClass, this.validClass]);
37632 this.el.addClass(this.invalidClass);
37634 this.el.removeClass(['is-invalid','is-valid']);
37635 this.el.addClass(['is-invalid']);
37638 this.fireEvent('invalid', this, msg);
37642 setValue : function(v, suppressEvent)
37644 if(this.value === v){
37651 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37654 Roo.each(this.radioes, function(i){
37656 i.el.removeClass('checked');
37659 Roo.each(this.radioes, function(i){
37661 if(i.value === v || i.value.toString() === v.toString()){
37663 i.el.addClass('checked');
37665 if(suppressEvent !== true){
37666 this.fireEvent('check', this, i);
37677 clearInvalid : function(){
37679 if(!this.el || this.preventMark){
37683 this.el.removeClass([this.invalidClass]);
37685 this.fireEvent('valid', this);
37690 Roo.apply(Roo.bootstrap.RadioSet, {
37694 register : function(set)
37696 this.groups[set.name] = set;
37699 get: function(name)
37701 if (typeof(this.groups[name]) == 'undefined') {
37705 return this.groups[name] ;
37711 * Ext JS Library 1.1.1
37712 * Copyright(c) 2006-2007, Ext JS, LLC.
37714 * Originally Released Under LGPL - original licence link has changed is not relivant.
37717 * <script type="text/javascript">
37722 * @class Roo.bootstrap.SplitBar
37723 * @extends Roo.util.Observable
37724 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37728 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37729 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37730 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37731 split.minSize = 100;
37732 split.maxSize = 600;
37733 split.animate = true;
37734 split.on('moved', splitterMoved);
37737 * Create a new SplitBar
37738 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37739 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37740 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37741 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37742 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37743 position of the SplitBar).
37745 Roo.bootstrap.SplitBar = function(cfg){
37750 // dragElement : elm
37751 // resizingElement: el,
37753 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37754 // placement : Roo.bootstrap.SplitBar.LEFT ,
37755 // existingProxy ???
37758 this.el = Roo.get(cfg.dragElement, true);
37759 this.el.dom.unselectable = "on";
37761 this.resizingEl = Roo.get(cfg.resizingElement, true);
37765 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37766 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37769 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37772 * The minimum size of the resizing element. (Defaults to 0)
37778 * The maximum size of the resizing element. (Defaults to 2000)
37781 this.maxSize = 2000;
37784 * Whether to animate the transition to the new size
37787 this.animate = false;
37790 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37793 this.useShim = false;
37798 if(!cfg.existingProxy){
37800 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37802 this.proxy = Roo.get(cfg.existingProxy).dom;
37805 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37808 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37811 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37814 this.dragSpecs = {};
37817 * @private The adapter to use to positon and resize elements
37819 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37820 this.adapter.init(this);
37822 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37824 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37825 this.el.addClass("roo-splitbar-h");
37828 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37829 this.el.addClass("roo-splitbar-v");
37835 * Fires when the splitter is moved (alias for {@link #event-moved})
37836 * @param {Roo.bootstrap.SplitBar} this
37837 * @param {Number} newSize the new width or height
37842 * Fires when the splitter is moved
37843 * @param {Roo.bootstrap.SplitBar} this
37844 * @param {Number} newSize the new width or height
37848 * @event beforeresize
37849 * Fires before the splitter is dragged
37850 * @param {Roo.bootstrap.SplitBar} this
37852 "beforeresize" : true,
37854 "beforeapply" : true
37857 Roo.util.Observable.call(this);
37860 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37861 onStartProxyDrag : function(x, y){
37862 this.fireEvent("beforeresize", this);
37864 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37866 o.enableDisplayMode("block");
37867 // all splitbars share the same overlay
37868 Roo.bootstrap.SplitBar.prototype.overlay = o;
37870 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37871 this.overlay.show();
37872 Roo.get(this.proxy).setDisplayed("block");
37873 var size = this.adapter.getElementSize(this);
37874 this.activeMinSize = this.getMinimumSize();;
37875 this.activeMaxSize = this.getMaximumSize();;
37876 var c1 = size - this.activeMinSize;
37877 var c2 = Math.max(this.activeMaxSize - size, 0);
37878 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37879 this.dd.resetConstraints();
37880 this.dd.setXConstraint(
37881 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37882 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37884 this.dd.setYConstraint(0, 0);
37886 this.dd.resetConstraints();
37887 this.dd.setXConstraint(0, 0);
37888 this.dd.setYConstraint(
37889 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37890 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37893 this.dragSpecs.startSize = size;
37894 this.dragSpecs.startPoint = [x, y];
37895 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37899 * @private Called after the drag operation by the DDProxy
37901 onEndProxyDrag : function(e){
37902 Roo.get(this.proxy).setDisplayed(false);
37903 var endPoint = Roo.lib.Event.getXY(e);
37905 this.overlay.hide();
37908 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37909 newSize = this.dragSpecs.startSize +
37910 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37911 endPoint[0] - this.dragSpecs.startPoint[0] :
37912 this.dragSpecs.startPoint[0] - endPoint[0]
37915 newSize = this.dragSpecs.startSize +
37916 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37917 endPoint[1] - this.dragSpecs.startPoint[1] :
37918 this.dragSpecs.startPoint[1] - endPoint[1]
37921 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37922 if(newSize != this.dragSpecs.startSize){
37923 if(this.fireEvent('beforeapply', this, newSize) !== false){
37924 this.adapter.setElementSize(this, newSize);
37925 this.fireEvent("moved", this, newSize);
37926 this.fireEvent("resize", this, newSize);
37932 * Get the adapter this SplitBar uses
37933 * @return The adapter object
37935 getAdapter : function(){
37936 return this.adapter;
37940 * Set the adapter this SplitBar uses
37941 * @param {Object} adapter A SplitBar adapter object
37943 setAdapter : function(adapter){
37944 this.adapter = adapter;
37945 this.adapter.init(this);
37949 * Gets the minimum size for the resizing element
37950 * @return {Number} The minimum size
37952 getMinimumSize : function(){
37953 return this.minSize;
37957 * Sets the minimum size for the resizing element
37958 * @param {Number} minSize The minimum size
37960 setMinimumSize : function(minSize){
37961 this.minSize = minSize;
37965 * Gets the maximum size for the resizing element
37966 * @return {Number} The maximum size
37968 getMaximumSize : function(){
37969 return this.maxSize;
37973 * Sets the maximum size for the resizing element
37974 * @param {Number} maxSize The maximum size
37976 setMaximumSize : function(maxSize){
37977 this.maxSize = maxSize;
37981 * Sets the initialize size for the resizing element
37982 * @param {Number} size The initial size
37984 setCurrentSize : function(size){
37985 var oldAnimate = this.animate;
37986 this.animate = false;
37987 this.adapter.setElementSize(this, size);
37988 this.animate = oldAnimate;
37992 * Destroy this splitbar.
37993 * @param {Boolean} removeEl True to remove the element
37995 destroy : function(removeEl){
37997 this.shim.remove();
38000 this.proxy.parentNode.removeChild(this.proxy);
38008 * @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.
38010 Roo.bootstrap.SplitBar.createProxy = function(dir){
38011 var proxy = new Roo.Element(document.createElement("div"));
38012 proxy.unselectable();
38013 var cls = 'roo-splitbar-proxy';
38014 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38015 document.body.appendChild(proxy.dom);
38020 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38021 * Default Adapter. It assumes the splitter and resizing element are not positioned
38022 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38024 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38027 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38028 // do nothing for now
38029 init : function(s){
38033 * Called before drag operations to get the current size of the resizing element.
38034 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38036 getElementSize : function(s){
38037 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38038 return s.resizingEl.getWidth();
38040 return s.resizingEl.getHeight();
38045 * Called after drag operations to set the size of the resizing element.
38046 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38047 * @param {Number} newSize The new size to set
38048 * @param {Function} onComplete A function to be invoked when resizing is complete
38050 setElementSize : function(s, newSize, onComplete){
38051 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38053 s.resizingEl.setWidth(newSize);
38055 onComplete(s, newSize);
38058 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38063 s.resizingEl.setHeight(newSize);
38065 onComplete(s, newSize);
38068 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38075 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38076 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38077 * Adapter that moves the splitter element to align with the resized sizing element.
38078 * Used with an absolute positioned SplitBar.
38079 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38080 * document.body, make sure you assign an id to the body element.
38082 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38083 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38084 this.container = Roo.get(container);
38087 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38088 init : function(s){
38089 this.basic.init(s);
38092 getElementSize : function(s){
38093 return this.basic.getElementSize(s);
38096 setElementSize : function(s, newSize, onComplete){
38097 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38100 moveSplitter : function(s){
38101 var yes = Roo.bootstrap.SplitBar;
38102 switch(s.placement){
38104 s.el.setX(s.resizingEl.getRight());
38107 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38110 s.el.setY(s.resizingEl.getBottom());
38113 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38120 * Orientation constant - Create a vertical SplitBar
38124 Roo.bootstrap.SplitBar.VERTICAL = 1;
38127 * Orientation constant - Create a horizontal SplitBar
38131 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38134 * Placement constant - The resizing element is to the left of the splitter element
38138 Roo.bootstrap.SplitBar.LEFT = 1;
38141 * Placement constant - The resizing element is to the right of the splitter element
38145 Roo.bootstrap.SplitBar.RIGHT = 2;
38148 * Placement constant - The resizing element is positioned above the splitter element
38152 Roo.bootstrap.SplitBar.TOP = 3;
38155 * Placement constant - The resizing element is positioned under splitter element
38159 Roo.bootstrap.SplitBar.BOTTOM = 4;
38160 Roo.namespace("Roo.bootstrap.layout");/*
38162 * Ext JS Library 1.1.1
38163 * Copyright(c) 2006-2007, Ext JS, LLC.
38165 * Originally Released Under LGPL - original licence link has changed is not relivant.
38168 * <script type="text/javascript">
38172 * @class Roo.bootstrap.layout.Manager
38173 * @extends Roo.bootstrap.Component
38174 * Base class for layout managers.
38176 Roo.bootstrap.layout.Manager = function(config)
38178 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38184 /** false to disable window resize monitoring @type Boolean */
38185 this.monitorWindowResize = true;
38190 * Fires when a layout is performed.
38191 * @param {Roo.LayoutManager} this
38195 * @event regionresized
38196 * Fires when the user resizes a region.
38197 * @param {Roo.LayoutRegion} region The resized region
38198 * @param {Number} newSize The new size (width for east/west, height for north/south)
38200 "regionresized" : true,
38202 * @event regioncollapsed
38203 * Fires when a region is collapsed.
38204 * @param {Roo.LayoutRegion} region The collapsed region
38206 "regioncollapsed" : true,
38208 * @event regionexpanded
38209 * Fires when a region is expanded.
38210 * @param {Roo.LayoutRegion} region The expanded region
38212 "regionexpanded" : true
38214 this.updating = false;
38217 this.el = Roo.get(config.el);
38223 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38228 monitorWindowResize : true,
38234 onRender : function(ct, position)
38237 this.el = Roo.get(ct);
38240 //this.fireEvent('render',this);
38244 initEvents: function()
38248 // ie scrollbar fix
38249 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38250 document.body.scroll = "no";
38251 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38252 this.el.position('relative');
38254 this.id = this.el.id;
38255 this.el.addClass("roo-layout-container");
38256 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38257 if(this.el.dom != document.body ) {
38258 this.el.on('resize', this.layout,this);
38259 this.el.on('show', this.layout,this);
38265 * Returns true if this layout is currently being updated
38266 * @return {Boolean}
38268 isUpdating : function(){
38269 return this.updating;
38273 * Suspend the LayoutManager from doing auto-layouts while
38274 * making multiple add or remove calls
38276 beginUpdate : function(){
38277 this.updating = true;
38281 * Restore auto-layouts and optionally disable the manager from performing a layout
38282 * @param {Boolean} noLayout true to disable a layout update
38284 endUpdate : function(noLayout){
38285 this.updating = false;
38291 layout: function(){
38295 onRegionResized : function(region, newSize){
38296 this.fireEvent("regionresized", region, newSize);
38300 onRegionCollapsed : function(region){
38301 this.fireEvent("regioncollapsed", region);
38304 onRegionExpanded : function(region){
38305 this.fireEvent("regionexpanded", region);
38309 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38310 * performs box-model adjustments.
38311 * @return {Object} The size as an object {width: (the width), height: (the height)}
38313 getViewSize : function()
38316 if(this.el.dom != document.body){
38317 size = this.el.getSize();
38319 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38321 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38322 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38327 * Returns the Element this layout is bound to.
38328 * @return {Roo.Element}
38330 getEl : function(){
38335 * Returns the specified region.
38336 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38337 * @return {Roo.LayoutRegion}
38339 getRegion : function(target){
38340 return this.regions[target.toLowerCase()];
38343 onWindowResize : function(){
38344 if(this.monitorWindowResize){
38351 * Ext JS Library 1.1.1
38352 * Copyright(c) 2006-2007, Ext JS, LLC.
38354 * Originally Released Under LGPL - original licence link has changed is not relivant.
38357 * <script type="text/javascript">
38360 * @class Roo.bootstrap.layout.Border
38361 * @extends Roo.bootstrap.layout.Manager
38362 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38363 * please see: examples/bootstrap/nested.html<br><br>
38365 <b>The container the layout is rendered into can be either the body element or any other element.
38366 If it is not the body element, the container needs to either be an absolute positioned element,
38367 or you will need to add "position:relative" to the css of the container. You will also need to specify
38368 the container size if it is not the body element.</b>
38371 * Create a new Border
38372 * @param {Object} config Configuration options
38374 Roo.bootstrap.layout.Border = function(config){
38375 config = config || {};
38376 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38380 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38381 if(config[region]){
38382 config[region].region = region;
38383 this.addRegion(config[region]);
38389 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38391 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38393 parent : false, // this might point to a 'nest' or a ???
38396 * Creates and adds a new region if it doesn't already exist.
38397 * @param {String} target The target region key (north, south, east, west or center).
38398 * @param {Object} config The regions config object
38399 * @return {BorderLayoutRegion} The new region
38401 addRegion : function(config)
38403 if(!this.regions[config.region]){
38404 var r = this.factory(config);
38405 this.bindRegion(r);
38407 return this.regions[config.region];
38411 bindRegion : function(r){
38412 this.regions[r.config.region] = r;
38414 r.on("visibilitychange", this.layout, this);
38415 r.on("paneladded", this.layout, this);
38416 r.on("panelremoved", this.layout, this);
38417 r.on("invalidated", this.layout, this);
38418 r.on("resized", this.onRegionResized, this);
38419 r.on("collapsed", this.onRegionCollapsed, this);
38420 r.on("expanded", this.onRegionExpanded, this);
38424 * Performs a layout update.
38426 layout : function()
38428 if(this.updating) {
38432 // render all the rebions if they have not been done alreayd?
38433 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38434 if(this.regions[region] && !this.regions[region].bodyEl){
38435 this.regions[region].onRender(this.el)
38439 var size = this.getViewSize();
38440 var w = size.width;
38441 var h = size.height;
38446 //var x = 0, y = 0;
38448 var rs = this.regions;
38449 var north = rs["north"];
38450 var south = rs["south"];
38451 var west = rs["west"];
38452 var east = rs["east"];
38453 var center = rs["center"];
38454 //if(this.hideOnLayout){ // not supported anymore
38455 //c.el.setStyle("display", "none");
38457 if(north && north.isVisible()){
38458 var b = north.getBox();
38459 var m = north.getMargins();
38460 b.width = w - (m.left+m.right);
38463 centerY = b.height + b.y + m.bottom;
38464 centerH -= centerY;
38465 north.updateBox(this.safeBox(b));
38467 if(south && south.isVisible()){
38468 var b = south.getBox();
38469 var m = south.getMargins();
38470 b.width = w - (m.left+m.right);
38472 var totalHeight = (b.height + m.top + m.bottom);
38473 b.y = h - totalHeight + m.top;
38474 centerH -= totalHeight;
38475 south.updateBox(this.safeBox(b));
38477 if(west && west.isVisible()){
38478 var b = west.getBox();
38479 var m = west.getMargins();
38480 b.height = centerH - (m.top+m.bottom);
38482 b.y = centerY + m.top;
38483 var totalWidth = (b.width + m.left + m.right);
38484 centerX += totalWidth;
38485 centerW -= totalWidth;
38486 west.updateBox(this.safeBox(b));
38488 if(east && east.isVisible()){
38489 var b = east.getBox();
38490 var m = east.getMargins();
38491 b.height = centerH - (m.top+m.bottom);
38492 var totalWidth = (b.width + m.left + m.right);
38493 b.x = w - totalWidth + m.left;
38494 b.y = centerY + m.top;
38495 centerW -= totalWidth;
38496 east.updateBox(this.safeBox(b));
38499 var m = center.getMargins();
38501 x: centerX + m.left,
38502 y: centerY + m.top,
38503 width: centerW - (m.left+m.right),
38504 height: centerH - (m.top+m.bottom)
38506 //if(this.hideOnLayout){
38507 //center.el.setStyle("display", "block");
38509 center.updateBox(this.safeBox(centerBox));
38512 this.fireEvent("layout", this);
38516 safeBox : function(box){
38517 box.width = Math.max(0, box.width);
38518 box.height = Math.max(0, box.height);
38523 * Adds a ContentPanel (or subclass) to this layout.
38524 * @param {String} target The target region key (north, south, east, west or center).
38525 * @param {Roo.ContentPanel} panel The panel to add
38526 * @return {Roo.ContentPanel} The added panel
38528 add : function(target, panel){
38530 target = target.toLowerCase();
38531 return this.regions[target].add(panel);
38535 * Remove a ContentPanel (or subclass) to this layout.
38536 * @param {String} target The target region key (north, south, east, west or center).
38537 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38538 * @return {Roo.ContentPanel} The removed panel
38540 remove : function(target, panel){
38541 target = target.toLowerCase();
38542 return this.regions[target].remove(panel);
38546 * Searches all regions for a panel with the specified id
38547 * @param {String} panelId
38548 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38550 findPanel : function(panelId){
38551 var rs = this.regions;
38552 for(var target in rs){
38553 if(typeof rs[target] != "function"){
38554 var p = rs[target].getPanel(panelId);
38564 * Searches all regions for a panel with the specified id and activates (shows) it.
38565 * @param {String/ContentPanel} panelId The panels id or the panel itself
38566 * @return {Roo.ContentPanel} The shown panel or null
38568 showPanel : function(panelId) {
38569 var rs = this.regions;
38570 for(var target in rs){
38571 var r = rs[target];
38572 if(typeof r != "function"){
38573 if(r.hasPanel(panelId)){
38574 return r.showPanel(panelId);
38582 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38583 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38586 restoreState : function(provider){
38588 provider = Roo.state.Manager;
38590 var sm = new Roo.LayoutStateManager();
38591 sm.init(this, provider);
38597 * Adds a xtype elements to the layout.
38601 xtype : 'ContentPanel',
38608 xtype : 'NestedLayoutPanel',
38614 items : [ ... list of content panels or nested layout panels.. ]
38618 * @param {Object} cfg Xtype definition of item to add.
38620 addxtype : function(cfg)
38622 // basically accepts a pannel...
38623 // can accept a layout region..!?!?
38624 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38627 // theory? children can only be panels??
38629 //if (!cfg.xtype.match(/Panel$/)) {
38634 if (typeof(cfg.region) == 'undefined') {
38635 Roo.log("Failed to add Panel, region was not set");
38639 var region = cfg.region;
38645 xitems = cfg.items;
38650 if ( region == 'center') {
38651 Roo.log("Center: " + cfg.title);
38657 case 'Content': // ContentPanel (el, cfg)
38658 case 'Scroll': // ContentPanel (el, cfg)
38660 cfg.autoCreate = cfg.autoCreate || true;
38661 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38663 // var el = this.el.createChild();
38664 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38667 this.add(region, ret);
38671 case 'TreePanel': // our new panel!
38672 cfg.el = this.el.createChild();
38673 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38674 this.add(region, ret);
38679 // create a new Layout (which is a Border Layout...
38681 var clayout = cfg.layout;
38682 clayout.el = this.el.createChild();
38683 clayout.items = clayout.items || [];
38687 // replace this exitems with the clayout ones..
38688 xitems = clayout.items;
38690 // force background off if it's in center...
38691 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38692 cfg.background = false;
38694 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38697 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38698 //console.log('adding nested layout panel ' + cfg.toSource());
38699 this.add(region, ret);
38700 nb = {}; /// find first...
38705 // needs grid and region
38707 //var el = this.getRegion(region).el.createChild();
38709 *var el = this.el.createChild();
38710 // create the grid first...
38711 cfg.grid.container = el;
38712 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38715 if (region == 'center' && this.active ) {
38716 cfg.background = false;
38719 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38721 this.add(region, ret);
38723 if (cfg.background) {
38724 // render grid on panel activation (if panel background)
38725 ret.on('activate', function(gp) {
38726 if (!gp.grid.rendered) {
38727 // gp.grid.render(el);
38731 // cfg.grid.render(el);
38737 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38738 // it was the old xcomponent building that caused this before.
38739 // espeically if border is the top element in the tree.
38749 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38751 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38752 this.add(region, ret);
38756 throw "Can not add '" + cfg.xtype + "' to Border";
38762 this.beginUpdate();
38766 Roo.each(xitems, function(i) {
38767 region = nb && i.region ? i.region : false;
38769 var add = ret.addxtype(i);
38772 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38773 if (!i.background) {
38774 abn[region] = nb[region] ;
38781 // make the last non-background panel active..
38782 //if (nb) { Roo.log(abn); }
38785 for(var r in abn) {
38786 region = this.getRegion(r);
38788 // tried using nb[r], but it does not work..
38790 region.showPanel(abn[r]);
38801 factory : function(cfg)
38804 var validRegions = Roo.bootstrap.layout.Border.regions;
38806 var target = cfg.region;
38809 var r = Roo.bootstrap.layout;
38813 return new r.North(cfg);
38815 return new r.South(cfg);
38817 return new r.East(cfg);
38819 return new r.West(cfg);
38821 return new r.Center(cfg);
38823 throw 'Layout region "'+target+'" not supported.';
38830 * Ext JS Library 1.1.1
38831 * Copyright(c) 2006-2007, Ext JS, LLC.
38833 * Originally Released Under LGPL - original licence link has changed is not relivant.
38836 * <script type="text/javascript">
38840 * @class Roo.bootstrap.layout.Basic
38841 * @extends Roo.util.Observable
38842 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38843 * and does not have a titlebar, tabs or any other features. All it does is size and position
38844 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38845 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38846 * @cfg {string} region the region that it inhabits..
38847 * @cfg {bool} skipConfig skip config?
38851 Roo.bootstrap.layout.Basic = function(config){
38853 this.mgr = config.mgr;
38855 this.position = config.region;
38857 var skipConfig = config.skipConfig;
38861 * @scope Roo.BasicLayoutRegion
38865 * @event beforeremove
38866 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38867 * @param {Roo.LayoutRegion} this
38868 * @param {Roo.ContentPanel} panel The panel
38869 * @param {Object} e The cancel event object
38871 "beforeremove" : true,
38873 * @event invalidated
38874 * Fires when the layout for this region is changed.
38875 * @param {Roo.LayoutRegion} this
38877 "invalidated" : true,
38879 * @event visibilitychange
38880 * Fires when this region is shown or hidden
38881 * @param {Roo.LayoutRegion} this
38882 * @param {Boolean} visibility true or false
38884 "visibilitychange" : true,
38886 * @event paneladded
38887 * Fires when a panel is added.
38888 * @param {Roo.LayoutRegion} this
38889 * @param {Roo.ContentPanel} panel The panel
38891 "paneladded" : true,
38893 * @event panelremoved
38894 * Fires when a panel is removed.
38895 * @param {Roo.LayoutRegion} this
38896 * @param {Roo.ContentPanel} panel The panel
38898 "panelremoved" : true,
38900 * @event beforecollapse
38901 * Fires when this region before collapse.
38902 * @param {Roo.LayoutRegion} this
38904 "beforecollapse" : true,
38907 * Fires when this region is collapsed.
38908 * @param {Roo.LayoutRegion} this
38910 "collapsed" : true,
38913 * Fires when this region is expanded.
38914 * @param {Roo.LayoutRegion} this
38919 * Fires when this region is slid into view.
38920 * @param {Roo.LayoutRegion} this
38922 "slideshow" : true,
38925 * Fires when this region slides out of view.
38926 * @param {Roo.LayoutRegion} this
38928 "slidehide" : true,
38930 * @event panelactivated
38931 * Fires when a panel is activated.
38932 * @param {Roo.LayoutRegion} this
38933 * @param {Roo.ContentPanel} panel The activated panel
38935 "panelactivated" : true,
38938 * Fires when the user resizes this region.
38939 * @param {Roo.LayoutRegion} this
38940 * @param {Number} newSize The new size (width for east/west, height for north/south)
38944 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38945 this.panels = new Roo.util.MixedCollection();
38946 this.panels.getKey = this.getPanelId.createDelegate(this);
38948 this.activePanel = null;
38949 // ensure listeners are added...
38951 if (config.listeners || config.events) {
38952 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38953 listeners : config.listeners || {},
38954 events : config.events || {}
38958 if(skipConfig !== true){
38959 this.applyConfig(config);
38963 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38965 getPanelId : function(p){
38969 applyConfig : function(config){
38970 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38971 this.config = config;
38976 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38977 * the width, for horizontal (north, south) the height.
38978 * @param {Number} newSize The new width or height
38980 resizeTo : function(newSize){
38981 var el = this.el ? this.el :
38982 (this.activePanel ? this.activePanel.getEl() : null);
38984 switch(this.position){
38987 el.setWidth(newSize);
38988 this.fireEvent("resized", this, newSize);
38992 el.setHeight(newSize);
38993 this.fireEvent("resized", this, newSize);
38999 getBox : function(){
39000 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39003 getMargins : function(){
39004 return this.margins;
39007 updateBox : function(box){
39009 var el = this.activePanel.getEl();
39010 el.dom.style.left = box.x + "px";
39011 el.dom.style.top = box.y + "px";
39012 this.activePanel.setSize(box.width, box.height);
39016 * Returns the container element for this region.
39017 * @return {Roo.Element}
39019 getEl : function(){
39020 return this.activePanel;
39024 * Returns true if this region is currently visible.
39025 * @return {Boolean}
39027 isVisible : function(){
39028 return this.activePanel ? true : false;
39031 setActivePanel : function(panel){
39032 panel = this.getPanel(panel);
39033 if(this.activePanel && this.activePanel != panel){
39034 this.activePanel.setActiveState(false);
39035 this.activePanel.getEl().setLeftTop(-10000,-10000);
39037 this.activePanel = panel;
39038 panel.setActiveState(true);
39040 panel.setSize(this.box.width, this.box.height);
39042 this.fireEvent("panelactivated", this, panel);
39043 this.fireEvent("invalidated");
39047 * Show the specified panel.
39048 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39049 * @return {Roo.ContentPanel} The shown panel or null
39051 showPanel : function(panel){
39052 panel = this.getPanel(panel);
39054 this.setActivePanel(panel);
39060 * Get the active panel for this region.
39061 * @return {Roo.ContentPanel} The active panel or null
39063 getActivePanel : function(){
39064 return this.activePanel;
39068 * Add the passed ContentPanel(s)
39069 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39070 * @return {Roo.ContentPanel} The panel added (if only one was added)
39072 add : function(panel){
39073 if(arguments.length > 1){
39074 for(var i = 0, len = arguments.length; i < len; i++) {
39075 this.add(arguments[i]);
39079 if(this.hasPanel(panel)){
39080 this.showPanel(panel);
39083 var el = panel.getEl();
39084 if(el.dom.parentNode != this.mgr.el.dom){
39085 this.mgr.el.dom.appendChild(el.dom);
39087 if(panel.setRegion){
39088 panel.setRegion(this);
39090 this.panels.add(panel);
39091 el.setStyle("position", "absolute");
39092 if(!panel.background){
39093 this.setActivePanel(panel);
39094 if(this.config.initialSize && this.panels.getCount()==1){
39095 this.resizeTo(this.config.initialSize);
39098 this.fireEvent("paneladded", this, panel);
39103 * Returns true if the panel is in this region.
39104 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39105 * @return {Boolean}
39107 hasPanel : function(panel){
39108 if(typeof panel == "object"){ // must be panel obj
39109 panel = panel.getId();
39111 return this.getPanel(panel) ? true : false;
39115 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39116 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39117 * @param {Boolean} preservePanel Overrides the config preservePanel option
39118 * @return {Roo.ContentPanel} The panel that was removed
39120 remove : function(panel, preservePanel){
39121 panel = this.getPanel(panel);
39126 this.fireEvent("beforeremove", this, panel, e);
39127 if(e.cancel === true){
39130 var panelId = panel.getId();
39131 this.panels.removeKey(panelId);
39136 * Returns the panel specified or null if it's not in this region.
39137 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39138 * @return {Roo.ContentPanel}
39140 getPanel : function(id){
39141 if(typeof id == "object"){ // must be panel obj
39144 return this.panels.get(id);
39148 * Returns this regions position (north/south/east/west/center).
39151 getPosition: function(){
39152 return this.position;
39156 * Ext JS Library 1.1.1
39157 * Copyright(c) 2006-2007, Ext JS, LLC.
39159 * Originally Released Under LGPL - original licence link has changed is not relivant.
39162 * <script type="text/javascript">
39166 * @class Roo.bootstrap.layout.Region
39167 * @extends Roo.bootstrap.layout.Basic
39168 * This class represents a region in a layout manager.
39170 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39171 * @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})
39172 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39173 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39174 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39175 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39176 * @cfg {String} title The title for the region (overrides panel titles)
39177 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39178 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39179 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39180 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39181 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39182 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39183 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39184 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39185 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39186 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39188 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39189 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39190 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39191 * @cfg {Number} width For East/West panels
39192 * @cfg {Number} height For North/South panels
39193 * @cfg {Boolean} split To show the splitter
39194 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39196 * @cfg {string} cls Extra CSS classes to add to region
39198 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39199 * @cfg {string} region the region that it inhabits..
39202 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39203 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39205 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39206 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39207 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39209 Roo.bootstrap.layout.Region = function(config)
39211 this.applyConfig(config);
39213 var mgr = config.mgr;
39214 var pos = config.region;
39215 config.skipConfig = true;
39216 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39219 this.onRender(mgr.el);
39222 this.visible = true;
39223 this.collapsed = false;
39224 this.unrendered_panels = [];
39227 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39229 position: '', // set by wrapper (eg. north/south etc..)
39230 unrendered_panels : null, // unrendered panels.
39232 tabPosition : false,
39234 mgr: false, // points to 'Border'
39237 createBody : function(){
39238 /** This region's body element
39239 * @type Roo.Element */
39240 this.bodyEl = this.el.createChild({
39242 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39246 onRender: function(ctr, pos)
39248 var dh = Roo.DomHelper;
39249 /** This region's container element
39250 * @type Roo.Element */
39251 this.el = dh.append(ctr.dom, {
39253 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39255 /** This region's title element
39256 * @type Roo.Element */
39258 this.titleEl = dh.append(this.el.dom, {
39260 unselectable: "on",
39261 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39263 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39264 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39268 this.titleEl.enableDisplayMode();
39269 /** This region's title text element
39270 * @type HTMLElement */
39271 this.titleTextEl = this.titleEl.dom.firstChild;
39272 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39274 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39275 this.closeBtn.enableDisplayMode();
39276 this.closeBtn.on("click", this.closeClicked, this);
39277 this.closeBtn.hide();
39279 this.createBody(this.config);
39280 if(this.config.hideWhenEmpty){
39282 this.on("paneladded", this.validateVisibility, this);
39283 this.on("panelremoved", this.validateVisibility, this);
39285 if(this.autoScroll){
39286 this.bodyEl.setStyle("overflow", "auto");
39288 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39290 //if(c.titlebar !== false){
39291 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39292 this.titleEl.hide();
39294 this.titleEl.show();
39295 if(this.config.title){
39296 this.titleTextEl.innerHTML = this.config.title;
39300 if(this.config.collapsed){
39301 this.collapse(true);
39303 if(this.config.hidden){
39307 if (this.unrendered_panels && this.unrendered_panels.length) {
39308 for (var i =0;i< this.unrendered_panels.length; i++) {
39309 this.add(this.unrendered_panels[i]);
39311 this.unrendered_panels = null;
39317 applyConfig : function(c)
39320 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39321 var dh = Roo.DomHelper;
39322 if(c.titlebar !== false){
39323 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39324 this.collapseBtn.on("click", this.collapse, this);
39325 this.collapseBtn.enableDisplayMode();
39327 if(c.showPin === true || this.showPin){
39328 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39329 this.stickBtn.enableDisplayMode();
39330 this.stickBtn.on("click", this.expand, this);
39331 this.stickBtn.hide();
39336 /** This region's collapsed element
39337 * @type Roo.Element */
39340 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39341 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39344 if(c.floatable !== false){
39345 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39346 this.collapsedEl.on("click", this.collapseClick, this);
39349 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39350 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39351 id: "message", unselectable: "on", style:{"float":"left"}});
39352 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39354 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39355 this.expandBtn.on("click", this.expand, this);
39359 if(this.collapseBtn){
39360 this.collapseBtn.setVisible(c.collapsible == true);
39363 this.cmargins = c.cmargins || this.cmargins ||
39364 (this.position == "west" || this.position == "east" ?
39365 {top: 0, left: 2, right:2, bottom: 0} :
39366 {top: 2, left: 0, right:0, bottom: 2});
39368 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39371 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39373 this.autoScroll = c.autoScroll || false;
39378 this.duration = c.duration || .30;
39379 this.slideDuration = c.slideDuration || .45;
39384 * Returns true if this region is currently visible.
39385 * @return {Boolean}
39387 isVisible : function(){
39388 return this.visible;
39392 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39393 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39395 //setCollapsedTitle : function(title){
39396 // title = title || " ";
39397 // if(this.collapsedTitleTextEl){
39398 // this.collapsedTitleTextEl.innerHTML = title;
39402 getBox : function(){
39404 // if(!this.collapsed){
39405 b = this.el.getBox(false, true);
39407 // b = this.collapsedEl.getBox(false, true);
39412 getMargins : function(){
39413 return this.margins;
39414 //return this.collapsed ? this.cmargins : this.margins;
39417 highlight : function(){
39418 this.el.addClass("x-layout-panel-dragover");
39421 unhighlight : function(){
39422 this.el.removeClass("x-layout-panel-dragover");
39425 updateBox : function(box)
39427 if (!this.bodyEl) {
39428 return; // not rendered yet..
39432 if(!this.collapsed){
39433 this.el.dom.style.left = box.x + "px";
39434 this.el.dom.style.top = box.y + "px";
39435 this.updateBody(box.width, box.height);
39437 this.collapsedEl.dom.style.left = box.x + "px";
39438 this.collapsedEl.dom.style.top = box.y + "px";
39439 this.collapsedEl.setSize(box.width, box.height);
39442 this.tabs.autoSizeTabs();
39446 updateBody : function(w, h)
39449 this.el.setWidth(w);
39450 w -= this.el.getBorderWidth("rl");
39451 if(this.config.adjustments){
39452 w += this.config.adjustments[0];
39455 if(h !== null && h > 0){
39456 this.el.setHeight(h);
39457 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39458 h -= this.el.getBorderWidth("tb");
39459 if(this.config.adjustments){
39460 h += this.config.adjustments[1];
39462 this.bodyEl.setHeight(h);
39464 h = this.tabs.syncHeight(h);
39467 if(this.panelSize){
39468 w = w !== null ? w : this.panelSize.width;
39469 h = h !== null ? h : this.panelSize.height;
39471 if(this.activePanel){
39472 var el = this.activePanel.getEl();
39473 w = w !== null ? w : el.getWidth();
39474 h = h !== null ? h : el.getHeight();
39475 this.panelSize = {width: w, height: h};
39476 this.activePanel.setSize(w, h);
39478 if(Roo.isIE && this.tabs){
39479 this.tabs.el.repaint();
39484 * Returns the container element for this region.
39485 * @return {Roo.Element}
39487 getEl : function(){
39492 * Hides this region.
39495 //if(!this.collapsed){
39496 this.el.dom.style.left = "-2000px";
39499 // this.collapsedEl.dom.style.left = "-2000px";
39500 // this.collapsedEl.hide();
39502 this.visible = false;
39503 this.fireEvent("visibilitychange", this, false);
39507 * Shows this region if it was previously hidden.
39510 //if(!this.collapsed){
39513 // this.collapsedEl.show();
39515 this.visible = true;
39516 this.fireEvent("visibilitychange", this, true);
39519 closeClicked : function(){
39520 if(this.activePanel){
39521 this.remove(this.activePanel);
39525 collapseClick : function(e){
39527 e.stopPropagation();
39530 e.stopPropagation();
39536 * Collapses this region.
39537 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39540 collapse : function(skipAnim, skipCheck = false){
39541 if(this.collapsed) {
39545 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39547 this.collapsed = true;
39549 this.split.el.hide();
39551 if(this.config.animate && skipAnim !== true){
39552 this.fireEvent("invalidated", this);
39553 this.animateCollapse();
39555 this.el.setLocation(-20000,-20000);
39557 this.collapsedEl.show();
39558 this.fireEvent("collapsed", this);
39559 this.fireEvent("invalidated", this);
39565 animateCollapse : function(){
39570 * Expands this region if it was previously collapsed.
39571 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39572 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39575 expand : function(e, skipAnim){
39577 e.stopPropagation();
39579 if(!this.collapsed || this.el.hasActiveFx()) {
39583 this.afterSlideIn();
39586 this.collapsed = false;
39587 if(this.config.animate && skipAnim !== true){
39588 this.animateExpand();
39592 this.split.el.show();
39594 this.collapsedEl.setLocation(-2000,-2000);
39595 this.collapsedEl.hide();
39596 this.fireEvent("invalidated", this);
39597 this.fireEvent("expanded", this);
39601 animateExpand : function(){
39605 initTabs : function()
39607 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39609 var ts = new Roo.bootstrap.panel.Tabs({
39610 el: this.bodyEl.dom,
39612 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39613 disableTooltips: this.config.disableTabTips,
39614 toolbar : this.config.toolbar
39617 if(this.config.hideTabs){
39618 ts.stripWrap.setDisplayed(false);
39621 ts.resizeTabs = this.config.resizeTabs === true;
39622 ts.minTabWidth = this.config.minTabWidth || 40;
39623 ts.maxTabWidth = this.config.maxTabWidth || 250;
39624 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39625 ts.monitorResize = false;
39626 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39627 ts.bodyEl.addClass('roo-layout-tabs-body');
39628 this.panels.each(this.initPanelAsTab, this);
39631 initPanelAsTab : function(panel){
39632 var ti = this.tabs.addTab(
39636 this.config.closeOnTab && panel.isClosable(),
39639 if(panel.tabTip !== undefined){
39640 ti.setTooltip(panel.tabTip);
39642 ti.on("activate", function(){
39643 this.setActivePanel(panel);
39646 if(this.config.closeOnTab){
39647 ti.on("beforeclose", function(t, e){
39649 this.remove(panel);
39653 panel.tabItem = ti;
39658 updatePanelTitle : function(panel, title)
39660 if(this.activePanel == panel){
39661 this.updateTitle(title);
39664 var ti = this.tabs.getTab(panel.getEl().id);
39666 if(panel.tabTip !== undefined){
39667 ti.setTooltip(panel.tabTip);
39672 updateTitle : function(title){
39673 if(this.titleTextEl && !this.config.title){
39674 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39678 setActivePanel : function(panel)
39680 panel = this.getPanel(panel);
39681 if(this.activePanel && this.activePanel != panel){
39682 if(this.activePanel.setActiveState(false) === false){
39686 this.activePanel = panel;
39687 panel.setActiveState(true);
39688 if(this.panelSize){
39689 panel.setSize(this.panelSize.width, this.panelSize.height);
39692 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39694 this.updateTitle(panel.getTitle());
39696 this.fireEvent("invalidated", this);
39698 this.fireEvent("panelactivated", this, panel);
39702 * Shows the specified panel.
39703 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39704 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39706 showPanel : function(panel)
39708 panel = this.getPanel(panel);
39711 var tab = this.tabs.getTab(panel.getEl().id);
39712 if(tab.isHidden()){
39713 this.tabs.unhideTab(tab.id);
39717 this.setActivePanel(panel);
39724 * Get the active panel for this region.
39725 * @return {Roo.ContentPanel} The active panel or null
39727 getActivePanel : function(){
39728 return this.activePanel;
39731 validateVisibility : function(){
39732 if(this.panels.getCount() < 1){
39733 this.updateTitle(" ");
39734 this.closeBtn.hide();
39737 if(!this.isVisible()){
39744 * Adds the passed ContentPanel(s) to this region.
39745 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39746 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39748 add : function(panel)
39750 if(arguments.length > 1){
39751 for(var i = 0, len = arguments.length; i < len; i++) {
39752 this.add(arguments[i]);
39757 // if we have not been rendered yet, then we can not really do much of this..
39758 if (!this.bodyEl) {
39759 this.unrendered_panels.push(panel);
39766 if(this.hasPanel(panel)){
39767 this.showPanel(panel);
39770 panel.setRegion(this);
39771 this.panels.add(panel);
39772 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39773 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39774 // and hide them... ???
39775 this.bodyEl.dom.appendChild(panel.getEl().dom);
39776 if(panel.background !== true){
39777 this.setActivePanel(panel);
39779 this.fireEvent("paneladded", this, panel);
39786 this.initPanelAsTab(panel);
39790 if(panel.background !== true){
39791 this.tabs.activate(panel.getEl().id);
39793 this.fireEvent("paneladded", this, panel);
39798 * Hides the tab for the specified panel.
39799 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39801 hidePanel : function(panel){
39802 if(this.tabs && (panel = this.getPanel(panel))){
39803 this.tabs.hideTab(panel.getEl().id);
39808 * Unhides the tab for a previously hidden panel.
39809 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39811 unhidePanel : function(panel){
39812 if(this.tabs && (panel = this.getPanel(panel))){
39813 this.tabs.unhideTab(panel.getEl().id);
39817 clearPanels : function(){
39818 while(this.panels.getCount() > 0){
39819 this.remove(this.panels.first());
39824 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39825 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39826 * @param {Boolean} preservePanel Overrides the config preservePanel option
39827 * @return {Roo.ContentPanel} The panel that was removed
39829 remove : function(panel, preservePanel)
39831 panel = this.getPanel(panel);
39836 this.fireEvent("beforeremove", this, panel, e);
39837 if(e.cancel === true){
39840 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39841 var panelId = panel.getId();
39842 this.panels.removeKey(panelId);
39844 document.body.appendChild(panel.getEl().dom);
39847 this.tabs.removeTab(panel.getEl().id);
39848 }else if (!preservePanel){
39849 this.bodyEl.dom.removeChild(panel.getEl().dom);
39851 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39852 var p = this.panels.first();
39853 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39854 tempEl.appendChild(p.getEl().dom);
39855 this.bodyEl.update("");
39856 this.bodyEl.dom.appendChild(p.getEl().dom);
39858 this.updateTitle(p.getTitle());
39860 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39861 this.setActivePanel(p);
39863 panel.setRegion(null);
39864 if(this.activePanel == panel){
39865 this.activePanel = null;
39867 if(this.config.autoDestroy !== false && preservePanel !== true){
39868 try{panel.destroy();}catch(e){}
39870 this.fireEvent("panelremoved", this, panel);
39875 * Returns the TabPanel component used by this region
39876 * @return {Roo.TabPanel}
39878 getTabs : function(){
39882 createTool : function(parentEl, className){
39883 var btn = Roo.DomHelper.append(parentEl, {
39885 cls: "x-layout-tools-button",
39888 cls: "roo-layout-tools-button-inner " + className,
39892 btn.addClassOnOver("roo-layout-tools-button-over");
39897 * Ext JS Library 1.1.1
39898 * Copyright(c) 2006-2007, Ext JS, LLC.
39900 * Originally Released Under LGPL - original licence link has changed is not relivant.
39903 * <script type="text/javascript">
39909 * @class Roo.SplitLayoutRegion
39910 * @extends Roo.LayoutRegion
39911 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39913 Roo.bootstrap.layout.Split = function(config){
39914 this.cursor = config.cursor;
39915 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39918 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39920 splitTip : "Drag to resize.",
39921 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39922 useSplitTips : false,
39924 applyConfig : function(config){
39925 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39928 onRender : function(ctr,pos) {
39930 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39931 if(!this.config.split){
39936 var splitEl = Roo.DomHelper.append(ctr.dom, {
39938 id: this.el.id + "-split",
39939 cls: "roo-layout-split roo-layout-split-"+this.position,
39942 /** The SplitBar for this region
39943 * @type Roo.SplitBar */
39944 // does not exist yet...
39945 Roo.log([this.position, this.orientation]);
39947 this.split = new Roo.bootstrap.SplitBar({
39948 dragElement : splitEl,
39949 resizingElement: this.el,
39950 orientation : this.orientation
39953 this.split.on("moved", this.onSplitMove, this);
39954 this.split.useShim = this.config.useShim === true;
39955 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39956 if(this.useSplitTips){
39957 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39959 //if(config.collapsible){
39960 // this.split.el.on("dblclick", this.collapse, this);
39963 if(typeof this.config.minSize != "undefined"){
39964 this.split.minSize = this.config.minSize;
39966 if(typeof this.config.maxSize != "undefined"){
39967 this.split.maxSize = this.config.maxSize;
39969 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39970 this.hideSplitter();
39975 getHMaxSize : function(){
39976 var cmax = this.config.maxSize || 10000;
39977 var center = this.mgr.getRegion("center");
39978 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39981 getVMaxSize : function(){
39982 var cmax = this.config.maxSize || 10000;
39983 var center = this.mgr.getRegion("center");
39984 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39987 onSplitMove : function(split, newSize){
39988 this.fireEvent("resized", this, newSize);
39992 * Returns the {@link Roo.SplitBar} for this region.
39993 * @return {Roo.SplitBar}
39995 getSplitBar : function(){
40000 this.hideSplitter();
40001 Roo.bootstrap.layout.Split.superclass.hide.call(this);
40004 hideSplitter : function(){
40006 this.split.el.setLocation(-2000,-2000);
40007 this.split.el.hide();
40013 this.split.el.show();
40015 Roo.bootstrap.layout.Split.superclass.show.call(this);
40018 beforeSlide: function(){
40019 if(Roo.isGecko){// firefox overflow auto bug workaround
40020 this.bodyEl.clip();
40022 this.tabs.bodyEl.clip();
40024 if(this.activePanel){
40025 this.activePanel.getEl().clip();
40027 if(this.activePanel.beforeSlide){
40028 this.activePanel.beforeSlide();
40034 afterSlide : function(){
40035 if(Roo.isGecko){// firefox overflow auto bug workaround
40036 this.bodyEl.unclip();
40038 this.tabs.bodyEl.unclip();
40040 if(this.activePanel){
40041 this.activePanel.getEl().unclip();
40042 if(this.activePanel.afterSlide){
40043 this.activePanel.afterSlide();
40049 initAutoHide : function(){
40050 if(this.autoHide !== false){
40051 if(!this.autoHideHd){
40052 var st = new Roo.util.DelayedTask(this.slideIn, this);
40053 this.autoHideHd = {
40054 "mouseout": function(e){
40055 if(!e.within(this.el, true)){
40059 "mouseover" : function(e){
40065 this.el.on(this.autoHideHd);
40069 clearAutoHide : function(){
40070 if(this.autoHide !== false){
40071 this.el.un("mouseout", this.autoHideHd.mouseout);
40072 this.el.un("mouseover", this.autoHideHd.mouseover);
40076 clearMonitor : function(){
40077 Roo.get(document).un("click", this.slideInIf, this);
40080 // these names are backwards but not changed for compat
40081 slideOut : function(){
40082 if(this.isSlid || this.el.hasActiveFx()){
40085 this.isSlid = true;
40086 if(this.collapseBtn){
40087 this.collapseBtn.hide();
40089 this.closeBtnState = this.closeBtn.getStyle('display');
40090 this.closeBtn.hide();
40092 this.stickBtn.show();
40095 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40096 this.beforeSlide();
40097 this.el.setStyle("z-index", 10001);
40098 this.el.slideIn(this.getSlideAnchor(), {
40099 callback: function(){
40101 this.initAutoHide();
40102 Roo.get(document).on("click", this.slideInIf, this);
40103 this.fireEvent("slideshow", this);
40110 afterSlideIn : function(){
40111 this.clearAutoHide();
40112 this.isSlid = false;
40113 this.clearMonitor();
40114 this.el.setStyle("z-index", "");
40115 if(this.collapseBtn){
40116 this.collapseBtn.show();
40118 this.closeBtn.setStyle('display', this.closeBtnState);
40120 this.stickBtn.hide();
40122 this.fireEvent("slidehide", this);
40125 slideIn : function(cb){
40126 if(!this.isSlid || this.el.hasActiveFx()){
40130 this.isSlid = false;
40131 this.beforeSlide();
40132 this.el.slideOut(this.getSlideAnchor(), {
40133 callback: function(){
40134 this.el.setLeftTop(-10000, -10000);
40136 this.afterSlideIn();
40144 slideInIf : function(e){
40145 if(!e.within(this.el)){
40150 animateCollapse : function(){
40151 this.beforeSlide();
40152 this.el.setStyle("z-index", 20000);
40153 var anchor = this.getSlideAnchor();
40154 this.el.slideOut(anchor, {
40155 callback : function(){
40156 this.el.setStyle("z-index", "");
40157 this.collapsedEl.slideIn(anchor, {duration:.3});
40159 this.el.setLocation(-10000,-10000);
40161 this.fireEvent("collapsed", this);
40168 animateExpand : function(){
40169 this.beforeSlide();
40170 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40171 this.el.setStyle("z-index", 20000);
40172 this.collapsedEl.hide({
40175 this.el.slideIn(this.getSlideAnchor(), {
40176 callback : function(){
40177 this.el.setStyle("z-index", "");
40180 this.split.el.show();
40182 this.fireEvent("invalidated", this);
40183 this.fireEvent("expanded", this);
40211 getAnchor : function(){
40212 return this.anchors[this.position];
40215 getCollapseAnchor : function(){
40216 return this.canchors[this.position];
40219 getSlideAnchor : function(){
40220 return this.sanchors[this.position];
40223 getAlignAdj : function(){
40224 var cm = this.cmargins;
40225 switch(this.position){
40241 getExpandAdj : function(){
40242 var c = this.collapsedEl, cm = this.cmargins;
40243 switch(this.position){
40245 return [-(cm.right+c.getWidth()+cm.left), 0];
40248 return [cm.right+c.getWidth()+cm.left, 0];
40251 return [0, -(cm.top+cm.bottom+c.getHeight())];
40254 return [0, cm.top+cm.bottom+c.getHeight()];
40260 * Ext JS Library 1.1.1
40261 * Copyright(c) 2006-2007, Ext JS, LLC.
40263 * Originally Released Under LGPL - original licence link has changed is not relivant.
40266 * <script type="text/javascript">
40269 * These classes are private internal classes
40271 Roo.bootstrap.layout.Center = function(config){
40272 config.region = "center";
40273 Roo.bootstrap.layout.Region.call(this, config);
40274 this.visible = true;
40275 this.minWidth = config.minWidth || 20;
40276 this.minHeight = config.minHeight || 20;
40279 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40281 // center panel can't be hidden
40285 // center panel can't be hidden
40288 getMinWidth: function(){
40289 return this.minWidth;
40292 getMinHeight: function(){
40293 return this.minHeight;
40307 Roo.bootstrap.layout.North = function(config)
40309 config.region = 'north';
40310 config.cursor = 'n-resize';
40312 Roo.bootstrap.layout.Split.call(this, config);
40316 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40317 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40318 this.split.el.addClass("roo-layout-split-v");
40320 //var size = config.initialSize || config.height;
40321 //if(this.el && typeof size != "undefined"){
40322 // this.el.setHeight(size);
40325 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40327 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40330 onRender : function(ctr, pos)
40332 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40333 var size = this.config.initialSize || this.config.height;
40334 if(this.el && typeof size != "undefined"){
40335 this.el.setHeight(size);
40340 getBox : function(){
40341 if(this.collapsed){
40342 return this.collapsedEl.getBox();
40344 var box = this.el.getBox();
40346 box.height += this.split.el.getHeight();
40351 updateBox : function(box){
40352 if(this.split && !this.collapsed){
40353 box.height -= this.split.el.getHeight();
40354 this.split.el.setLeft(box.x);
40355 this.split.el.setTop(box.y+box.height);
40356 this.split.el.setWidth(box.width);
40358 if(this.collapsed){
40359 this.updateBody(box.width, null);
40361 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40369 Roo.bootstrap.layout.South = function(config){
40370 config.region = 'south';
40371 config.cursor = 's-resize';
40372 Roo.bootstrap.layout.Split.call(this, config);
40374 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40375 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40376 this.split.el.addClass("roo-layout-split-v");
40381 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40382 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40384 onRender : function(ctr, pos)
40386 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40387 var size = this.config.initialSize || this.config.height;
40388 if(this.el && typeof size != "undefined"){
40389 this.el.setHeight(size);
40394 getBox : function(){
40395 if(this.collapsed){
40396 return this.collapsedEl.getBox();
40398 var box = this.el.getBox();
40400 var sh = this.split.el.getHeight();
40407 updateBox : function(box){
40408 if(this.split && !this.collapsed){
40409 var sh = this.split.el.getHeight();
40412 this.split.el.setLeft(box.x);
40413 this.split.el.setTop(box.y-sh);
40414 this.split.el.setWidth(box.width);
40416 if(this.collapsed){
40417 this.updateBody(box.width, null);
40419 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40423 Roo.bootstrap.layout.East = function(config){
40424 config.region = "east";
40425 config.cursor = "e-resize";
40426 Roo.bootstrap.layout.Split.call(this, config);
40428 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40429 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40430 this.split.el.addClass("roo-layout-split-h");
40434 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40435 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40437 onRender : function(ctr, pos)
40439 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40440 var size = this.config.initialSize || this.config.width;
40441 if(this.el && typeof size != "undefined"){
40442 this.el.setWidth(size);
40447 getBox : function(){
40448 if(this.collapsed){
40449 return this.collapsedEl.getBox();
40451 var box = this.el.getBox();
40453 var sw = this.split.el.getWidth();
40460 updateBox : function(box){
40461 if(this.split && !this.collapsed){
40462 var sw = this.split.el.getWidth();
40464 this.split.el.setLeft(box.x);
40465 this.split.el.setTop(box.y);
40466 this.split.el.setHeight(box.height);
40469 if(this.collapsed){
40470 this.updateBody(null, box.height);
40472 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40476 Roo.bootstrap.layout.West = function(config){
40477 config.region = "west";
40478 config.cursor = "w-resize";
40480 Roo.bootstrap.layout.Split.call(this, config);
40482 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40483 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40484 this.split.el.addClass("roo-layout-split-h");
40488 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40489 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40491 onRender: function(ctr, pos)
40493 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40494 var size = this.config.initialSize || this.config.width;
40495 if(typeof size != "undefined"){
40496 this.el.setWidth(size);
40500 getBox : function(){
40501 if(this.collapsed){
40502 return this.collapsedEl.getBox();
40504 var box = this.el.getBox();
40505 if (box.width == 0) {
40506 box.width = this.config.width; // kludge?
40509 box.width += this.split.el.getWidth();
40514 updateBox : function(box){
40515 if(this.split && !this.collapsed){
40516 var sw = this.split.el.getWidth();
40518 this.split.el.setLeft(box.x+box.width);
40519 this.split.el.setTop(box.y);
40520 this.split.el.setHeight(box.height);
40522 if(this.collapsed){
40523 this.updateBody(null, box.height);
40525 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40527 });Roo.namespace("Roo.bootstrap.panel");/*
40529 * Ext JS Library 1.1.1
40530 * Copyright(c) 2006-2007, Ext JS, LLC.
40532 * Originally Released Under LGPL - original licence link has changed is not relivant.
40535 * <script type="text/javascript">
40538 * @class Roo.ContentPanel
40539 * @extends Roo.util.Observable
40540 * A basic ContentPanel element.
40541 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40542 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40543 * @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
40544 * @cfg {Boolean} closable True if the panel can be closed/removed
40545 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40546 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40547 * @cfg {Toolbar} toolbar A toolbar for this panel
40548 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40549 * @cfg {String} title The title for this panel
40550 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40551 * @cfg {String} url Calls {@link #setUrl} with this value
40552 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40553 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40554 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40555 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40556 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40557 * @cfg {Boolean} badges render the badges
40558 * @cfg {String} cls extra classes to use
40559 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40562 * Create a new ContentPanel.
40563 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40564 * @param {String/Object} config A string to set only the title or a config object
40565 * @param {String} content (optional) Set the HTML content for this panel
40566 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40568 Roo.bootstrap.panel.Content = function( config){
40570 this.tpl = config.tpl || false;
40572 var el = config.el;
40573 var content = config.content;
40575 if(config.autoCreate){ // xtype is available if this is called from factory
40578 this.el = Roo.get(el);
40579 if(!this.el && config && config.autoCreate){
40580 if(typeof config.autoCreate == "object"){
40581 if(!config.autoCreate.id){
40582 config.autoCreate.id = config.id||el;
40584 this.el = Roo.DomHelper.append(document.body,
40585 config.autoCreate, true);
40589 cls: (config.cls || '') +
40590 (config.background ? ' bg-' + config.background : '') +
40591 " roo-layout-inactive-content",
40594 if (config.iframe) {
40598 style : 'border: 0px',
40599 src : 'about:blank'
40605 elcfg.html = config.html;
40609 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40610 if (config.iframe) {
40611 this.iframeEl = this.el.select('iframe',true).first();
40616 this.closable = false;
40617 this.loaded = false;
40618 this.active = false;
40621 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40623 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40625 this.wrapEl = this.el; //this.el.wrap();
40627 if (config.toolbar.items) {
40628 ti = config.toolbar.items ;
40629 delete config.toolbar.items ;
40633 this.toolbar.render(this.wrapEl, 'before');
40634 for(var i =0;i < ti.length;i++) {
40635 // Roo.log(['add child', items[i]]);
40636 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40638 this.toolbar.items = nitems;
40639 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40640 delete config.toolbar;
40644 // xtype created footer. - not sure if will work as we normally have to render first..
40645 if (this.footer && !this.footer.el && this.footer.xtype) {
40646 if (!this.wrapEl) {
40647 this.wrapEl = this.el.wrap();
40650 this.footer.container = this.wrapEl.createChild();
40652 this.footer = Roo.factory(this.footer, Roo);
40657 if(typeof config == "string"){
40658 this.title = config;
40660 Roo.apply(this, config);
40664 this.resizeEl = Roo.get(this.resizeEl, true);
40666 this.resizeEl = this.el;
40668 // handle view.xtype
40676 * Fires when this panel is activated.
40677 * @param {Roo.ContentPanel} this
40681 * @event deactivate
40682 * Fires when this panel is activated.
40683 * @param {Roo.ContentPanel} this
40685 "deactivate" : true,
40689 * Fires when this panel is resized if fitToFrame is true.
40690 * @param {Roo.ContentPanel} this
40691 * @param {Number} width The width after any component adjustments
40692 * @param {Number} height The height after any component adjustments
40698 * Fires when this tab is created
40699 * @param {Roo.ContentPanel} this
40705 * Fires when this content is scrolled
40706 * @param {Roo.ContentPanel} this
40707 * @param {Event} scrollEvent
40718 if(this.autoScroll && !this.iframe){
40719 this.resizeEl.setStyle("overflow", "auto");
40720 this.resizeEl.on('scroll', this.onScroll, this);
40722 // fix randome scrolling
40723 //this.el.on('scroll', function() {
40724 // Roo.log('fix random scolling');
40725 // this.scrollTo('top',0);
40728 content = content || this.content;
40730 this.setContent(content);
40732 if(config && config.url){
40733 this.setUrl(this.url, this.params, this.loadOnce);
40738 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40740 if (this.view && typeof(this.view.xtype) != 'undefined') {
40741 this.view.el = this.el.appendChild(document.createElement("div"));
40742 this.view = Roo.factory(this.view);
40743 this.view.render && this.view.render(false, '');
40747 this.fireEvent('render', this);
40750 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40760 /* Resize Element - use this to work out scroll etc. */
40763 setRegion : function(region){
40764 this.region = region;
40765 this.setActiveClass(region && !this.background);
40769 setActiveClass: function(state)
40772 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40773 this.el.setStyle('position','relative');
40775 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40776 this.el.setStyle('position', 'absolute');
40781 * Returns the toolbar for this Panel if one was configured.
40782 * @return {Roo.Toolbar}
40784 getToolbar : function(){
40785 return this.toolbar;
40788 setActiveState : function(active)
40790 this.active = active;
40791 this.setActiveClass(active);
40793 if(this.fireEvent("deactivate", this) === false){
40798 this.fireEvent("activate", this);
40802 * Updates this panel's element (not for iframe)
40803 * @param {String} content The new content
40804 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40806 setContent : function(content, loadScripts){
40811 this.el.update(content, loadScripts);
40814 ignoreResize : function(w, h){
40815 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40818 this.lastSize = {width: w, height: h};
40823 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40824 * @return {Roo.UpdateManager} The UpdateManager
40826 getUpdateManager : function(){
40830 return this.el.getUpdateManager();
40833 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40834 * Does not work with IFRAME contents
40835 * @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:
40838 url: "your-url.php",
40839 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40840 callback: yourFunction,
40841 scope: yourObject, //(optional scope)
40844 text: "Loading...",
40850 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40851 * 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.
40852 * @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}
40853 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40854 * @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.
40855 * @return {Roo.ContentPanel} this
40863 var um = this.el.getUpdateManager();
40864 um.update.apply(um, arguments);
40870 * 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.
40871 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40872 * @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)
40873 * @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)
40874 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40876 setUrl : function(url, params, loadOnce){
40878 this.iframeEl.dom.src = url;
40882 if(this.refreshDelegate){
40883 this.removeListener("activate", this.refreshDelegate);
40885 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40886 this.on("activate", this.refreshDelegate);
40887 return this.el.getUpdateManager();
40890 _handleRefresh : function(url, params, loadOnce){
40891 if(!loadOnce || !this.loaded){
40892 var updater = this.el.getUpdateManager();
40893 updater.update(url, params, this._setLoaded.createDelegate(this));
40897 _setLoaded : function(){
40898 this.loaded = true;
40902 * Returns this panel's id
40905 getId : function(){
40910 * Returns this panel's element - used by regiosn to add.
40911 * @return {Roo.Element}
40913 getEl : function(){
40914 return this.wrapEl || this.el;
40919 adjustForComponents : function(width, height)
40921 //Roo.log('adjustForComponents ');
40922 if(this.resizeEl != this.el){
40923 width -= this.el.getFrameWidth('lr');
40924 height -= this.el.getFrameWidth('tb');
40927 var te = this.toolbar.getEl();
40928 te.setWidth(width);
40929 height -= te.getHeight();
40932 var te = this.footer.getEl();
40933 te.setWidth(width);
40934 height -= te.getHeight();
40938 if(this.adjustments){
40939 width += this.adjustments[0];
40940 height += this.adjustments[1];
40942 return {"width": width, "height": height};
40945 setSize : function(width, height){
40946 if(this.fitToFrame && !this.ignoreResize(width, height)){
40947 if(this.fitContainer && this.resizeEl != this.el){
40948 this.el.setSize(width, height);
40950 var size = this.adjustForComponents(width, height);
40952 this.iframeEl.setSize(width,height);
40955 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40956 this.fireEvent('resize', this, size.width, size.height);
40963 * Returns this panel's title
40966 getTitle : function(){
40968 if (typeof(this.title) != 'object') {
40973 for (var k in this.title) {
40974 if (!this.title.hasOwnProperty(k)) {
40978 if (k.indexOf('-') >= 0) {
40979 var s = k.split('-');
40980 for (var i = 0; i<s.length; i++) {
40981 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40984 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40991 * Set this panel's title
40992 * @param {String} title
40994 setTitle : function(title){
40995 this.title = title;
40997 this.region.updatePanelTitle(this, title);
41002 * Returns true is this panel was configured to be closable
41003 * @return {Boolean}
41005 isClosable : function(){
41006 return this.closable;
41009 beforeSlide : function(){
41011 this.resizeEl.clip();
41014 afterSlide : function(){
41016 this.resizeEl.unclip();
41020 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41021 * Will fail silently if the {@link #setUrl} method has not been called.
41022 * This does not activate the panel, just updates its content.
41024 refresh : function(){
41025 if(this.refreshDelegate){
41026 this.loaded = false;
41027 this.refreshDelegate();
41032 * Destroys this panel
41034 destroy : function(){
41035 this.el.removeAllListeners();
41036 var tempEl = document.createElement("span");
41037 tempEl.appendChild(this.el.dom);
41038 tempEl.innerHTML = "";
41044 * form - if the content panel contains a form - this is a reference to it.
41045 * @type {Roo.form.Form}
41049 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41050 * This contains a reference to it.
41056 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41066 * @param {Object} cfg Xtype definition of item to add.
41070 getChildContainer: function () {
41071 return this.getEl();
41075 onScroll : function(e)
41077 this.fireEvent('scroll', this, e);
41082 var ret = new Roo.factory(cfg);
41087 if (cfg.xtype.match(/^Form$/)) {
41090 //if (this.footer) {
41091 // el = this.footer.container.insertSibling(false, 'before');
41093 el = this.el.createChild();
41096 this.form = new Roo.form.Form(cfg);
41099 if ( this.form.allItems.length) {
41100 this.form.render(el.dom);
41104 // should only have one of theses..
41105 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41106 // views.. should not be just added - used named prop 'view''
41108 cfg.el = this.el.appendChild(document.createElement("div"));
41111 var ret = new Roo.factory(cfg);
41113 ret.render && ret.render(false, ''); // render blank..
41123 * @class Roo.bootstrap.panel.Grid
41124 * @extends Roo.bootstrap.panel.Content
41126 * Create a new GridPanel.
41127 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41128 * @param {Object} config A the config object
41134 Roo.bootstrap.panel.Grid = function(config)
41138 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41139 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41141 config.el = this.wrapper;
41142 //this.el = this.wrapper;
41144 if (config.container) {
41145 // ctor'ed from a Border/panel.grid
41148 this.wrapper.setStyle("overflow", "hidden");
41149 this.wrapper.addClass('roo-grid-container');
41154 if(config.toolbar){
41155 var tool_el = this.wrapper.createChild();
41156 this.toolbar = Roo.factory(config.toolbar);
41158 if (config.toolbar.items) {
41159 ti = config.toolbar.items ;
41160 delete config.toolbar.items ;
41164 this.toolbar.render(tool_el);
41165 for(var i =0;i < ti.length;i++) {
41166 // Roo.log(['add child', items[i]]);
41167 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41169 this.toolbar.items = nitems;
41171 delete config.toolbar;
41174 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41175 config.grid.scrollBody = true;;
41176 config.grid.monitorWindowResize = false; // turn off autosizing
41177 config.grid.autoHeight = false;
41178 config.grid.autoWidth = false;
41180 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41182 if (config.background) {
41183 // render grid on panel activation (if panel background)
41184 this.on('activate', function(gp) {
41185 if (!gp.grid.rendered) {
41186 gp.grid.render(this.wrapper);
41187 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41192 this.grid.render(this.wrapper);
41193 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41196 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41197 // ??? needed ??? config.el = this.wrapper;
41202 // xtype created footer. - not sure if will work as we normally have to render first..
41203 if (this.footer && !this.footer.el && this.footer.xtype) {
41205 var ctr = this.grid.getView().getFooterPanel(true);
41206 this.footer.dataSource = this.grid.dataSource;
41207 this.footer = Roo.factory(this.footer, Roo);
41208 this.footer.render(ctr);
41218 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41219 getId : function(){
41220 return this.grid.id;
41224 * Returns the grid for this panel
41225 * @return {Roo.bootstrap.Table}
41227 getGrid : function(){
41231 setSize : function(width, height){
41232 if(!this.ignoreResize(width, height)){
41233 var grid = this.grid;
41234 var size = this.adjustForComponents(width, height);
41235 // tfoot is not a footer?
41238 var gridel = grid.getGridEl();
41239 gridel.setSize(size.width, size.height);
41241 var tbd = grid.getGridEl().select('tbody', true).first();
41242 var thd = grid.getGridEl().select('thead',true).first();
41243 var tbf= grid.getGridEl().select('tfoot', true).first();
41246 size.height -= tbf.getHeight();
41249 size.height -= thd.getHeight();
41252 tbd.setSize(size.width, size.height );
41253 // this is for the account management tab -seems to work there.
41254 var thd = grid.getGridEl().select('thead',true).first();
41256 // tbd.setSize(size.width, size.height - thd.getHeight());
41265 beforeSlide : function(){
41266 this.grid.getView().scroller.clip();
41269 afterSlide : function(){
41270 this.grid.getView().scroller.unclip();
41273 destroy : function(){
41274 this.grid.destroy();
41276 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41281 * @class Roo.bootstrap.panel.Nest
41282 * @extends Roo.bootstrap.panel.Content
41284 * Create a new Panel, that can contain a layout.Border.
41287 * @param {Roo.BorderLayout} layout The layout for this panel
41288 * @param {String/Object} config A string to set only the title or a config object
41290 Roo.bootstrap.panel.Nest = function(config)
41292 // construct with only one argument..
41293 /* FIXME - implement nicer consturctors
41294 if (layout.layout) {
41296 layout = config.layout;
41297 delete config.layout;
41299 if (layout.xtype && !layout.getEl) {
41300 // then layout needs constructing..
41301 layout = Roo.factory(layout, Roo);
41305 config.el = config.layout.getEl();
41307 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41309 config.layout.monitorWindowResize = false; // turn off autosizing
41310 this.layout = config.layout;
41311 this.layout.getEl().addClass("roo-layout-nested-layout");
41312 this.layout.parent = this;
41319 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41321 setSize : function(width, height){
41322 if(!this.ignoreResize(width, height)){
41323 var size = this.adjustForComponents(width, height);
41324 var el = this.layout.getEl();
41325 if (size.height < 1) {
41326 el.setWidth(size.width);
41328 el.setSize(size.width, size.height);
41330 var touch = el.dom.offsetWidth;
41331 this.layout.layout();
41332 // ie requires a double layout on the first pass
41333 if(Roo.isIE && !this.initialized){
41334 this.initialized = true;
41335 this.layout.layout();
41340 // activate all subpanels if not currently active..
41342 setActiveState : function(active){
41343 this.active = active;
41344 this.setActiveClass(active);
41347 this.fireEvent("deactivate", this);
41351 this.fireEvent("activate", this);
41352 // not sure if this should happen before or after..
41353 if (!this.layout) {
41354 return; // should not happen..
41357 for (var r in this.layout.regions) {
41358 reg = this.layout.getRegion(r);
41359 if (reg.getActivePanel()) {
41360 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41361 reg.setActivePanel(reg.getActivePanel());
41364 if (!reg.panels.length) {
41367 reg.showPanel(reg.getPanel(0));
41376 * Returns the nested BorderLayout for this panel
41377 * @return {Roo.BorderLayout}
41379 getLayout : function(){
41380 return this.layout;
41384 * Adds a xtype elements to the layout of the nested panel
41388 xtype : 'ContentPanel',
41395 xtype : 'NestedLayoutPanel',
41401 items : [ ... list of content panels or nested layout panels.. ]
41405 * @param {Object} cfg Xtype definition of item to add.
41407 addxtype : function(cfg) {
41408 return this.layout.addxtype(cfg);
41413 * Ext JS Library 1.1.1
41414 * Copyright(c) 2006-2007, Ext JS, LLC.
41416 * Originally Released Under LGPL - original licence link has changed is not relivant.
41419 * <script type="text/javascript">
41422 * @class Roo.TabPanel
41423 * @extends Roo.util.Observable
41424 * A lightweight tab container.
41428 // basic tabs 1, built from existing content
41429 var tabs = new Roo.TabPanel("tabs1");
41430 tabs.addTab("script", "View Script");
41431 tabs.addTab("markup", "View Markup");
41432 tabs.activate("script");
41434 // more advanced tabs, built from javascript
41435 var jtabs = new Roo.TabPanel("jtabs");
41436 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41438 // set up the UpdateManager
41439 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41440 var updater = tab2.getUpdateManager();
41441 updater.setDefaultUrl("ajax1.htm");
41442 tab2.on('activate', updater.refresh, updater, true);
41444 // Use setUrl for Ajax loading
41445 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41446 tab3.setUrl("ajax2.htm", null, true);
41449 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41452 jtabs.activate("jtabs-1");
41455 * Create a new TabPanel.
41456 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41457 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41459 Roo.bootstrap.panel.Tabs = function(config){
41461 * The container element for this TabPanel.
41462 * @type Roo.Element
41464 this.el = Roo.get(config.el);
41467 if(typeof config == "boolean"){
41468 this.tabPosition = config ? "bottom" : "top";
41470 Roo.apply(this, config);
41474 if(this.tabPosition == "bottom"){
41475 // if tabs are at the bottom = create the body first.
41476 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41477 this.el.addClass("roo-tabs-bottom");
41479 // next create the tabs holders
41481 if (this.tabPosition == "west"){
41483 var reg = this.region; // fake it..
41485 if (!reg.mgr.parent) {
41488 reg = reg.mgr.parent.region;
41490 Roo.log("got nest?");
41492 if (reg.mgr.getRegion('west')) {
41493 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41494 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41495 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41496 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41497 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41505 this.stripWrap = Roo.get(this.createStrip(this.el.dom), 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);
41513 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41516 // finally - if tabs are at the top, then create the body last..
41517 if(this.tabPosition != "bottom"){
41518 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41519 * @type Roo.Element
41521 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41522 this.el.addClass("roo-tabs-top");
41526 this.bodyEl.setStyle("position", "relative");
41528 this.active = null;
41529 this.activateDelegate = this.activate.createDelegate(this);
41534 * Fires when the active tab changes
41535 * @param {Roo.TabPanel} this
41536 * @param {Roo.TabPanelItem} activePanel The new active tab
41540 * @event beforetabchange
41541 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41542 * @param {Roo.TabPanel} this
41543 * @param {Object} e Set cancel to true on this object to cancel the tab change
41544 * @param {Roo.TabPanelItem} tab The tab being changed to
41546 "beforetabchange" : true
41549 Roo.EventManager.onWindowResize(this.onResize, this);
41550 this.cpad = this.el.getPadding("lr");
41551 this.hiddenCount = 0;
41554 // toolbar on the tabbar support...
41555 if (this.toolbar) {
41556 alert("no toolbar support yet");
41557 this.toolbar = false;
41559 var tcfg = this.toolbar;
41560 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41561 this.toolbar = new Roo.Toolbar(tcfg);
41562 if (Roo.isSafari) {
41563 var tbl = tcfg.container.child('table', true);
41564 tbl.setAttribute('width', '100%');
41572 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41575 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41577 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41579 tabPosition : "top",
41581 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41583 currentTabWidth : 0,
41585 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41589 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41593 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41595 preferredTabWidth : 175,
41597 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41599 resizeTabs : false,
41601 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41603 monitorResize : true,
41605 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41607 toolbar : false, // set by caller..
41609 region : false, /// set by caller
41611 disableTooltips : true, // not used yet...
41614 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41615 * @param {String} id The id of the div to use <b>or create</b>
41616 * @param {String} text The text for the tab
41617 * @param {String} content (optional) Content to put in the TabPanelItem body
41618 * @param {Boolean} closable (optional) True to create a close icon on the tab
41619 * @return {Roo.TabPanelItem} The created TabPanelItem
41621 addTab : function(id, text, content, closable, tpl)
41623 var item = new Roo.bootstrap.panel.TabItem({
41627 closable : closable,
41630 this.addTabItem(item);
41632 item.setContent(content);
41638 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41639 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41640 * @return {Roo.TabPanelItem}
41642 getTab : function(id){
41643 return this.items[id];
41647 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41648 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41650 hideTab : function(id){
41651 var t = this.items[id];
41654 this.hiddenCount++;
41655 this.autoSizeTabs();
41660 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41661 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41663 unhideTab : function(id){
41664 var t = this.items[id];
41666 t.setHidden(false);
41667 this.hiddenCount--;
41668 this.autoSizeTabs();
41673 * Adds an existing {@link Roo.TabPanelItem}.
41674 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41676 addTabItem : function(item)
41678 this.items[item.id] = item;
41679 this.items.push(item);
41680 this.autoSizeTabs();
41681 // if(this.resizeTabs){
41682 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41683 // this.autoSizeTabs();
41685 // item.autoSize();
41690 * Removes a {@link Roo.TabPanelItem}.
41691 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41693 removeTab : function(id){
41694 var items = this.items;
41695 var tab = items[id];
41696 if(!tab) { return; }
41697 var index = items.indexOf(tab);
41698 if(this.active == tab && items.length > 1){
41699 var newTab = this.getNextAvailable(index);
41704 this.stripEl.dom.removeChild(tab.pnode.dom);
41705 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41706 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41708 items.splice(index, 1);
41709 delete this.items[tab.id];
41710 tab.fireEvent("close", tab);
41711 tab.purgeListeners();
41712 this.autoSizeTabs();
41715 getNextAvailable : function(start){
41716 var items = this.items;
41718 // look for a next tab that will slide over to
41719 // replace the one being removed
41720 while(index < items.length){
41721 var item = items[++index];
41722 if(item && !item.isHidden()){
41726 // if one isn't found select the previous tab (on the left)
41729 var item = items[--index];
41730 if(item && !item.isHidden()){
41738 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41739 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41741 disableTab : function(id){
41742 var tab = this.items[id];
41743 if(tab && this.active != tab){
41749 * Enables a {@link Roo.TabPanelItem} that is disabled.
41750 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41752 enableTab : function(id){
41753 var tab = this.items[id];
41758 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41759 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41760 * @return {Roo.TabPanelItem} The TabPanelItem.
41762 activate : function(id)
41764 //Roo.log('activite:' + id);
41766 var tab = this.items[id];
41770 if(tab == this.active || tab.disabled){
41774 this.fireEvent("beforetabchange", this, e, tab);
41775 if(e.cancel !== true && !tab.disabled){
41777 this.active.hide();
41779 this.active = this.items[id];
41780 this.active.show();
41781 this.fireEvent("tabchange", this, this.active);
41787 * Gets the active {@link Roo.TabPanelItem}.
41788 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41790 getActiveTab : function(){
41791 return this.active;
41795 * Updates the tab body element to fit the height of the container element
41796 * for overflow scrolling
41797 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41799 syncHeight : function(targetHeight){
41800 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41801 var bm = this.bodyEl.getMargins();
41802 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41803 this.bodyEl.setHeight(newHeight);
41807 onResize : function(){
41808 if(this.monitorResize){
41809 this.autoSizeTabs();
41814 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41816 beginUpdate : function(){
41817 this.updating = true;
41821 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41823 endUpdate : function(){
41824 this.updating = false;
41825 this.autoSizeTabs();
41829 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41831 autoSizeTabs : function()
41833 var count = this.items.length;
41834 var vcount = count - this.hiddenCount;
41837 this.stripEl.hide();
41839 this.stripEl.show();
41842 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41847 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41848 var availWidth = Math.floor(w / vcount);
41849 var b = this.stripBody;
41850 if(b.getWidth() > w){
41851 var tabs = this.items;
41852 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41853 if(availWidth < this.minTabWidth){
41854 /*if(!this.sleft){ // incomplete scrolling code
41855 this.createScrollButtons();
41858 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41861 if(this.currentTabWidth < this.preferredTabWidth){
41862 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41868 * Returns the number of tabs in this TabPanel.
41871 getCount : function(){
41872 return this.items.length;
41876 * Resizes all the tabs to the passed width
41877 * @param {Number} The new width
41879 setTabWidth : function(width){
41880 this.currentTabWidth = width;
41881 for(var i = 0, len = this.items.length; i < len; i++) {
41882 if(!this.items[i].isHidden()) {
41883 this.items[i].setWidth(width);
41889 * Destroys this TabPanel
41890 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41892 destroy : function(removeEl){
41893 Roo.EventManager.removeResizeListener(this.onResize, this);
41894 for(var i = 0, len = this.items.length; i < len; i++){
41895 this.items[i].purgeListeners();
41897 if(removeEl === true){
41898 this.el.update("");
41903 createStrip : function(container)
41905 var strip = document.createElement("nav");
41906 strip.className = Roo.bootstrap.version == 4 ?
41907 "navbar-light bg-light" :
41908 "navbar navbar-default"; //"x-tabs-wrap";
41909 container.appendChild(strip);
41913 createStripList : function(strip)
41915 // div wrapper for retard IE
41916 // returns the "tr" element.
41917 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41918 //'<div class="x-tabs-strip-wrap">'+
41919 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41920 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41921 return strip.firstChild; //.firstChild.firstChild.firstChild;
41923 createBody : function(container)
41925 var body = document.createElement("div");
41926 Roo.id(body, "tab-body");
41927 //Roo.fly(body).addClass("x-tabs-body");
41928 Roo.fly(body).addClass("tab-content");
41929 container.appendChild(body);
41932 createItemBody :function(bodyEl, id){
41933 var body = Roo.getDom(id);
41935 body = document.createElement("div");
41938 //Roo.fly(body).addClass("x-tabs-item-body");
41939 Roo.fly(body).addClass("tab-pane");
41940 bodyEl.insertBefore(body, bodyEl.firstChild);
41944 createStripElements : function(stripEl, text, closable, tpl)
41946 var td = document.createElement("li"); // was td..
41947 td.className = 'nav-item';
41949 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41952 stripEl.appendChild(td);
41954 td.className = "x-tabs-closable";
41955 if(!this.closeTpl){
41956 this.closeTpl = new Roo.Template(
41957 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41958 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41959 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41962 var el = this.closeTpl.overwrite(td, {"text": text});
41963 var close = el.getElementsByTagName("div")[0];
41964 var inner = el.getElementsByTagName("em")[0];
41965 return {"el": el, "close": close, "inner": inner};
41968 // not sure what this is..
41969 // if(!this.tabTpl){
41970 //this.tabTpl = new Roo.Template(
41971 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41972 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41974 // this.tabTpl = new Roo.Template(
41975 // '<a href="#">' +
41976 // '<span unselectable="on"' +
41977 // (this.disableTooltips ? '' : ' title="{text}"') +
41978 // ' >{text}</span></a>'
41984 var template = tpl || this.tabTpl || false;
41987 template = new Roo.Template(
41988 Roo.bootstrap.version == 4 ?
41990 '<a class="nav-link" href="#" unselectable="on"' +
41991 (this.disableTooltips ? '' : ' title="{text}"') +
41994 '<a class="nav-link" href="#">' +
41995 '<span unselectable="on"' +
41996 (this.disableTooltips ? '' : ' title="{text}"') +
41997 ' >{text}</span></a>'
42002 switch (typeof(template)) {
42006 template = new Roo.Template(template);
42012 var el = template.overwrite(td, {"text": text});
42014 var inner = el.getElementsByTagName("span")[0];
42016 return {"el": el, "inner": inner};
42024 * @class Roo.TabPanelItem
42025 * @extends Roo.util.Observable
42026 * Represents an individual item (tab plus body) in a TabPanel.
42027 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42028 * @param {String} id The id of this TabPanelItem
42029 * @param {String} text The text for the tab of this TabPanelItem
42030 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42032 Roo.bootstrap.panel.TabItem = function(config){
42034 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42035 * @type Roo.TabPanel
42037 this.tabPanel = config.panel;
42039 * The id for this TabPanelItem
42042 this.id = config.id;
42044 this.disabled = false;
42046 this.text = config.text;
42048 this.loaded = false;
42049 this.closable = config.closable;
42052 * The body element for this TabPanelItem.
42053 * @type Roo.Element
42055 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42056 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42057 this.bodyEl.setStyle("display", "block");
42058 this.bodyEl.setStyle("zoom", "1");
42059 //this.hideAction();
42061 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42063 this.el = Roo.get(els.el);
42064 this.inner = Roo.get(els.inner, true);
42065 this.textEl = Roo.bootstrap.version == 4 ?
42066 this.el : Roo.get(this.el.dom.firstChild, true);
42068 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42069 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42072 // this.el.on("mousedown", this.onTabMouseDown, this);
42073 this.el.on("click", this.onTabClick, this);
42075 if(config.closable){
42076 var c = Roo.get(els.close, true);
42077 c.dom.title = this.closeText;
42078 c.addClassOnOver("close-over");
42079 c.on("click", this.closeClick, this);
42085 * Fires when this tab becomes the active tab.
42086 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42087 * @param {Roo.TabPanelItem} this
42091 * @event beforeclose
42092 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42093 * @param {Roo.TabPanelItem} this
42094 * @param {Object} e Set cancel to true on this object to cancel the close.
42096 "beforeclose": true,
42099 * Fires when this tab is closed.
42100 * @param {Roo.TabPanelItem} this
42104 * @event deactivate
42105 * Fires when this tab is no longer the active tab.
42106 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42107 * @param {Roo.TabPanelItem} this
42109 "deactivate" : true
42111 this.hidden = false;
42113 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42116 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42118 purgeListeners : function(){
42119 Roo.util.Observable.prototype.purgeListeners.call(this);
42120 this.el.removeAllListeners();
42123 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42126 this.status_node.addClass("active");
42129 this.tabPanel.stripWrap.repaint();
42131 this.fireEvent("activate", this.tabPanel, this);
42135 * Returns true if this tab is the active tab.
42136 * @return {Boolean}
42138 isActive : function(){
42139 return this.tabPanel.getActiveTab() == this;
42143 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42146 this.status_node.removeClass("active");
42148 this.fireEvent("deactivate", this.tabPanel, this);
42151 hideAction : function(){
42152 this.bodyEl.hide();
42153 this.bodyEl.setStyle("position", "absolute");
42154 this.bodyEl.setLeft("-20000px");
42155 this.bodyEl.setTop("-20000px");
42158 showAction : function(){
42159 this.bodyEl.setStyle("position", "relative");
42160 this.bodyEl.setTop("");
42161 this.bodyEl.setLeft("");
42162 this.bodyEl.show();
42166 * Set the tooltip for the tab.
42167 * @param {String} tooltip The tab's tooltip
42169 setTooltip : function(text){
42170 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42171 this.textEl.dom.qtip = text;
42172 this.textEl.dom.removeAttribute('title');
42174 this.textEl.dom.title = text;
42178 onTabClick : function(e){
42179 e.preventDefault();
42180 this.tabPanel.activate(this.id);
42183 onTabMouseDown : function(e){
42184 e.preventDefault();
42185 this.tabPanel.activate(this.id);
42188 getWidth : function(){
42189 return this.inner.getWidth();
42192 setWidth : function(width){
42193 var iwidth = width - this.linode.getPadding("lr");
42194 this.inner.setWidth(iwidth);
42195 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42196 this.linode.setWidth(width);
42200 * Show or hide the tab
42201 * @param {Boolean} hidden True to hide or false to show.
42203 setHidden : function(hidden){
42204 this.hidden = hidden;
42205 this.linode.setStyle("display", hidden ? "none" : "");
42209 * Returns true if this tab is "hidden"
42210 * @return {Boolean}
42212 isHidden : function(){
42213 return this.hidden;
42217 * Returns the text for this tab
42220 getText : function(){
42224 autoSize : function(){
42225 //this.el.beginMeasure();
42226 this.textEl.setWidth(1);
42228 * #2804 [new] Tabs in Roojs
42229 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42231 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42232 //this.el.endMeasure();
42236 * Sets the text for the tab (Note: this also sets the tooltip text)
42237 * @param {String} text The tab's text and tooltip
42239 setText : function(text){
42241 this.textEl.update(text);
42242 this.setTooltip(text);
42243 //if(!this.tabPanel.resizeTabs){
42244 // this.autoSize();
42248 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42250 activate : function(){
42251 this.tabPanel.activate(this.id);
42255 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42257 disable : function(){
42258 if(this.tabPanel.active != this){
42259 this.disabled = true;
42260 this.status_node.addClass("disabled");
42265 * Enables this TabPanelItem if it was previously disabled.
42267 enable : function(){
42268 this.disabled = false;
42269 this.status_node.removeClass("disabled");
42273 * Sets the content for this TabPanelItem.
42274 * @param {String} content The content
42275 * @param {Boolean} loadScripts true to look for and load scripts
42277 setContent : function(content, loadScripts){
42278 this.bodyEl.update(content, loadScripts);
42282 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42283 * @return {Roo.UpdateManager} The UpdateManager
42285 getUpdateManager : function(){
42286 return this.bodyEl.getUpdateManager();
42290 * Set a URL to be used to load the content for this TabPanelItem.
42291 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42292 * @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)
42293 * @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)
42294 * @return {Roo.UpdateManager} The UpdateManager
42296 setUrl : function(url, params, loadOnce){
42297 if(this.refreshDelegate){
42298 this.un('activate', this.refreshDelegate);
42300 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42301 this.on("activate", this.refreshDelegate);
42302 return this.bodyEl.getUpdateManager();
42306 _handleRefresh : function(url, params, loadOnce){
42307 if(!loadOnce || !this.loaded){
42308 var updater = this.bodyEl.getUpdateManager();
42309 updater.update(url, params, this._setLoaded.createDelegate(this));
42314 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42315 * Will fail silently if the setUrl method has not been called.
42316 * This does not activate the panel, just updates its content.
42318 refresh : function(){
42319 if(this.refreshDelegate){
42320 this.loaded = false;
42321 this.refreshDelegate();
42326 _setLoaded : function(){
42327 this.loaded = true;
42331 closeClick : function(e){
42334 this.fireEvent("beforeclose", this, o);
42335 if(o.cancel !== true){
42336 this.tabPanel.removeTab(this.id);
42340 * The text displayed in the tooltip for the close icon.
42343 closeText : "Close this tab"
42346 * This script refer to:
42347 * Title: International Telephone Input
42348 * Author: Jack O'Connor
42349 * Code version: v12.1.12
42350 * Availability: https://github.com/jackocnr/intl-tel-input.git
42353 Roo.bootstrap.PhoneInputData = function() {
42356 "Afghanistan (افغانستان)",
42361 "Albania (Shqipëri)",
42366 "Algeria (الجزائر)",
42391 "Antigua and Barbuda",
42401 "Armenia (Հայաստան)",
42417 "Austria (Österreich)",
42422 "Azerbaijan (Azərbaycan)",
42432 "Bahrain (البحرين)",
42437 "Bangladesh (বাংলাদেশ)",
42447 "Belarus (Беларусь)",
42452 "Belgium (België)",
42482 "Bosnia and Herzegovina (Босна и Херцеговина)",
42497 "British Indian Ocean Territory",
42502 "British Virgin Islands",
42512 "Bulgaria (България)",
42522 "Burundi (Uburundi)",
42527 "Cambodia (កម្ពុជា)",
42532 "Cameroon (Cameroun)",
42541 ["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"]
42544 "Cape Verde (Kabu Verdi)",
42549 "Caribbean Netherlands",
42560 "Central African Republic (République centrafricaine)",
42580 "Christmas Island",
42586 "Cocos (Keeling) Islands",
42597 "Comoros (جزر القمر)",
42602 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42607 "Congo (Republic) (Congo-Brazzaville)",
42627 "Croatia (Hrvatska)",
42648 "Czech Republic (Česká republika)",
42653 "Denmark (Danmark)",
42668 "Dominican Republic (República Dominicana)",
42672 ["809", "829", "849"]
42690 "Equatorial Guinea (Guinea Ecuatorial)",
42710 "Falkland Islands (Islas Malvinas)",
42715 "Faroe Islands (Føroyar)",
42736 "French Guiana (Guyane française)",
42741 "French Polynesia (Polynésie française)",
42756 "Georgia (საქართველო)",
42761 "Germany (Deutschland)",
42781 "Greenland (Kalaallit Nunaat)",
42818 "Guinea-Bissau (Guiné Bissau)",
42843 "Hungary (Magyarország)",
42848 "Iceland (Ísland)",
42868 "Iraq (العراق)",
42884 "Israel (ישראל)",
42911 "Jordan (الأردن)",
42916 "Kazakhstan (Казахстан)",
42937 "Kuwait (الكويت)",
42942 "Kyrgyzstan (Кыргызстан)",
42952 "Latvia (Latvija)",
42957 "Lebanon (لبنان)",
42972 "Libya (ليبيا)",
42982 "Lithuania (Lietuva)",
42997 "Macedonia (FYROM) (Македонија)",
43002 "Madagascar (Madagasikara)",
43032 "Marshall Islands",
43042 "Mauritania (موريتانيا)",
43047 "Mauritius (Moris)",
43068 "Moldova (Republica Moldova)",
43078 "Mongolia (Монгол)",
43083 "Montenegro (Crna Gora)",
43093 "Morocco (المغرب)",
43099 "Mozambique (Moçambique)",
43104 "Myanmar (Burma) (မြန်မာ)",
43109 "Namibia (Namibië)",
43124 "Netherlands (Nederland)",
43129 "New Caledonia (Nouvelle-Calédonie)",
43164 "North Korea (조선 민주주의 인민 공화국)",
43169 "Northern Mariana Islands",
43185 "Pakistan (پاکستان)",
43195 "Palestine (فلسطين)",
43205 "Papua New Guinea",
43247 "Réunion (La Réunion)",
43253 "Romania (România)",
43269 "Saint Barthélemy",
43280 "Saint Kitts and Nevis",
43290 "Saint Martin (Saint-Martin (partie française))",
43296 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43301 "Saint Vincent and the Grenadines",
43316 "São Tomé and Príncipe (São Tomé e Príncipe)",
43321 "Saudi Arabia (المملكة العربية السعودية)",
43326 "Senegal (Sénégal)",
43356 "Slovakia (Slovensko)",
43361 "Slovenia (Slovenija)",
43371 "Somalia (Soomaaliya)",
43381 "South Korea (대한민국)",
43386 "South Sudan (جنوب السودان)",
43396 "Sri Lanka (ශ්රී ලංකාව)",
43401 "Sudan (السودان)",
43411 "Svalbard and Jan Mayen",
43422 "Sweden (Sverige)",
43427 "Switzerland (Schweiz)",
43432 "Syria (سوريا)",
43477 "Trinidad and Tobago",
43482 "Tunisia (تونس)",
43487 "Turkey (Türkiye)",
43497 "Turks and Caicos Islands",
43507 "U.S. Virgin Islands",
43517 "Ukraine (Україна)",
43522 "United Arab Emirates (الإمارات العربية المتحدة)",
43544 "Uzbekistan (Oʻzbekiston)",
43554 "Vatican City (Città del Vaticano)",
43565 "Vietnam (Việt Nam)",
43570 "Wallis and Futuna (Wallis-et-Futuna)",
43575 "Western Sahara (الصحراء الغربية)",
43581 "Yemen (اليمن)",
43605 * This script refer to:
43606 * Title: International Telephone Input
43607 * Author: Jack O'Connor
43608 * Code version: v12.1.12
43609 * Availability: https://github.com/jackocnr/intl-tel-input.git
43613 * @class Roo.bootstrap.PhoneInput
43614 * @extends Roo.bootstrap.TriggerField
43615 * An input with International dial-code selection
43617 * @cfg {String} defaultDialCode default '+852'
43618 * @cfg {Array} preferedCountries default []
43621 * Create a new PhoneInput.
43622 * @param {Object} config Configuration options
43625 Roo.bootstrap.PhoneInput = function(config) {
43626 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43629 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43631 listWidth: undefined,
43633 selectedClass: 'active',
43635 invalidClass : "has-warning",
43637 validClass: 'has-success',
43639 allowed: '0123456789',
43644 * @cfg {String} defaultDialCode The default dial code when initializing the input
43646 defaultDialCode: '+852',
43649 * @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
43651 preferedCountries: false,
43653 getAutoCreate : function()
43655 var data = Roo.bootstrap.PhoneInputData();
43656 var align = this.labelAlign || this.parentLabelAlign();
43659 this.allCountries = [];
43660 this.dialCodeMapping = [];
43662 for (var i = 0; i < data.length; i++) {
43664 this.allCountries[i] = {
43668 priority: c[3] || 0,
43669 areaCodes: c[4] || null
43671 this.dialCodeMapping[c[2]] = {
43674 priority: c[3] || 0,
43675 areaCodes: c[4] || null
43687 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43688 maxlength: this.max_length,
43689 cls : 'form-control tel-input',
43690 autocomplete: 'new-password'
43693 var hiddenInput = {
43696 cls: 'hidden-tel-input'
43700 hiddenInput.name = this.name;
43703 if (this.disabled) {
43704 input.disabled = true;
43707 var flag_container = {
43724 cls: this.hasFeedback ? 'has-feedback' : '',
43730 cls: 'dial-code-holder',
43737 cls: 'roo-select2-container input-group',
43744 if (this.fieldLabel.length) {
43747 tooltip: 'This field is required'
43753 cls: 'control-label',
43759 html: this.fieldLabel
43762 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43768 if(this.indicatorpos == 'right') {
43769 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43776 if(align == 'left') {
43784 if(this.labelWidth > 12){
43785 label.style = "width: " + this.labelWidth + 'px';
43787 if(this.labelWidth < 13 && this.labelmd == 0){
43788 this.labelmd = this.labelWidth;
43790 if(this.labellg > 0){
43791 label.cls += ' col-lg-' + this.labellg;
43792 input.cls += ' col-lg-' + (12 - this.labellg);
43794 if(this.labelmd > 0){
43795 label.cls += ' col-md-' + this.labelmd;
43796 container.cls += ' col-md-' + (12 - this.labelmd);
43798 if(this.labelsm > 0){
43799 label.cls += ' col-sm-' + this.labelsm;
43800 container.cls += ' col-sm-' + (12 - this.labelsm);
43802 if(this.labelxs > 0){
43803 label.cls += ' col-xs-' + this.labelxs;
43804 container.cls += ' col-xs-' + (12 - this.labelxs);
43814 var settings = this;
43816 ['xs','sm','md','lg'].map(function(size){
43817 if (settings[size]) {
43818 cfg.cls += ' col-' + size + '-' + settings[size];
43822 this.store = new Roo.data.Store({
43823 proxy : new Roo.data.MemoryProxy({}),
43824 reader : new Roo.data.JsonReader({
43835 'name' : 'dialCode',
43839 'name' : 'priority',
43843 'name' : 'areaCodes',
43850 if(!this.preferedCountries) {
43851 this.preferedCountries = [
43858 var p = this.preferedCountries.reverse();
43861 for (var i = 0; i < p.length; i++) {
43862 for (var j = 0; j < this.allCountries.length; j++) {
43863 if(this.allCountries[j].iso2 == p[i]) {
43864 var t = this.allCountries[j];
43865 this.allCountries.splice(j,1);
43866 this.allCountries.unshift(t);
43872 this.store.proxy.data = {
43874 data: this.allCountries
43880 initEvents : function()
43883 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43885 this.indicator = this.indicatorEl();
43886 this.flag = this.flagEl();
43887 this.dialCodeHolder = this.dialCodeHolderEl();
43889 this.trigger = this.el.select('div.flag-box',true).first();
43890 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43895 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43896 _this.list.setWidth(lw);
43899 this.list.on('mouseover', this.onViewOver, this);
43900 this.list.on('mousemove', this.onViewMove, this);
43901 this.inputEl().on("keyup", this.onKeyUp, this);
43902 this.inputEl().on("keypress", this.onKeyPress, this);
43904 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43906 this.view = new Roo.View(this.list, this.tpl, {
43907 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43910 this.view.on('click', this.onViewClick, this);
43911 this.setValue(this.defaultDialCode);
43914 onTriggerClick : function(e)
43916 Roo.log('trigger click');
43921 if(this.isExpanded()){
43923 this.hasFocus = false;
43925 this.store.load({});
43926 this.hasFocus = true;
43931 isExpanded : function()
43933 return this.list.isVisible();
43936 collapse : function()
43938 if(!this.isExpanded()){
43942 Roo.get(document).un('mousedown', this.collapseIf, this);
43943 Roo.get(document).un('mousewheel', this.collapseIf, this);
43944 this.fireEvent('collapse', this);
43948 expand : function()
43952 if(this.isExpanded() || !this.hasFocus){
43956 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43957 this.list.setWidth(lw);
43960 this.restrictHeight();
43962 Roo.get(document).on('mousedown', this.collapseIf, this);
43963 Roo.get(document).on('mousewheel', this.collapseIf, this);
43965 this.fireEvent('expand', this);
43968 restrictHeight : function()
43970 this.list.alignTo(this.inputEl(), this.listAlign);
43971 this.list.alignTo(this.inputEl(), this.listAlign);
43974 onViewOver : function(e, t)
43976 if(this.inKeyMode){
43979 var item = this.view.findItemFromChild(t);
43982 var index = this.view.indexOf(item);
43983 this.select(index, false);
43988 onViewClick : function(view, doFocus, el, e)
43990 var index = this.view.getSelectedIndexes()[0];
43992 var r = this.store.getAt(index);
43995 this.onSelect(r, index);
43997 if(doFocus !== false && !this.blockFocus){
43998 this.inputEl().focus();
44002 onViewMove : function(e, t)
44004 this.inKeyMode = false;
44007 select : function(index, scrollIntoView)
44009 this.selectedIndex = index;
44010 this.view.select(index);
44011 if(scrollIntoView !== false){
44012 var el = this.view.getNode(index);
44014 this.list.scrollChildIntoView(el, false);
44019 createList : function()
44021 this.list = Roo.get(document.body).createChild({
44023 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44024 style: 'display:none'
44027 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44030 collapseIf : function(e)
44032 var in_combo = e.within(this.el);
44033 var in_list = e.within(this.list);
44034 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44036 if (in_combo || in_list || is_list) {
44042 onSelect : function(record, index)
44044 if(this.fireEvent('beforeselect', this, record, index) !== false){
44046 this.setFlagClass(record.data.iso2);
44047 this.setDialCode(record.data.dialCode);
44048 this.hasFocus = false;
44050 this.fireEvent('select', this, record, index);
44054 flagEl : function()
44056 var flag = this.el.select('div.flag',true).first();
44063 dialCodeHolderEl : function()
44065 var d = this.el.select('input.dial-code-holder',true).first();
44072 setDialCode : function(v)
44074 this.dialCodeHolder.dom.value = '+'+v;
44077 setFlagClass : function(n)
44079 this.flag.dom.className = 'flag '+n;
44082 getValue : function()
44084 var v = this.inputEl().getValue();
44085 if(this.dialCodeHolder) {
44086 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44091 setValue : function(v)
44093 var d = this.getDialCode(v);
44095 //invalid dial code
44096 if(v.length == 0 || !d || d.length == 0) {
44098 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44099 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44105 this.setFlagClass(this.dialCodeMapping[d].iso2);
44106 this.setDialCode(d);
44107 this.inputEl().dom.value = v.replace('+'+d,'');
44108 this.hiddenEl().dom.value = this.getValue();
44113 getDialCode : function(v)
44117 if (v.length == 0) {
44118 return this.dialCodeHolder.dom.value;
44122 if (v.charAt(0) != "+") {
44125 var numericChars = "";
44126 for (var i = 1; i < v.length; i++) {
44127 var c = v.charAt(i);
44130 if (this.dialCodeMapping[numericChars]) {
44131 dialCode = v.substr(1, i);
44133 if (numericChars.length == 4) {
44143 this.setValue(this.defaultDialCode);
44147 hiddenEl : function()
44149 return this.el.select('input.hidden-tel-input',true).first();
44152 // after setting val
44153 onKeyUp : function(e){
44154 this.setValue(this.getValue());
44157 onKeyPress : function(e){
44158 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44165 * @class Roo.bootstrap.MoneyField
44166 * @extends Roo.bootstrap.ComboBox
44167 * Bootstrap MoneyField class
44170 * Create a new MoneyField.
44171 * @param {Object} config Configuration options
44174 Roo.bootstrap.MoneyField = function(config) {
44176 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44180 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44183 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44185 allowDecimals : true,
44187 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44189 decimalSeparator : ".",
44191 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44193 decimalPrecision : 0,
44195 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44197 allowNegative : true,
44199 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44203 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44205 minValue : Number.NEGATIVE_INFINITY,
44207 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44209 maxValue : Number.MAX_VALUE,
44211 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44213 minText : "The minimum value for this field is {0}",
44215 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44217 maxText : "The maximum value for this field is {0}",
44219 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44220 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44222 nanText : "{0} is not a valid number",
44224 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44228 * @cfg {String} defaults currency of the MoneyField
44229 * value should be in lkey
44231 defaultCurrency : false,
44233 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44235 thousandsDelimiter : false,
44237 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44248 getAutoCreate : function()
44250 var align = this.labelAlign || this.parentLabelAlign();
44262 cls : 'form-control roo-money-amount-input',
44263 autocomplete: 'new-password'
44266 var hiddenInput = {
44270 cls: 'hidden-number-input'
44273 if(this.max_length) {
44274 input.maxlength = this.max_length;
44278 hiddenInput.name = this.name;
44281 if (this.disabled) {
44282 input.disabled = true;
44285 var clg = 12 - this.inputlg;
44286 var cmd = 12 - this.inputmd;
44287 var csm = 12 - this.inputsm;
44288 var cxs = 12 - this.inputxs;
44292 cls : 'row roo-money-field',
44296 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44300 cls: 'roo-select2-container input-group',
44304 cls : 'form-control roo-money-currency-input',
44305 autocomplete: 'new-password',
44307 name : this.currencyName
44311 cls : 'input-group-addon',
44325 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44329 cls: this.hasFeedback ? 'has-feedback' : '',
44340 if (this.fieldLabel.length) {
44343 tooltip: 'This field is required'
44349 cls: 'control-label',
44355 html: this.fieldLabel
44358 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44364 if(this.indicatorpos == 'right') {
44365 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44372 if(align == 'left') {
44380 if(this.labelWidth > 12){
44381 label.style = "width: " + this.labelWidth + 'px';
44383 if(this.labelWidth < 13 && this.labelmd == 0){
44384 this.labelmd = this.labelWidth;
44386 if(this.labellg > 0){
44387 label.cls += ' col-lg-' + this.labellg;
44388 input.cls += ' col-lg-' + (12 - this.labellg);
44390 if(this.labelmd > 0){
44391 label.cls += ' col-md-' + this.labelmd;
44392 container.cls += ' col-md-' + (12 - this.labelmd);
44394 if(this.labelsm > 0){
44395 label.cls += ' col-sm-' + this.labelsm;
44396 container.cls += ' col-sm-' + (12 - this.labelsm);
44398 if(this.labelxs > 0){
44399 label.cls += ' col-xs-' + this.labelxs;
44400 container.cls += ' col-xs-' + (12 - this.labelxs);
44411 var settings = this;
44413 ['xs','sm','md','lg'].map(function(size){
44414 if (settings[size]) {
44415 cfg.cls += ' col-' + size + '-' + settings[size];
44422 initEvents : function()
44424 this.indicator = this.indicatorEl();
44426 this.initCurrencyEvent();
44428 this.initNumberEvent();
44431 initCurrencyEvent : function()
44434 throw "can not find store for combo";
44437 this.store = Roo.factory(this.store, Roo.data);
44438 this.store.parent = this;
44442 this.triggerEl = this.el.select('.input-group-addon', true).first();
44444 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44449 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44450 _this.list.setWidth(lw);
44453 this.list.on('mouseover', this.onViewOver, this);
44454 this.list.on('mousemove', this.onViewMove, this);
44455 this.list.on('scroll', this.onViewScroll, this);
44458 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44461 this.view = new Roo.View(this.list, this.tpl, {
44462 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44465 this.view.on('click', this.onViewClick, this);
44467 this.store.on('beforeload', this.onBeforeLoad, this);
44468 this.store.on('load', this.onLoad, this);
44469 this.store.on('loadexception', this.onLoadException, this);
44471 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44472 "up" : function(e){
44473 this.inKeyMode = true;
44477 "down" : function(e){
44478 if(!this.isExpanded()){
44479 this.onTriggerClick();
44481 this.inKeyMode = true;
44486 "enter" : function(e){
44489 if(this.fireEvent("specialkey", this, e)){
44490 this.onViewClick(false);
44496 "esc" : function(e){
44500 "tab" : function(e){
44503 if(this.fireEvent("specialkey", this, e)){
44504 this.onViewClick(false);
44512 doRelay : function(foo, bar, hname){
44513 if(hname == 'down' || this.scope.isExpanded()){
44514 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44522 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44526 initNumberEvent : function(e)
44528 this.inputEl().on("keydown" , this.fireKey, this);
44529 this.inputEl().on("focus", this.onFocus, this);
44530 this.inputEl().on("blur", this.onBlur, this);
44532 this.inputEl().relayEvent('keyup', this);
44534 if(this.indicator){
44535 this.indicator.addClass('invisible');
44538 this.originalValue = this.getValue();
44540 if(this.validationEvent == 'keyup'){
44541 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44542 this.inputEl().on('keyup', this.filterValidation, this);
44544 else if(this.validationEvent !== false){
44545 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44548 if(this.selectOnFocus){
44549 this.on("focus", this.preFocus, this);
44552 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44553 this.inputEl().on("keypress", this.filterKeys, this);
44555 this.inputEl().relayEvent('keypress', this);
44558 var allowed = "0123456789";
44560 if(this.allowDecimals){
44561 allowed += this.decimalSeparator;
44564 if(this.allowNegative){
44568 if(this.thousandsDelimiter) {
44572 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44574 var keyPress = function(e){
44576 var k = e.getKey();
44578 var c = e.getCharCode();
44581 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44582 allowed.indexOf(String.fromCharCode(c)) === -1
44588 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44592 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44597 this.inputEl().on("keypress", keyPress, this);
44601 onTriggerClick : function(e)
44608 this.loadNext = false;
44610 if(this.isExpanded()){
44615 this.hasFocus = true;
44617 if(this.triggerAction == 'all') {
44618 this.doQuery(this.allQuery, true);
44622 this.doQuery(this.getRawValue());
44625 getCurrency : function()
44627 var v = this.currencyEl().getValue();
44632 restrictHeight : function()
44634 this.list.alignTo(this.currencyEl(), this.listAlign);
44635 this.list.alignTo(this.currencyEl(), this.listAlign);
44638 onViewClick : function(view, doFocus, el, e)
44640 var index = this.view.getSelectedIndexes()[0];
44642 var r = this.store.getAt(index);
44645 this.onSelect(r, index);
44649 onSelect : function(record, index){
44651 if(this.fireEvent('beforeselect', this, record, index) !== false){
44653 this.setFromCurrencyData(index > -1 ? record.data : false);
44657 this.fireEvent('select', this, record, index);
44661 setFromCurrencyData : function(o)
44665 this.lastCurrency = o;
44667 if (this.currencyField) {
44668 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44670 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44673 this.lastSelectionText = currency;
44675 //setting default currency
44676 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44677 this.setCurrency(this.defaultCurrency);
44681 this.setCurrency(currency);
44684 setFromData : function(o)
44688 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44690 this.setFromCurrencyData(c);
44695 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44697 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44700 this.setValue(value);
44704 setCurrency : function(v)
44706 this.currencyValue = v;
44709 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44714 setValue : function(v)
44716 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44722 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44724 this.inputEl().dom.value = (v == '') ? '' :
44725 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44727 if(!this.allowZero && v === '0') {
44728 this.hiddenEl().dom.value = '';
44729 this.inputEl().dom.value = '';
44736 getRawValue : function()
44738 var v = this.inputEl().getValue();
44743 getValue : function()
44745 return this.fixPrecision(this.parseValue(this.getRawValue()));
44748 parseValue : function(value)
44750 if(this.thousandsDelimiter) {
44752 r = new RegExp(",", "g");
44753 value = value.replace(r, "");
44756 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44757 return isNaN(value) ? '' : value;
44761 fixPrecision : function(value)
44763 if(this.thousandsDelimiter) {
44765 r = new RegExp(",", "g");
44766 value = value.replace(r, "");
44769 var nan = isNaN(value);
44771 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44772 return nan ? '' : value;
44774 return parseFloat(value).toFixed(this.decimalPrecision);
44777 decimalPrecisionFcn : function(v)
44779 return Math.floor(v);
44782 validateValue : function(value)
44784 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44788 var num = this.parseValue(value);
44791 this.markInvalid(String.format(this.nanText, value));
44795 if(num < this.minValue){
44796 this.markInvalid(String.format(this.minText, this.minValue));
44800 if(num > this.maxValue){
44801 this.markInvalid(String.format(this.maxText, this.maxValue));
44808 validate : function()
44810 if(this.disabled || this.allowBlank){
44815 var currency = this.getCurrency();
44817 if(this.validateValue(this.getRawValue()) && currency.length){
44822 this.markInvalid();
44826 getName: function()
44831 beforeBlur : function()
44837 var v = this.parseValue(this.getRawValue());
44844 onBlur : function()
44848 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44849 //this.el.removeClass(this.focusClass);
44852 this.hasFocus = false;
44854 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44858 var v = this.getValue();
44860 if(String(v) !== String(this.startValue)){
44861 this.fireEvent('change', this, v, this.startValue);
44864 this.fireEvent("blur", this);
44867 inputEl : function()
44869 return this.el.select('.roo-money-amount-input', true).first();
44872 currencyEl : function()
44874 return this.el.select('.roo-money-currency-input', true).first();
44877 hiddenEl : function()
44879 return this.el.select('input.hidden-number-input',true).first();
44883 * @class Roo.bootstrap.BezierSignature
44884 * @extends Roo.bootstrap.Component
44885 * Bootstrap BezierSignature class
44886 * This script refer to:
44887 * Title: Signature Pad
44889 * Availability: https://github.com/szimek/signature_pad
44892 * Create a new BezierSignature
44893 * @param {Object} config The config object
44896 Roo.bootstrap.BezierSignature = function(config){
44897 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44903 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44910 mouse_btn_down: true,
44913 * @cfg {int} canvas height
44915 canvas_height: '200px',
44918 * @cfg {float|function} Radius of a single dot.
44923 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44928 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44933 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44938 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44943 * @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.
44945 bg_color: 'rgba(0, 0, 0, 0)',
44948 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44950 dot_color: 'black',
44953 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44955 velocity_filter_weight: 0.7,
44958 * @cfg {function} Callback when stroke begin.
44963 * @cfg {function} Callback when stroke end.
44967 getAutoCreate : function()
44969 var cls = 'roo-signature column';
44972 cls += ' ' + this.cls;
44982 for(var i = 0; i < col_sizes.length; i++) {
44983 if(this[col_sizes[i]]) {
44984 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44994 cls: 'roo-signature-body',
44998 cls: 'roo-signature-body-canvas',
44999 height: this.canvas_height,
45000 width: this.canvas_width
45007 style: 'display: none'
45015 initEvents: function()
45017 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45019 var canvas = this.canvasEl();
45021 // mouse && touch event swapping...
45022 canvas.dom.style.touchAction = 'none';
45023 canvas.dom.style.msTouchAction = 'none';
45025 this.mouse_btn_down = false;
45026 canvas.on('mousedown', this._handleMouseDown, this);
45027 canvas.on('mousemove', this._handleMouseMove, this);
45028 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45030 if (window.PointerEvent) {
45031 canvas.on('pointerdown', this._handleMouseDown, this);
45032 canvas.on('pointermove', this._handleMouseMove, this);
45033 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45036 if ('ontouchstart' in window) {
45037 canvas.on('touchstart', this._handleTouchStart, this);
45038 canvas.on('touchmove', this._handleTouchMove, this);
45039 canvas.on('touchend', this._handleTouchEnd, this);
45042 Roo.EventManager.onWindowResize(this.resize, this, true);
45044 // file input event
45045 this.fileEl().on('change', this.uploadImage, this);
45052 resize: function(){
45054 var canvas = this.canvasEl().dom;
45055 var ctx = this.canvasElCtx();
45056 var img_data = false;
45058 if(canvas.width > 0) {
45059 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45061 // setting canvas width will clean img data
45064 var style = window.getComputedStyle ?
45065 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45067 var padding_left = parseInt(style.paddingLeft) || 0;
45068 var padding_right = parseInt(style.paddingRight) || 0;
45070 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45073 ctx.putImageData(img_data, 0, 0);
45077 _handleMouseDown: function(e)
45079 if (e.browserEvent.which === 1) {
45080 this.mouse_btn_down = true;
45081 this.strokeBegin(e);
45085 _handleMouseMove: function (e)
45087 if (this.mouse_btn_down) {
45088 this.strokeMoveUpdate(e);
45092 _handleMouseUp: function (e)
45094 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45095 this.mouse_btn_down = false;
45100 _handleTouchStart: function (e) {
45102 e.preventDefault();
45103 if (e.browserEvent.targetTouches.length === 1) {
45104 // var touch = e.browserEvent.changedTouches[0];
45105 // this.strokeBegin(touch);
45107 this.strokeBegin(e); // assume e catching the correct xy...
45111 _handleTouchMove: function (e) {
45112 e.preventDefault();
45113 // var touch = event.targetTouches[0];
45114 // _this._strokeMoveUpdate(touch);
45115 this.strokeMoveUpdate(e);
45118 _handleTouchEnd: function (e) {
45119 var wasCanvasTouched = e.target === this.canvasEl().dom;
45120 if (wasCanvasTouched) {
45121 e.preventDefault();
45122 // var touch = event.changedTouches[0];
45123 // _this._strokeEnd(touch);
45128 reset: function () {
45129 this._lastPoints = [];
45130 this._lastVelocity = 0;
45131 this._lastWidth = (this.min_width + this.max_width) / 2;
45132 this.canvasElCtx().fillStyle = this.dot_color;
45135 strokeMoveUpdate: function(e)
45137 this.strokeUpdate(e);
45139 if (this.throttle) {
45140 this.throttleStroke(this.strokeUpdate, this.throttle);
45143 this.strokeUpdate(e);
45147 strokeBegin: function(e)
45149 var newPointGroup = {
45150 color: this.dot_color,
45154 if (typeof this.onBegin === 'function') {
45158 this.curve_data.push(newPointGroup);
45160 this.strokeUpdate(e);
45163 strokeUpdate: function(e)
45165 var rect = this.canvasEl().dom.getBoundingClientRect();
45166 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45167 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45168 var lastPoints = lastPointGroup.points;
45169 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45170 var isLastPointTooClose = lastPoint
45171 ? point.distanceTo(lastPoint) <= this.min_distance
45173 var color = lastPointGroup.color;
45174 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45175 var curve = this.addPoint(point);
45177 this.drawDot({color: color, point: point});
45180 this.drawCurve({color: color, curve: curve});
45190 strokeEnd: function(e)
45192 this.strokeUpdate(e);
45193 if (typeof this.onEnd === 'function') {
45198 addPoint: function (point) {
45199 var _lastPoints = this._lastPoints;
45200 _lastPoints.push(point);
45201 if (_lastPoints.length > 2) {
45202 if (_lastPoints.length === 3) {
45203 _lastPoints.unshift(_lastPoints[0]);
45205 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45206 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45207 _lastPoints.shift();
45213 calculateCurveWidths: function (startPoint, endPoint) {
45214 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45215 (1 - this.velocity_filter_weight) * this._lastVelocity;
45217 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45220 start: this._lastWidth
45223 this._lastVelocity = velocity;
45224 this._lastWidth = newWidth;
45228 drawDot: function (_a) {
45229 var color = _a.color, point = _a.point;
45230 var ctx = this.canvasElCtx();
45231 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45233 this.drawCurveSegment(point.x, point.y, width);
45235 ctx.fillStyle = color;
45239 drawCurve: function (_a) {
45240 var color = _a.color, curve = _a.curve;
45241 var ctx = this.canvasElCtx();
45242 var widthDelta = curve.endWidth - curve.startWidth;
45243 var drawSteps = Math.floor(curve.length()) * 2;
45245 ctx.fillStyle = color;
45246 for (var i = 0; i < drawSteps; i += 1) {
45247 var t = i / drawSteps;
45253 var x = uuu * curve.startPoint.x;
45254 x += 3 * uu * t * curve.control1.x;
45255 x += 3 * u * tt * curve.control2.x;
45256 x += ttt * curve.endPoint.x;
45257 var y = uuu * curve.startPoint.y;
45258 y += 3 * uu * t * curve.control1.y;
45259 y += 3 * u * tt * curve.control2.y;
45260 y += ttt * curve.endPoint.y;
45261 var width = curve.startWidth + ttt * widthDelta;
45262 this.drawCurveSegment(x, y, width);
45268 drawCurveSegment: function (x, y, width) {
45269 var ctx = this.canvasElCtx();
45271 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45272 this.is_empty = false;
45277 var ctx = this.canvasElCtx();
45278 var canvas = this.canvasEl().dom;
45279 ctx.fillStyle = this.bg_color;
45280 ctx.clearRect(0, 0, canvas.width, canvas.height);
45281 ctx.fillRect(0, 0, canvas.width, canvas.height);
45282 this.curve_data = [];
45284 this.is_empty = true;
45289 return this.el.select('input',true).first();
45292 canvasEl: function()
45294 return this.el.select('canvas',true).first();
45297 canvasElCtx: function()
45299 return this.el.select('canvas',true).first().dom.getContext('2d');
45302 getImage: function(type)
45304 if(this.is_empty) {
45309 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45312 drawFromImage: function(img_src)
45314 var img = new Image();
45316 img.onload = function(){
45317 this.canvasElCtx().drawImage(img, 0, 0);
45322 this.is_empty = false;
45325 selectImage: function()
45327 this.fileEl().dom.click();
45330 uploadImage: function(e)
45332 var reader = new FileReader();
45334 reader.onload = function(e){
45335 var img = new Image();
45336 img.onload = function(){
45338 this.canvasElCtx().drawImage(img, 0, 0);
45340 img.src = e.target.result;
45343 reader.readAsDataURL(e.target.files[0]);
45346 // Bezier Point Constructor
45347 Point: (function () {
45348 function Point(x, y, time) {
45351 this.time = time || Date.now();
45353 Point.prototype.distanceTo = function (start) {
45354 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45356 Point.prototype.equals = function (other) {
45357 return this.x === other.x && this.y === other.y && this.time === other.time;
45359 Point.prototype.velocityFrom = function (start) {
45360 return this.time !== start.time
45361 ? this.distanceTo(start) / (this.time - start.time)
45368 // Bezier Constructor
45369 Bezier: (function () {
45370 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45371 this.startPoint = startPoint;
45372 this.control2 = control2;
45373 this.control1 = control1;
45374 this.endPoint = endPoint;
45375 this.startWidth = startWidth;
45376 this.endWidth = endWidth;
45378 Bezier.fromPoints = function (points, widths, scope) {
45379 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45380 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45381 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45383 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45384 var dx1 = s1.x - s2.x;
45385 var dy1 = s1.y - s2.y;
45386 var dx2 = s2.x - s3.x;
45387 var dy2 = s2.y - s3.y;
45388 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45389 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45390 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45391 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45392 var dxm = m1.x - m2.x;
45393 var dym = m1.y - m2.y;
45394 var k = l2 / (l1 + l2);
45395 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45396 var tx = s2.x - cm.x;
45397 var ty = s2.y - cm.y;
45399 c1: new scope.Point(m1.x + tx, m1.y + ty),
45400 c2: new scope.Point(m2.x + tx, m2.y + ty)
45403 Bezier.prototype.length = function () {
45408 for (var i = 0; i <= steps; i += 1) {
45410 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45411 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45413 var xdiff = cx - px;
45414 var ydiff = cy - py;
45415 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45422 Bezier.prototype.point = function (t, start, c1, c2, end) {
45423 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45424 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45425 + (3.0 * c2 * (1.0 - t) * t * t)
45426 + (end * t * t * t);
45431 throttleStroke: function(fn, wait) {
45432 if (wait === void 0) { wait = 250; }
45434 var timeout = null;
45438 var later = function () {
45439 previous = Date.now();
45441 result = fn.apply(storedContext, storedArgs);
45443 storedContext = null;
45447 return function wrapper() {
45449 for (var _i = 0; _i < arguments.length; _i++) {
45450 args[_i] = arguments[_i];
45452 var now = Date.now();
45453 var remaining = wait - (now - previous);
45454 storedContext = this;
45456 if (remaining <= 0 || remaining > wait) {
45458 clearTimeout(timeout);
45462 result = fn.apply(storedContext, storedArgs);
45464 storedContext = null;
45468 else if (!timeout) {
45469 timeout = window.setTimeout(later, remaining);