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,
25615 // private properties
25616 validationEvent : false,
25618 initialized : false,
25620 sourceEditMode : false,
25621 onFocus : Roo.emptyFn,
25623 hideMode:'offsets',
25627 // blacklist + whitelisted elements..
25634 * Protected method that will not generally be called directly. It
25635 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25636 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25638 getDocMarkup : function(){
25642 // inherit styels from page...??
25643 if (this.stylesheets === false) {
25645 Roo.get(document.head).select('style').each(function(node) {
25646 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25649 Roo.get(document.head).select('link').each(function(node) {
25650 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25653 } else if (!this.stylesheets.length) {
25655 st = '<style type="text/css">' +
25656 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25659 for (var i in this.stylesheets) {
25660 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25665 st += '<style type="text/css">' +
25666 'IMG { cursor: pointer } ' +
25669 var cls = 'roo-htmleditor-body';
25671 if(this.bodyCls.length){
25672 cls += ' ' + this.bodyCls;
25675 return '<html><head>' + st +
25676 //<style type="text/css">' +
25677 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25679 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25683 onRender : function(ct, position)
25686 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25687 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25690 this.el.dom.style.border = '0 none';
25691 this.el.dom.setAttribute('tabIndex', -1);
25692 this.el.addClass('x-hidden hide');
25696 if(Roo.isIE){ // fix IE 1px bogus margin
25697 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25701 this.frameId = Roo.id();
25705 var iframe = this.owner.wrap.createChild({
25707 cls: 'form-control', // bootstrap..
25709 name: this.frameId,
25710 frameBorder : 'no',
25711 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25716 this.iframe = iframe.dom;
25718 this.assignDocWin();
25720 this.doc.designMode = 'on';
25723 this.doc.write(this.getDocMarkup());
25727 var task = { // must defer to wait for browser to be ready
25729 //console.log("run task?" + this.doc.readyState);
25730 this.assignDocWin();
25731 if(this.doc.body || this.doc.readyState == 'complete'){
25733 this.doc.designMode="on";
25737 Roo.TaskMgr.stop(task);
25738 this.initEditor.defer(10, this);
25745 Roo.TaskMgr.start(task);
25750 onResize : function(w, h)
25752 Roo.log('resize: ' +w + ',' + h );
25753 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25757 if(typeof w == 'number'){
25759 this.iframe.style.width = w + 'px';
25761 if(typeof h == 'number'){
25763 this.iframe.style.height = h + 'px';
25765 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25772 * Toggles the editor between standard and source edit mode.
25773 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25775 toggleSourceEdit : function(sourceEditMode){
25777 this.sourceEditMode = sourceEditMode === true;
25779 if(this.sourceEditMode){
25781 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25784 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25785 //this.iframe.className = '';
25788 //this.setSize(this.owner.wrap.getSize());
25789 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25796 * Protected method that will not generally be called directly. If you need/want
25797 * custom HTML cleanup, this is the method you should override.
25798 * @param {String} html The HTML to be cleaned
25799 * return {String} The cleaned HTML
25801 cleanHtml : function(html){
25802 html = String(html);
25803 if(html.length > 5){
25804 if(Roo.isSafari){ // strip safari nonsense
25805 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25808 if(html == ' '){
25815 * HTML Editor -> Textarea
25816 * Protected method that will not generally be called directly. Syncs the contents
25817 * of the editor iframe with the textarea.
25819 syncValue : function(){
25820 if(this.initialized){
25821 var bd = (this.doc.body || this.doc.documentElement);
25822 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25823 var html = bd.innerHTML;
25825 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25826 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25828 html = '<div style="'+m[0]+'">' + html + '</div>';
25831 html = this.cleanHtml(html);
25832 // fix up the special chars.. normaly like back quotes in word...
25833 // however we do not want to do this with chinese..
25834 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25836 var cc = match.charCodeAt();
25838 // Get the character value, handling surrogate pairs
25839 if (match.length == 2) {
25840 // It's a surrogate pair, calculate the Unicode code point
25841 var high = match.charCodeAt(0) - 0xD800;
25842 var low = match.charCodeAt(1) - 0xDC00;
25843 cc = (high * 0x400) + low + 0x10000;
25845 (cc >= 0x4E00 && cc < 0xA000 ) ||
25846 (cc >= 0x3400 && cc < 0x4E00 ) ||
25847 (cc >= 0xf900 && cc < 0xfb00 )
25852 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25853 return "&#" + cc + ";";
25860 if(this.owner.fireEvent('beforesync', this, html) !== false){
25861 this.el.dom.value = html;
25862 this.owner.fireEvent('sync', this, html);
25868 * Protected method that will not generally be called directly. Pushes the value of the textarea
25869 * into the iframe editor.
25871 pushValue : function(){
25872 if(this.initialized){
25873 var v = this.el.dom.value.trim();
25875 // if(v.length < 1){
25879 if(this.owner.fireEvent('beforepush', this, v) !== false){
25880 var d = (this.doc.body || this.doc.documentElement);
25882 this.cleanUpPaste();
25883 this.el.dom.value = d.innerHTML;
25884 this.owner.fireEvent('push', this, v);
25890 deferFocus : function(){
25891 this.focus.defer(10, this);
25895 focus : function(){
25896 if(this.win && !this.sourceEditMode){
25903 assignDocWin: function()
25905 var iframe = this.iframe;
25908 this.doc = iframe.contentWindow.document;
25909 this.win = iframe.contentWindow;
25911 // if (!Roo.get(this.frameId)) {
25914 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25915 // this.win = Roo.get(this.frameId).dom.contentWindow;
25917 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25921 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25922 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25927 initEditor : function(){
25928 //console.log("INIT EDITOR");
25929 this.assignDocWin();
25933 this.doc.designMode="on";
25935 this.doc.write(this.getDocMarkup());
25938 var dbody = (this.doc.body || this.doc.documentElement);
25939 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25940 // this copies styles from the containing element into thsi one..
25941 // not sure why we need all of this..
25942 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25944 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25945 //ss['background-attachment'] = 'fixed'; // w3c
25946 dbody.bgProperties = 'fixed'; // ie
25947 //Roo.DomHelper.applyStyles(dbody, ss);
25948 Roo.EventManager.on(this.doc, {
25949 //'mousedown': this.onEditorEvent,
25950 'mouseup': this.onEditorEvent,
25951 'dblclick': this.onEditorEvent,
25952 'click': this.onEditorEvent,
25953 'keyup': this.onEditorEvent,
25958 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25960 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25961 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25963 this.initialized = true;
25965 this.owner.fireEvent('initialize', this);
25970 onDestroy : function(){
25976 //for (var i =0; i < this.toolbars.length;i++) {
25977 // // fixme - ask toolbars for heights?
25978 // this.toolbars[i].onDestroy();
25981 //this.wrap.dom.innerHTML = '';
25982 //this.wrap.remove();
25987 onFirstFocus : function(){
25989 this.assignDocWin();
25992 this.activated = true;
25995 if(Roo.isGecko){ // prevent silly gecko errors
25997 var s = this.win.getSelection();
25998 if(!s.focusNode || s.focusNode.nodeType != 3){
25999 var r = s.getRangeAt(0);
26000 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26005 this.execCmd('useCSS', true);
26006 this.execCmd('styleWithCSS', false);
26009 this.owner.fireEvent('activate', this);
26013 adjustFont: function(btn){
26014 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26015 //if(Roo.isSafari){ // safari
26018 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26019 if(Roo.isSafari){ // safari
26020 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26021 v = (v < 10) ? 10 : v;
26022 v = (v > 48) ? 48 : v;
26023 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26028 v = Math.max(1, v+adjust);
26030 this.execCmd('FontSize', v );
26033 onEditorEvent : function(e)
26035 this.owner.fireEvent('editorevent', this, e);
26036 // this.updateToolbar();
26037 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26040 insertTag : function(tg)
26042 // could be a bit smarter... -> wrap the current selected tRoo..
26043 if (tg.toLowerCase() == 'span' ||
26044 tg.toLowerCase() == 'code' ||
26045 tg.toLowerCase() == 'sup' ||
26046 tg.toLowerCase() == 'sub'
26049 range = this.createRange(this.getSelection());
26050 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26051 wrappingNode.appendChild(range.extractContents());
26052 range.insertNode(wrappingNode);
26059 this.execCmd("formatblock", tg);
26063 insertText : function(txt)
26067 var range = this.createRange();
26068 range.deleteContents();
26069 //alert(Sender.getAttribute('label'));
26071 range.insertNode(this.doc.createTextNode(txt));
26077 * Executes a Midas editor command on the editor document and performs necessary focus and
26078 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26079 * @param {String} cmd The Midas command
26080 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26082 relayCmd : function(cmd, value){
26084 this.execCmd(cmd, value);
26085 this.owner.fireEvent('editorevent', this);
26086 //this.updateToolbar();
26087 this.owner.deferFocus();
26091 * Executes a Midas editor command directly on the editor document.
26092 * For visual commands, you should use {@link #relayCmd} instead.
26093 * <b>This should only be called after the editor is initialized.</b>
26094 * @param {String} cmd The Midas command
26095 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26097 execCmd : function(cmd, value){
26098 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26105 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26107 * @param {String} text | dom node..
26109 insertAtCursor : function(text)
26112 if(!this.activated){
26118 var r = this.doc.selection.createRange();
26129 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26133 // from jquery ui (MIT licenced)
26135 var win = this.win;
26137 if (win.getSelection && win.getSelection().getRangeAt) {
26138 range = win.getSelection().getRangeAt(0);
26139 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26140 range.insertNode(node);
26141 } else if (win.document.selection && win.document.selection.createRange) {
26142 // no firefox support
26143 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26144 win.document.selection.createRange().pasteHTML(txt);
26146 // no firefox support
26147 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26148 this.execCmd('InsertHTML', txt);
26157 mozKeyPress : function(e){
26159 var c = e.getCharCode(), cmd;
26162 c = String.fromCharCode(c).toLowerCase();
26176 this.cleanUpPaste.defer(100, this);
26184 e.preventDefault();
26192 fixKeys : function(){ // load time branching for fastest keydown performance
26194 return function(e){
26195 var k = e.getKey(), r;
26198 r = this.doc.selection.createRange();
26201 r.pasteHTML('    ');
26208 r = this.doc.selection.createRange();
26210 var target = r.parentElement();
26211 if(!target || target.tagName.toLowerCase() != 'li'){
26213 r.pasteHTML('<br />');
26219 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26220 this.cleanUpPaste.defer(100, this);
26226 }else if(Roo.isOpera){
26227 return function(e){
26228 var k = e.getKey();
26232 this.execCmd('InsertHTML','    ');
26235 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26236 this.cleanUpPaste.defer(100, this);
26241 }else if(Roo.isSafari){
26242 return function(e){
26243 var k = e.getKey();
26247 this.execCmd('InsertText','\t');
26251 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26252 this.cleanUpPaste.defer(100, this);
26260 getAllAncestors: function()
26262 var p = this.getSelectedNode();
26265 a.push(p); // push blank onto stack..
26266 p = this.getParentElement();
26270 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26274 a.push(this.doc.body);
26278 lastSelNode : false,
26281 getSelection : function()
26283 this.assignDocWin();
26284 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26287 getSelectedNode: function()
26289 // this may only work on Gecko!!!
26291 // should we cache this!!!!
26296 var range = this.createRange(this.getSelection()).cloneRange();
26299 var parent = range.parentElement();
26301 var testRange = range.duplicate();
26302 testRange.moveToElementText(parent);
26303 if (testRange.inRange(range)) {
26306 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26309 parent = parent.parentElement;
26314 // is ancestor a text element.
26315 var ac = range.commonAncestorContainer;
26316 if (ac.nodeType == 3) {
26317 ac = ac.parentNode;
26320 var ar = ac.childNodes;
26323 var other_nodes = [];
26324 var has_other_nodes = false;
26325 for (var i=0;i<ar.length;i++) {
26326 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26329 // fullly contained node.
26331 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26336 // probably selected..
26337 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26338 other_nodes.push(ar[i]);
26342 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26347 has_other_nodes = true;
26349 if (!nodes.length && other_nodes.length) {
26350 nodes= other_nodes;
26352 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26358 createRange: function(sel)
26360 // this has strange effects when using with
26361 // top toolbar - not sure if it's a great idea.
26362 //this.editor.contentWindow.focus();
26363 if (typeof sel != "undefined") {
26365 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26367 return this.doc.createRange();
26370 return this.doc.createRange();
26373 getParentElement: function()
26376 this.assignDocWin();
26377 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26379 var range = this.createRange(sel);
26382 var p = range.commonAncestorContainer;
26383 while (p.nodeType == 3) { // text node
26394 * Range intersection.. the hard stuff...
26398 * [ -- selected range --- ]
26402 * if end is before start or hits it. fail.
26403 * if start is after end or hits it fail.
26405 * if either hits (but other is outside. - then it's not
26411 // @see http://www.thismuchiknow.co.uk/?p=64.
26412 rangeIntersectsNode : function(range, node)
26414 var nodeRange = node.ownerDocument.createRange();
26416 nodeRange.selectNode(node);
26418 nodeRange.selectNodeContents(node);
26421 var rangeStartRange = range.cloneRange();
26422 rangeStartRange.collapse(true);
26424 var rangeEndRange = range.cloneRange();
26425 rangeEndRange.collapse(false);
26427 var nodeStartRange = nodeRange.cloneRange();
26428 nodeStartRange.collapse(true);
26430 var nodeEndRange = nodeRange.cloneRange();
26431 nodeEndRange.collapse(false);
26433 return rangeStartRange.compareBoundaryPoints(
26434 Range.START_TO_START, nodeEndRange) == -1 &&
26435 rangeEndRange.compareBoundaryPoints(
26436 Range.START_TO_START, nodeStartRange) == 1;
26440 rangeCompareNode : function(range, node)
26442 var nodeRange = node.ownerDocument.createRange();
26444 nodeRange.selectNode(node);
26446 nodeRange.selectNodeContents(node);
26450 range.collapse(true);
26452 nodeRange.collapse(true);
26454 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26455 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26457 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26459 var nodeIsBefore = ss == 1;
26460 var nodeIsAfter = ee == -1;
26462 if (nodeIsBefore && nodeIsAfter) {
26465 if (!nodeIsBefore && nodeIsAfter) {
26466 return 1; //right trailed.
26469 if (nodeIsBefore && !nodeIsAfter) {
26470 return 2; // left trailed.
26476 // private? - in a new class?
26477 cleanUpPaste : function()
26479 // cleans up the whole document..
26480 Roo.log('cleanuppaste');
26482 this.cleanUpChildren(this.doc.body);
26483 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26484 if (clean != this.doc.body.innerHTML) {
26485 this.doc.body.innerHTML = clean;
26490 cleanWordChars : function(input) {// change the chars to hex code
26491 var he = Roo.HtmlEditorCore;
26493 var output = input;
26494 Roo.each(he.swapCodes, function(sw) {
26495 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26497 output = output.replace(swapper, sw[1]);
26504 cleanUpChildren : function (n)
26506 if (!n.childNodes.length) {
26509 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26510 this.cleanUpChild(n.childNodes[i]);
26517 cleanUpChild : function (node)
26520 //console.log(node);
26521 if (node.nodeName == "#text") {
26522 // clean up silly Windows -- stuff?
26525 if (node.nodeName == "#comment") {
26526 node.parentNode.removeChild(node);
26527 // clean up silly Windows -- stuff?
26530 var lcname = node.tagName.toLowerCase();
26531 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26532 // whitelist of tags..
26534 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26536 node.parentNode.removeChild(node);
26541 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26543 // spans with no attributes - just remove them..
26544 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26545 remove_keep_children = true;
26548 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26549 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26551 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26552 // remove_keep_children = true;
26555 if (remove_keep_children) {
26556 this.cleanUpChildren(node);
26557 // inserts everything just before this node...
26558 while (node.childNodes.length) {
26559 var cn = node.childNodes[0];
26560 node.removeChild(cn);
26561 node.parentNode.insertBefore(cn, node);
26563 node.parentNode.removeChild(node);
26567 if (!node.attributes || !node.attributes.length) {
26572 this.cleanUpChildren(node);
26576 function cleanAttr(n,v)
26579 if (v.match(/^\./) || v.match(/^\//)) {
26582 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26585 if (v.match(/^#/)) {
26588 if (v.match(/^\{/)) { // allow template editing.
26591 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26592 node.removeAttribute(n);
26596 var cwhite = this.cwhite;
26597 var cblack = this.cblack;
26599 function cleanStyle(n,v)
26601 if (v.match(/expression/)) { //XSS?? should we even bother..
26602 node.removeAttribute(n);
26606 var parts = v.split(/;/);
26609 Roo.each(parts, function(p) {
26610 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26614 var l = p.split(':').shift().replace(/\s+/g,'');
26615 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26617 if ( cwhite.length && cblack.indexOf(l) > -1) {
26618 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26619 //node.removeAttribute(n);
26623 // only allow 'c whitelisted system attributes'
26624 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26625 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26626 //node.removeAttribute(n);
26636 if (clean.length) {
26637 node.setAttribute(n, clean.join(';'));
26639 node.removeAttribute(n);
26645 for (var i = node.attributes.length-1; i > -1 ; i--) {
26646 var a = node.attributes[i];
26649 if (a.name.toLowerCase().substr(0,2)=='on') {
26650 node.removeAttribute(a.name);
26653 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26654 node.removeAttribute(a.name);
26657 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26658 cleanAttr(a.name,a.value); // fixme..
26661 if (a.name == 'style') {
26662 cleanStyle(a.name,a.value);
26665 /// clean up MS crap..
26666 // tecnically this should be a list of valid class'es..
26669 if (a.name == 'class') {
26670 if (a.value.match(/^Mso/)) {
26671 node.removeAttribute('class');
26674 if (a.value.match(/^body$/)) {
26675 node.removeAttribute('class');
26686 this.cleanUpChildren(node);
26692 * Clean up MS wordisms...
26694 cleanWord : function(node)
26697 this.cleanWord(this.doc.body);
26702 node.nodeName == 'SPAN' &&
26703 !node.hasAttributes() &&
26704 node.childNodes.length == 1 &&
26705 node.firstChild.nodeName == "#text"
26707 var textNode = node.firstChild;
26708 node.removeChild(textNode);
26709 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26710 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26712 node.parentNode.insertBefore(textNode, node);
26713 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26714 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26716 node.parentNode.removeChild(node);
26719 if (node.nodeName == "#text") {
26720 // clean up silly Windows -- stuff?
26723 if (node.nodeName == "#comment") {
26724 node.parentNode.removeChild(node);
26725 // clean up silly Windows -- stuff?
26729 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26730 node.parentNode.removeChild(node);
26733 //Roo.log(node.tagName);
26734 // remove - but keep children..
26735 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26736 //Roo.log('-- removed');
26737 while (node.childNodes.length) {
26738 var cn = node.childNodes[0];
26739 node.removeChild(cn);
26740 node.parentNode.insertBefore(cn, node);
26741 // move node to parent - and clean it..
26742 this.cleanWord(cn);
26744 node.parentNode.removeChild(node);
26745 /// no need to iterate chidlren = it's got none..
26746 //this.iterateChildren(node, this.cleanWord);
26750 if (node.className.length) {
26752 var cn = node.className.split(/\W+/);
26754 Roo.each(cn, function(cls) {
26755 if (cls.match(/Mso[a-zA-Z]+/)) {
26760 node.className = cna.length ? cna.join(' ') : '';
26762 node.removeAttribute("class");
26766 if (node.hasAttribute("lang")) {
26767 node.removeAttribute("lang");
26770 if (node.hasAttribute("style")) {
26772 var styles = node.getAttribute("style").split(";");
26774 Roo.each(styles, function(s) {
26775 if (!s.match(/:/)) {
26778 var kv = s.split(":");
26779 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26782 // what ever is left... we allow.
26785 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26786 if (!nstyle.length) {
26787 node.removeAttribute('style');
26790 this.iterateChildren(node, this.cleanWord);
26796 * iterateChildren of a Node, calling fn each time, using this as the scole..
26797 * @param {DomNode} node node to iterate children of.
26798 * @param {Function} fn method of this class to call on each item.
26800 iterateChildren : function(node, fn)
26802 if (!node.childNodes.length) {
26805 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26806 fn.call(this, node.childNodes[i])
26812 * cleanTableWidths.
26814 * Quite often pasting from word etc.. results in tables with column and widths.
26815 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26818 cleanTableWidths : function(node)
26823 this.cleanTableWidths(this.doc.body);
26828 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26831 Roo.log(node.tagName);
26832 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26833 this.iterateChildren(node, this.cleanTableWidths);
26836 if (node.hasAttribute('width')) {
26837 node.removeAttribute('width');
26841 if (node.hasAttribute("style")) {
26844 var styles = node.getAttribute("style").split(";");
26846 Roo.each(styles, function(s) {
26847 if (!s.match(/:/)) {
26850 var kv = s.split(":");
26851 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26854 // what ever is left... we allow.
26857 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26858 if (!nstyle.length) {
26859 node.removeAttribute('style');
26863 this.iterateChildren(node, this.cleanTableWidths);
26871 domToHTML : function(currentElement, depth, nopadtext) {
26873 depth = depth || 0;
26874 nopadtext = nopadtext || false;
26876 if (!currentElement) {
26877 return this.domToHTML(this.doc.body);
26880 //Roo.log(currentElement);
26882 var allText = false;
26883 var nodeName = currentElement.nodeName;
26884 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26886 if (nodeName == '#text') {
26888 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26893 if (nodeName != 'BODY') {
26896 // Prints the node tagName, such as <A>, <IMG>, etc
26899 for(i = 0; i < currentElement.attributes.length;i++) {
26901 var aname = currentElement.attributes.item(i).name;
26902 if (!currentElement.attributes.item(i).value.length) {
26905 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26908 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26917 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26920 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26925 // Traverse the tree
26927 var currentElementChild = currentElement.childNodes.item(i);
26928 var allText = true;
26929 var innerHTML = '';
26931 while (currentElementChild) {
26932 // Formatting code (indent the tree so it looks nice on the screen)
26933 var nopad = nopadtext;
26934 if (lastnode == 'SPAN') {
26938 if (currentElementChild.nodeName == '#text') {
26939 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26940 toadd = nopadtext ? toadd : toadd.trim();
26941 if (!nopad && toadd.length > 80) {
26942 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26944 innerHTML += toadd;
26947 currentElementChild = currentElement.childNodes.item(i);
26953 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26955 // Recursively traverse the tree structure of the child node
26956 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26957 lastnode = currentElementChild.nodeName;
26959 currentElementChild=currentElement.childNodes.item(i);
26965 // The remaining code is mostly for formatting the tree
26966 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26971 ret+= "</"+tagName+">";
26977 applyBlacklists : function()
26979 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26980 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26984 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26985 if (b.indexOf(tag) > -1) {
26988 this.white.push(tag);
26992 Roo.each(w, function(tag) {
26993 if (b.indexOf(tag) > -1) {
26996 if (this.white.indexOf(tag) > -1) {
26999 this.white.push(tag);
27004 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27005 if (w.indexOf(tag) > -1) {
27008 this.black.push(tag);
27012 Roo.each(b, function(tag) {
27013 if (w.indexOf(tag) > -1) {
27016 if (this.black.indexOf(tag) > -1) {
27019 this.black.push(tag);
27024 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27025 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27029 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27030 if (b.indexOf(tag) > -1) {
27033 this.cwhite.push(tag);
27037 Roo.each(w, function(tag) {
27038 if (b.indexOf(tag) > -1) {
27041 if (this.cwhite.indexOf(tag) > -1) {
27044 this.cwhite.push(tag);
27049 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27050 if (w.indexOf(tag) > -1) {
27053 this.cblack.push(tag);
27057 Roo.each(b, function(tag) {
27058 if (w.indexOf(tag) > -1) {
27061 if (this.cblack.indexOf(tag) > -1) {
27064 this.cblack.push(tag);
27069 setStylesheets : function(stylesheets)
27071 if(typeof(stylesheets) == 'string'){
27072 Roo.get(this.iframe.contentDocument.head).createChild({
27074 rel : 'stylesheet',
27083 Roo.each(stylesheets, function(s) {
27088 Roo.get(_this.iframe.contentDocument.head).createChild({
27090 rel : 'stylesheet',
27099 removeStylesheets : function()
27103 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27108 setStyle : function(style)
27110 Roo.get(this.iframe.contentDocument.head).createChild({
27119 // hide stuff that is not compatible
27133 * @event specialkey
27137 * @cfg {String} fieldClass @hide
27140 * @cfg {String} focusClass @hide
27143 * @cfg {String} autoCreate @hide
27146 * @cfg {String} inputType @hide
27149 * @cfg {String} invalidClass @hide
27152 * @cfg {String} invalidText @hide
27155 * @cfg {String} msgFx @hide
27158 * @cfg {String} validateOnBlur @hide
27162 Roo.HtmlEditorCore.white = [
27163 'area', 'br', 'img', 'input', 'hr', 'wbr',
27165 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27166 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27167 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27168 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27169 'table', 'ul', 'xmp',
27171 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27174 'dir', 'menu', 'ol', 'ul', 'dl',
27180 Roo.HtmlEditorCore.black = [
27181 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27183 'base', 'basefont', 'bgsound', 'blink', 'body',
27184 'frame', 'frameset', 'head', 'html', 'ilayer',
27185 'iframe', 'layer', 'link', 'meta', 'object',
27186 'script', 'style' ,'title', 'xml' // clean later..
27188 Roo.HtmlEditorCore.clean = [
27189 'script', 'style', 'title', 'xml'
27191 Roo.HtmlEditorCore.remove = [
27196 Roo.HtmlEditorCore.ablack = [
27200 Roo.HtmlEditorCore.aclean = [
27201 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27205 Roo.HtmlEditorCore.pwhite= [
27206 'http', 'https', 'mailto'
27209 // white listed style attributes.
27210 Roo.HtmlEditorCore.cwhite= [
27211 // 'text-align', /// default is to allow most things..
27217 // black listed style attributes.
27218 Roo.HtmlEditorCore.cblack= [
27219 // 'font-size' -- this can be set by the project
27223 Roo.HtmlEditorCore.swapCodes =[
27224 [ 8211, "–" ],
27225 [ 8212, "—" ],
27242 * @class Roo.bootstrap.HtmlEditor
27243 * @extends Roo.bootstrap.TextArea
27244 * Bootstrap HtmlEditor class
27247 * Create a new HtmlEditor
27248 * @param {Object} config The config object
27251 Roo.bootstrap.HtmlEditor = function(config){
27252 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27253 if (!this.toolbars) {
27254 this.toolbars = [];
27257 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27260 * @event initialize
27261 * Fires when the editor is fully initialized (including the iframe)
27262 * @param {HtmlEditor} this
27267 * Fires when the editor is first receives the focus. Any insertion must wait
27268 * until after this event.
27269 * @param {HtmlEditor} this
27273 * @event beforesync
27274 * Fires before the textarea is updated with content from the editor iframe. Return false
27275 * to cancel the sync.
27276 * @param {HtmlEditor} this
27277 * @param {String} html
27281 * @event beforepush
27282 * Fires before the iframe editor is updated with content from the textarea. Return false
27283 * to cancel the push.
27284 * @param {HtmlEditor} this
27285 * @param {String} html
27290 * Fires when the textarea is updated with content from the editor iframe.
27291 * @param {HtmlEditor} this
27292 * @param {String} html
27297 * Fires when the iframe editor is updated with content from the textarea.
27298 * @param {HtmlEditor} this
27299 * @param {String} html
27303 * @event editmodechange
27304 * Fires when the editor switches edit modes
27305 * @param {HtmlEditor} this
27306 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27308 editmodechange: true,
27310 * @event editorevent
27311 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27312 * @param {HtmlEditor} this
27316 * @event firstfocus
27317 * Fires when on first focus - needed by toolbars..
27318 * @param {HtmlEditor} this
27323 * Auto save the htmlEditor value as a file into Events
27324 * @param {HtmlEditor} this
27328 * @event savedpreview
27329 * preview the saved version of htmlEditor
27330 * @param {HtmlEditor} this
27337 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27341 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27346 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27351 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27356 * @cfg {Number} height (in pixels)
27360 * @cfg {Number} width (in pixels)
27365 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27368 stylesheets: false,
27373 // private properties
27374 validationEvent : false,
27376 initialized : false,
27379 onFocus : Roo.emptyFn,
27381 hideMode:'offsets',
27383 tbContainer : false,
27387 toolbarContainer :function() {
27388 return this.wrap.select('.x-html-editor-tb',true).first();
27392 * Protected method that will not generally be called directly. It
27393 * is called when the editor creates its toolbar. Override this method if you need to
27394 * add custom toolbar buttons.
27395 * @param {HtmlEditor} editor
27397 createToolbar : function(){
27398 Roo.log('renewing');
27399 Roo.log("create toolbars");
27401 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27402 this.toolbars[0].render(this.toolbarContainer());
27406 // if (!editor.toolbars || !editor.toolbars.length) {
27407 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27410 // for (var i =0 ; i < editor.toolbars.length;i++) {
27411 // editor.toolbars[i] = Roo.factory(
27412 // typeof(editor.toolbars[i]) == 'string' ?
27413 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27414 // Roo.bootstrap.HtmlEditor);
27415 // editor.toolbars[i].init(editor);
27421 onRender : function(ct, position)
27423 // Roo.log("Call onRender: " + this.xtype);
27425 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27427 this.wrap = this.inputEl().wrap({
27428 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27431 this.editorcore.onRender(ct, position);
27433 if (this.resizable) {
27434 this.resizeEl = new Roo.Resizable(this.wrap, {
27438 minHeight : this.height,
27439 height: this.height,
27440 handles : this.resizable,
27443 resize : function(r, w, h) {
27444 _t.onResize(w,h); // -something
27450 this.createToolbar(this);
27453 if(!this.width && this.resizable){
27454 this.setSize(this.wrap.getSize());
27456 if (this.resizeEl) {
27457 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27458 // should trigger onReize..
27464 onResize : function(w, h)
27466 Roo.log('resize: ' +w + ',' + h );
27467 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27471 if(this.inputEl() ){
27472 if(typeof w == 'number'){
27473 var aw = w - this.wrap.getFrameWidth('lr');
27474 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27477 if(typeof h == 'number'){
27478 var tbh = -11; // fixme it needs to tool bar size!
27479 for (var i =0; i < this.toolbars.length;i++) {
27480 // fixme - ask toolbars for heights?
27481 tbh += this.toolbars[i].el.getHeight();
27482 //if (this.toolbars[i].footer) {
27483 // tbh += this.toolbars[i].footer.el.getHeight();
27491 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27492 ah -= 5; // knock a few pixes off for look..
27493 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27497 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27498 this.editorcore.onResize(ew,eh);
27503 * Toggles the editor between standard and source edit mode.
27504 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27506 toggleSourceEdit : function(sourceEditMode)
27508 this.editorcore.toggleSourceEdit(sourceEditMode);
27510 if(this.editorcore.sourceEditMode){
27511 Roo.log('editor - showing textarea');
27514 // Roo.log(this.syncValue());
27516 this.inputEl().removeClass(['hide', 'x-hidden']);
27517 this.inputEl().dom.removeAttribute('tabIndex');
27518 this.inputEl().focus();
27520 Roo.log('editor - hiding textarea');
27522 // Roo.log(this.pushValue());
27525 this.inputEl().addClass(['hide', 'x-hidden']);
27526 this.inputEl().dom.setAttribute('tabIndex', -1);
27527 //this.deferFocus();
27530 if(this.resizable){
27531 this.setSize(this.wrap.getSize());
27534 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27537 // private (for BoxComponent)
27538 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27540 // private (for BoxComponent)
27541 getResizeEl : function(){
27545 // private (for BoxComponent)
27546 getPositionEl : function(){
27551 initEvents : function(){
27552 this.originalValue = this.getValue();
27556 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27559 // markInvalid : Roo.emptyFn,
27561 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27564 // clearInvalid : Roo.emptyFn,
27566 setValue : function(v){
27567 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27568 this.editorcore.pushValue();
27573 deferFocus : function(){
27574 this.focus.defer(10, this);
27578 focus : function(){
27579 this.editorcore.focus();
27585 onDestroy : function(){
27591 for (var i =0; i < this.toolbars.length;i++) {
27592 // fixme - ask toolbars for heights?
27593 this.toolbars[i].onDestroy();
27596 this.wrap.dom.innerHTML = '';
27597 this.wrap.remove();
27602 onFirstFocus : function(){
27603 //Roo.log("onFirstFocus");
27604 this.editorcore.onFirstFocus();
27605 for (var i =0; i < this.toolbars.length;i++) {
27606 this.toolbars[i].onFirstFocus();
27612 syncValue : function()
27614 this.editorcore.syncValue();
27617 pushValue : function()
27619 this.editorcore.pushValue();
27623 // hide stuff that is not compatible
27637 * @event specialkey
27641 * @cfg {String} fieldClass @hide
27644 * @cfg {String} focusClass @hide
27647 * @cfg {String} autoCreate @hide
27650 * @cfg {String} inputType @hide
27654 * @cfg {String} invalidText @hide
27657 * @cfg {String} msgFx @hide
27660 * @cfg {String} validateOnBlur @hide
27669 Roo.namespace('Roo.bootstrap.htmleditor');
27671 * @class Roo.bootstrap.HtmlEditorToolbar1
27677 new Roo.bootstrap.HtmlEditor({
27680 new Roo.bootstrap.HtmlEditorToolbar1({
27681 disable : { fonts: 1 , format: 1, ..., ... , ...],
27687 * @cfg {Object} disable List of elements to disable..
27688 * @cfg {Array} btns List of additional buttons.
27692 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27695 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27698 Roo.apply(this, config);
27700 // default disabled, based on 'good practice'..
27701 this.disable = this.disable || {};
27702 Roo.applyIf(this.disable, {
27705 specialElements : true
27707 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27709 this.editor = config.editor;
27710 this.editorcore = config.editor.editorcore;
27712 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27714 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27715 // dont call parent... till later.
27717 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27722 editorcore : false,
27727 "h1","h2","h3","h4","h5","h6",
27729 "abbr", "acronym", "address", "cite", "samp", "var",
27733 onRender : function(ct, position)
27735 // Roo.log("Call onRender: " + this.xtype);
27737 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27739 this.el.dom.style.marginBottom = '0';
27741 var editorcore = this.editorcore;
27742 var editor= this.editor;
27745 var btn = function(id,cmd , toggle, handler, html){
27747 var event = toggle ? 'toggle' : 'click';
27752 xns: Roo.bootstrap,
27756 enableToggle:toggle !== false,
27758 pressed : toggle ? false : null,
27761 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27762 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27768 // var cb_box = function...
27773 xns: Roo.bootstrap,
27778 xns: Roo.bootstrap,
27782 Roo.each(this.formats, function(f) {
27783 style.menu.items.push({
27785 xns: Roo.bootstrap,
27786 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27791 editorcore.insertTag(this.tagname);
27798 children.push(style);
27800 btn('bold',false,true);
27801 btn('italic',false,true);
27802 btn('align-left', 'justifyleft',true);
27803 btn('align-center', 'justifycenter',true);
27804 btn('align-right' , 'justifyright',true);
27805 btn('link', false, false, function(btn) {
27806 //Roo.log("create link?");
27807 var url = prompt(this.createLinkText, this.defaultLinkValue);
27808 if(url && url != 'http:/'+'/'){
27809 this.editorcore.relayCmd('createlink', url);
27812 btn('list','insertunorderedlist',true);
27813 btn('pencil', false,true, function(btn){
27815 this.toggleSourceEdit(btn.pressed);
27818 if (this.editor.btns.length > 0) {
27819 for (var i = 0; i<this.editor.btns.length; i++) {
27820 children.push(this.editor.btns[i]);
27828 xns: Roo.bootstrap,
27833 xns: Roo.bootstrap,
27838 cog.menu.items.push({
27840 xns: Roo.bootstrap,
27841 html : Clean styles,
27846 editorcore.insertTag(this.tagname);
27855 this.xtype = 'NavSimplebar';
27857 for(var i=0;i< children.length;i++) {
27859 this.buttons.add(this.addxtypeChild(children[i]));
27863 editor.on('editorevent', this.updateToolbar, this);
27865 onBtnClick : function(id)
27867 this.editorcore.relayCmd(id);
27868 this.editorcore.focus();
27872 * Protected method that will not generally be called directly. It triggers
27873 * a toolbar update by reading the markup state of the current selection in the editor.
27875 updateToolbar: function(){
27877 if(!this.editorcore.activated){
27878 this.editor.onFirstFocus(); // is this neeed?
27882 var btns = this.buttons;
27883 var doc = this.editorcore.doc;
27884 btns.get('bold').setActive(doc.queryCommandState('bold'));
27885 btns.get('italic').setActive(doc.queryCommandState('italic'));
27886 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27888 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27889 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27890 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27892 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27893 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27896 var ans = this.editorcore.getAllAncestors();
27897 if (this.formatCombo) {
27900 var store = this.formatCombo.store;
27901 this.formatCombo.setValue("");
27902 for (var i =0; i < ans.length;i++) {
27903 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27905 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27913 // hides menus... - so this cant be on a menu...
27914 Roo.bootstrap.MenuMgr.hideAll();
27916 Roo.bootstrap.MenuMgr.hideAll();
27917 //this.editorsyncValue();
27919 onFirstFocus: function() {
27920 this.buttons.each(function(item){
27924 toggleSourceEdit : function(sourceEditMode){
27927 if(sourceEditMode){
27928 Roo.log("disabling buttons");
27929 this.buttons.each( function(item){
27930 if(item.cmd != 'pencil'){
27936 Roo.log("enabling buttons");
27937 if(this.editorcore.initialized){
27938 this.buttons.each( function(item){
27944 Roo.log("calling toggole on editor");
27945 // tell the editor that it's been pressed..
27946 this.editor.toggleSourceEdit(sourceEditMode);
27960 * @class Roo.bootstrap.Markdown
27961 * @extends Roo.bootstrap.TextArea
27962 * Bootstrap Showdown editable area
27963 * @cfg {string} content
27966 * Create a new Showdown
27969 Roo.bootstrap.Markdown = function(config){
27970 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27974 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27978 initEvents : function()
27981 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27982 this.markdownEl = this.el.createChild({
27983 cls : 'roo-markdown-area'
27985 this.inputEl().addClass('d-none');
27986 if (this.getValue() == '') {
27987 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27990 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27992 this.markdownEl.on('click', this.toggleTextEdit, this);
27993 this.on('blur', this.toggleTextEdit, this);
27994 this.on('specialkey', this.resizeTextArea, this);
27997 toggleTextEdit : function()
27999 var sh = this.markdownEl.getHeight();
28000 this.inputEl().addClass('d-none');
28001 this.markdownEl.addClass('d-none');
28002 if (!this.editing) {
28004 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28005 this.inputEl().removeClass('d-none');
28006 this.inputEl().focus();
28007 this.editing = true;
28010 // show showdown...
28011 this.updateMarkdown();
28012 this.markdownEl.removeClass('d-none');
28013 this.editing = false;
28016 updateMarkdown : function()
28018 if (this.getValue() == '') {
28019 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28023 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28026 resizeTextArea: function () {
28029 Roo.log([sh, this.getValue().split("\n").length * 30]);
28030 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28032 setValue : function(val)
28034 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28035 if (!this.editing) {
28036 this.updateMarkdown();
28042 if (!this.editing) {
28043 this.toggleTextEdit();
28051 * Ext JS Library 1.1.1
28052 * Copyright(c) 2006-2007, Ext JS, LLC.
28054 * Originally Released Under LGPL - original licence link has changed is not relivant.
28057 * <script type="text/javascript">
28061 * @class Roo.bootstrap.PagingToolbar
28062 * @extends Roo.bootstrap.NavSimplebar
28063 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28065 * Create a new PagingToolbar
28066 * @param {Object} config The config object
28067 * @param {Roo.data.Store} store
28069 Roo.bootstrap.PagingToolbar = function(config)
28071 // old args format still supported... - xtype is prefered..
28072 // created from xtype...
28074 this.ds = config.dataSource;
28076 if (config.store && !this.ds) {
28077 this.store= Roo.factory(config.store, Roo.data);
28078 this.ds = this.store;
28079 this.ds.xmodule = this.xmodule || false;
28082 this.toolbarItems = [];
28083 if (config.items) {
28084 this.toolbarItems = config.items;
28087 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28092 this.bind(this.ds);
28095 if (Roo.bootstrap.version == 4) {
28096 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28098 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28103 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28105 * @cfg {Roo.data.Store} dataSource
28106 * The underlying data store providing the paged data
28109 * @cfg {String/HTMLElement/Element} container
28110 * container The id or element that will contain the toolbar
28113 * @cfg {Boolean} displayInfo
28114 * True to display the displayMsg (defaults to false)
28117 * @cfg {Number} pageSize
28118 * The number of records to display per page (defaults to 20)
28122 * @cfg {String} displayMsg
28123 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28125 displayMsg : 'Displaying {0} - {1} of {2}',
28127 * @cfg {String} emptyMsg
28128 * The message to display when no records are found (defaults to "No data to display")
28130 emptyMsg : 'No data to display',
28132 * Customizable piece of the default paging text (defaults to "Page")
28135 beforePageText : "Page",
28137 * Customizable piece of the default paging text (defaults to "of %0")
28140 afterPageText : "of {0}",
28142 * Customizable piece of the default paging text (defaults to "First Page")
28145 firstText : "First Page",
28147 * Customizable piece of the default paging text (defaults to "Previous Page")
28150 prevText : "Previous Page",
28152 * Customizable piece of the default paging text (defaults to "Next Page")
28155 nextText : "Next Page",
28157 * Customizable piece of the default paging text (defaults to "Last Page")
28160 lastText : "Last Page",
28162 * Customizable piece of the default paging text (defaults to "Refresh")
28165 refreshText : "Refresh",
28169 onRender : function(ct, position)
28171 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28172 this.navgroup.parentId = this.id;
28173 this.navgroup.onRender(this.el, null);
28174 // add the buttons to the navgroup
28176 if(this.displayInfo){
28177 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28178 this.displayEl = this.el.select('.x-paging-info', true).first();
28179 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28180 // this.displayEl = navel.el.select('span',true).first();
28186 Roo.each(_this.buttons, function(e){ // this might need to use render????
28187 Roo.factory(e).render(_this.el);
28191 Roo.each(_this.toolbarItems, function(e) {
28192 _this.navgroup.addItem(e);
28196 this.first = this.navgroup.addItem({
28197 tooltip: this.firstText,
28198 cls: "prev btn-outline-secondary",
28199 html : ' <i class="fa fa-step-backward"></i>',
28201 preventDefault: true,
28202 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28205 this.prev = this.navgroup.addItem({
28206 tooltip: this.prevText,
28207 cls: "prev btn-outline-secondary",
28208 html : ' <i class="fa fa-backward"></i>',
28210 preventDefault: true,
28211 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28213 //this.addSeparator();
28216 var field = this.navgroup.addItem( {
28218 cls : 'x-paging-position btn-outline-secondary',
28220 html : this.beforePageText +
28221 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28222 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28225 this.field = field.el.select('input', true).first();
28226 this.field.on("keydown", this.onPagingKeydown, this);
28227 this.field.on("focus", function(){this.dom.select();});
28230 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28231 //this.field.setHeight(18);
28232 //this.addSeparator();
28233 this.next = this.navgroup.addItem({
28234 tooltip: this.nextText,
28235 cls: "next btn-outline-secondary",
28236 html : ' <i class="fa fa-forward"></i>',
28238 preventDefault: true,
28239 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28241 this.last = this.navgroup.addItem({
28242 tooltip: this.lastText,
28243 html : ' <i class="fa fa-step-forward"></i>',
28244 cls: "next btn-outline-secondary",
28246 preventDefault: true,
28247 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28249 //this.addSeparator();
28250 this.loading = this.navgroup.addItem({
28251 tooltip: this.refreshText,
28252 cls: "btn-outline-secondary",
28253 html : ' <i class="fa fa-refresh"></i>',
28254 preventDefault: true,
28255 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28261 updateInfo : function(){
28262 if(this.displayEl){
28263 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28264 var msg = count == 0 ?
28268 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28270 this.displayEl.update(msg);
28275 onLoad : function(ds, r, o)
28277 this.cursor = o.params && o.params.start ? o.params.start : 0;
28279 var d = this.getPageData(),
28284 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28285 this.field.dom.value = ap;
28286 this.first.setDisabled(ap == 1);
28287 this.prev.setDisabled(ap == 1);
28288 this.next.setDisabled(ap == ps);
28289 this.last.setDisabled(ap == ps);
28290 this.loading.enable();
28295 getPageData : function(){
28296 var total = this.ds.getTotalCount();
28299 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28300 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28305 onLoadError : function(){
28306 this.loading.enable();
28310 onPagingKeydown : function(e){
28311 var k = e.getKey();
28312 var d = this.getPageData();
28314 var v = this.field.dom.value, pageNum;
28315 if(!v || isNaN(pageNum = parseInt(v, 10))){
28316 this.field.dom.value = d.activePage;
28319 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28320 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28323 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))
28325 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28326 this.field.dom.value = pageNum;
28327 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28330 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28332 var v = this.field.dom.value, pageNum;
28333 var increment = (e.shiftKey) ? 10 : 1;
28334 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28337 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28338 this.field.dom.value = d.activePage;
28341 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28343 this.field.dom.value = parseInt(v, 10) + increment;
28344 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28345 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28352 beforeLoad : function(){
28354 this.loading.disable();
28359 onClick : function(which){
28368 ds.load({params:{start: 0, limit: this.pageSize}});
28371 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28374 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28377 var total = ds.getTotalCount();
28378 var extra = total % this.pageSize;
28379 var lastStart = extra ? (total - extra) : total-this.pageSize;
28380 ds.load({params:{start: lastStart, limit: this.pageSize}});
28383 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28389 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28390 * @param {Roo.data.Store} store The data store to unbind
28392 unbind : function(ds){
28393 ds.un("beforeload", this.beforeLoad, this);
28394 ds.un("load", this.onLoad, this);
28395 ds.un("loadexception", this.onLoadError, this);
28396 ds.un("remove", this.updateInfo, this);
28397 ds.un("add", this.updateInfo, this);
28398 this.ds = undefined;
28402 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28403 * @param {Roo.data.Store} store The data store to bind
28405 bind : function(ds){
28406 ds.on("beforeload", this.beforeLoad, this);
28407 ds.on("load", this.onLoad, this);
28408 ds.on("loadexception", this.onLoadError, this);
28409 ds.on("remove", this.updateInfo, this);
28410 ds.on("add", this.updateInfo, this);
28421 * @class Roo.bootstrap.MessageBar
28422 * @extends Roo.bootstrap.Component
28423 * Bootstrap MessageBar class
28424 * @cfg {String} html contents of the MessageBar
28425 * @cfg {String} weight (info | success | warning | danger) default info
28426 * @cfg {String} beforeClass insert the bar before the given class
28427 * @cfg {Boolean} closable (true | false) default false
28428 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28431 * Create a new Element
28432 * @param {Object} config The config object
28435 Roo.bootstrap.MessageBar = function(config){
28436 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28439 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28445 beforeClass: 'bootstrap-sticky-wrap',
28447 getAutoCreate : function(){
28451 cls: 'alert alert-dismissable alert-' + this.weight,
28456 html: this.html || ''
28462 cfg.cls += ' alert-messages-fixed';
28476 onRender : function(ct, position)
28478 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28481 var cfg = Roo.apply({}, this.getAutoCreate());
28485 cfg.cls += ' ' + this.cls;
28488 cfg.style = this.style;
28490 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28492 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28495 this.el.select('>button.close').on('click', this.hide, this);
28501 if (!this.rendered) {
28507 this.fireEvent('show', this);
28513 if (!this.rendered) {
28519 this.fireEvent('hide', this);
28522 update : function()
28524 // var e = this.el.dom.firstChild;
28526 // if(this.closable){
28527 // e = e.nextSibling;
28530 // e.data = this.html || '';
28532 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28548 * @class Roo.bootstrap.Graph
28549 * @extends Roo.bootstrap.Component
28550 * Bootstrap Graph class
28554 @cfg {String} graphtype bar | vbar | pie
28555 @cfg {number} g_x coodinator | centre x (pie)
28556 @cfg {number} g_y coodinator | centre y (pie)
28557 @cfg {number} g_r radius (pie)
28558 @cfg {number} g_height height of the chart (respected by all elements in the set)
28559 @cfg {number} g_width width of the chart (respected by all elements in the set)
28560 @cfg {Object} title The title of the chart
28563 -opts (object) options for the chart
28565 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28566 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28568 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.
28569 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28571 o stretch (boolean)
28573 -opts (object) options for the pie
28576 o startAngle (number)
28577 o endAngle (number)
28581 * Create a new Input
28582 * @param {Object} config The config object
28585 Roo.bootstrap.Graph = function(config){
28586 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28592 * The img click event for the img.
28593 * @param {Roo.EventObject} e
28599 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28610 //g_colors: this.colors,
28617 getAutoCreate : function(){
28628 onRender : function(ct,position){
28631 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28633 if (typeof(Raphael) == 'undefined') {
28634 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28638 this.raphael = Raphael(this.el.dom);
28640 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28641 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28642 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28643 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28645 r.text(160, 10, "Single Series Chart").attr(txtattr);
28646 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28647 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28648 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28650 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28651 r.barchart(330, 10, 300, 220, data1);
28652 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28653 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28656 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28657 // r.barchart(30, 30, 560, 250, xdata, {
28658 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28659 // axis : "0 0 1 1",
28660 // axisxlabels : xdata
28661 // //yvalues : cols,
28664 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28666 // this.load(null,xdata,{
28667 // axis : "0 0 1 1",
28668 // axisxlabels : xdata
28673 load : function(graphtype,xdata,opts)
28675 this.raphael.clear();
28677 graphtype = this.graphtype;
28682 var r = this.raphael,
28683 fin = function () {
28684 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28686 fout = function () {
28687 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28689 pfin = function() {
28690 this.sector.stop();
28691 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28694 this.label[0].stop();
28695 this.label[0].attr({ r: 7.5 });
28696 this.label[1].attr({ "font-weight": 800 });
28699 pfout = function() {
28700 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28703 this.label[0].animate({ r: 5 }, 500, "bounce");
28704 this.label[1].attr({ "font-weight": 400 });
28710 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28713 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28716 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28717 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28719 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28726 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28731 setTitle: function(o)
28736 initEvents: function() {
28739 this.el.on('click', this.onClick, this);
28743 onClick : function(e)
28745 Roo.log('img onclick');
28746 this.fireEvent('click', this, e);
28758 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28761 * @class Roo.bootstrap.dash.NumberBox
28762 * @extends Roo.bootstrap.Component
28763 * Bootstrap NumberBox class
28764 * @cfg {String} headline Box headline
28765 * @cfg {String} content Box content
28766 * @cfg {String} icon Box icon
28767 * @cfg {String} footer Footer text
28768 * @cfg {String} fhref Footer href
28771 * Create a new NumberBox
28772 * @param {Object} config The config object
28776 Roo.bootstrap.dash.NumberBox = function(config){
28777 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28781 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28790 getAutoCreate : function(){
28794 cls : 'small-box ',
28802 cls : 'roo-headline',
28803 html : this.headline
28807 cls : 'roo-content',
28808 html : this.content
28822 cls : 'ion ' + this.icon
28831 cls : 'small-box-footer',
28832 href : this.fhref || '#',
28836 cfg.cn.push(footer);
28843 onRender : function(ct,position){
28844 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28851 setHeadline: function (value)
28853 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28856 setFooter: function (value, href)
28858 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28861 this.el.select('a.small-box-footer',true).first().attr('href', href);
28866 setContent: function (value)
28868 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28871 initEvents: function()
28885 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28888 * @class Roo.bootstrap.dash.TabBox
28889 * @extends Roo.bootstrap.Component
28890 * Bootstrap TabBox class
28891 * @cfg {String} title Title of the TabBox
28892 * @cfg {String} icon Icon of the TabBox
28893 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28894 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28897 * Create a new TabBox
28898 * @param {Object} config The config object
28902 Roo.bootstrap.dash.TabBox = function(config){
28903 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28908 * When a pane is added
28909 * @param {Roo.bootstrap.dash.TabPane} pane
28913 * @event activatepane
28914 * When a pane is activated
28915 * @param {Roo.bootstrap.dash.TabPane} pane
28917 "activatepane" : true
28925 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28930 tabScrollable : false,
28932 getChildContainer : function()
28934 return this.el.select('.tab-content', true).first();
28937 getAutoCreate : function(){
28941 cls: 'pull-left header',
28949 cls: 'fa ' + this.icon
28955 cls: 'nav nav-tabs pull-right',
28961 if(this.tabScrollable){
28968 cls: 'nav nav-tabs pull-right',
28979 cls: 'nav-tabs-custom',
28984 cls: 'tab-content no-padding',
28992 initEvents : function()
28994 //Roo.log('add add pane handler');
28995 this.on('addpane', this.onAddPane, this);
28998 * Updates the box title
28999 * @param {String} html to set the title to.
29001 setTitle : function(value)
29003 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29005 onAddPane : function(pane)
29007 this.panes.push(pane);
29008 //Roo.log('addpane');
29010 // tabs are rendere left to right..
29011 if(!this.showtabs){
29015 var ctr = this.el.select('.nav-tabs', true).first();
29018 var existing = ctr.select('.nav-tab',true);
29019 var qty = existing.getCount();;
29022 var tab = ctr.createChild({
29024 cls : 'nav-tab' + (qty ? '' : ' active'),
29032 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29035 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29037 pane.el.addClass('active');
29042 onTabClick : function(ev,un,ob,pane)
29044 //Roo.log('tab - prev default');
29045 ev.preventDefault();
29048 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29049 pane.tab.addClass('active');
29050 //Roo.log(pane.title);
29051 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29052 // technically we should have a deactivate event.. but maybe add later.
29053 // and it should not de-activate the selected tab...
29054 this.fireEvent('activatepane', pane);
29055 pane.el.addClass('active');
29056 pane.fireEvent('activate');
29061 getActivePane : function()
29064 Roo.each(this.panes, function(p) {
29065 if(p.el.hasClass('active')){
29086 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29088 * @class Roo.bootstrap.TabPane
29089 * @extends Roo.bootstrap.Component
29090 * Bootstrap TabPane class
29091 * @cfg {Boolean} active (false | true) Default false
29092 * @cfg {String} title title of panel
29096 * Create a new TabPane
29097 * @param {Object} config The config object
29100 Roo.bootstrap.dash.TabPane = function(config){
29101 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29107 * When a pane is activated
29108 * @param {Roo.bootstrap.dash.TabPane} pane
29115 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29120 // the tabBox that this is attached to.
29123 getAutoCreate : function()
29131 cfg.cls += ' active';
29136 initEvents : function()
29138 //Roo.log('trigger add pane handler');
29139 this.parent().fireEvent('addpane', this)
29143 * Updates the tab title
29144 * @param {String} html to set the title to.
29146 setTitle: function(str)
29152 this.tab.select('a', true).first().dom.innerHTML = str;
29169 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29172 * @class Roo.bootstrap.menu.Menu
29173 * @extends Roo.bootstrap.Component
29174 * Bootstrap Menu class - container for Menu
29175 * @cfg {String} html Text of the menu
29176 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29177 * @cfg {String} icon Font awesome icon
29178 * @cfg {String} pos Menu align to (top | bottom) default bottom
29182 * Create a new Menu
29183 * @param {Object} config The config object
29187 Roo.bootstrap.menu.Menu = function(config){
29188 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29192 * @event beforeshow
29193 * Fires before this menu is displayed
29194 * @param {Roo.bootstrap.menu.Menu} this
29198 * @event beforehide
29199 * Fires before this menu is hidden
29200 * @param {Roo.bootstrap.menu.Menu} this
29205 * Fires after this menu is displayed
29206 * @param {Roo.bootstrap.menu.Menu} this
29211 * Fires after this menu is hidden
29212 * @param {Roo.bootstrap.menu.Menu} this
29217 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29218 * @param {Roo.bootstrap.menu.Menu} this
29219 * @param {Roo.EventObject} e
29226 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29230 weight : 'default',
29235 getChildContainer : function() {
29236 if(this.isSubMenu){
29240 return this.el.select('ul.dropdown-menu', true).first();
29243 getAutoCreate : function()
29248 cls : 'roo-menu-text',
29256 cls : 'fa ' + this.icon
29267 cls : 'dropdown-button btn btn-' + this.weight,
29272 cls : 'dropdown-toggle btn btn-' + this.weight,
29282 cls : 'dropdown-menu'
29288 if(this.pos == 'top'){
29289 cfg.cls += ' dropup';
29292 if(this.isSubMenu){
29295 cls : 'dropdown-menu'
29302 onRender : function(ct, position)
29304 this.isSubMenu = ct.hasClass('dropdown-submenu');
29306 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29309 initEvents : function()
29311 if(this.isSubMenu){
29315 this.hidden = true;
29317 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29318 this.triggerEl.on('click', this.onTriggerPress, this);
29320 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29321 this.buttonEl.on('click', this.onClick, this);
29327 if(this.isSubMenu){
29331 return this.el.select('ul.dropdown-menu', true).first();
29334 onClick : function(e)
29336 this.fireEvent("click", this, e);
29339 onTriggerPress : function(e)
29341 if (this.isVisible()) {
29348 isVisible : function(){
29349 return !this.hidden;
29354 this.fireEvent("beforeshow", this);
29356 this.hidden = false;
29357 this.el.addClass('open');
29359 Roo.get(document).on("mouseup", this.onMouseUp, this);
29361 this.fireEvent("show", this);
29368 this.fireEvent("beforehide", this);
29370 this.hidden = true;
29371 this.el.removeClass('open');
29373 Roo.get(document).un("mouseup", this.onMouseUp);
29375 this.fireEvent("hide", this);
29378 onMouseUp : function()
29392 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29395 * @class Roo.bootstrap.menu.Item
29396 * @extends Roo.bootstrap.Component
29397 * Bootstrap MenuItem class
29398 * @cfg {Boolean} submenu (true | false) default false
29399 * @cfg {String} html text of the item
29400 * @cfg {String} href the link
29401 * @cfg {Boolean} disable (true | false) default false
29402 * @cfg {Boolean} preventDefault (true | false) default true
29403 * @cfg {String} icon Font awesome icon
29404 * @cfg {String} pos Submenu align to (left | right) default right
29408 * Create a new Item
29409 * @param {Object} config The config object
29413 Roo.bootstrap.menu.Item = function(config){
29414 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29418 * Fires when the mouse is hovering over this menu
29419 * @param {Roo.bootstrap.menu.Item} this
29420 * @param {Roo.EventObject} e
29425 * Fires when the mouse exits this menu
29426 * @param {Roo.bootstrap.menu.Item} this
29427 * @param {Roo.EventObject} e
29433 * The raw click event for the entire grid.
29434 * @param {Roo.EventObject} e
29440 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29445 preventDefault: true,
29450 getAutoCreate : function()
29455 cls : 'roo-menu-item-text',
29463 cls : 'fa ' + this.icon
29472 href : this.href || '#',
29479 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29483 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29485 if(this.pos == 'left'){
29486 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29493 initEvents : function()
29495 this.el.on('mouseover', this.onMouseOver, this);
29496 this.el.on('mouseout', this.onMouseOut, this);
29498 this.el.select('a', true).first().on('click', this.onClick, this);
29502 onClick : function(e)
29504 if(this.preventDefault){
29505 e.preventDefault();
29508 this.fireEvent("click", this, e);
29511 onMouseOver : function(e)
29513 if(this.submenu && this.pos == 'left'){
29514 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29517 this.fireEvent("mouseover", this, e);
29520 onMouseOut : function(e)
29522 this.fireEvent("mouseout", this, e);
29534 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29537 * @class Roo.bootstrap.menu.Separator
29538 * @extends Roo.bootstrap.Component
29539 * Bootstrap Separator class
29542 * Create a new Separator
29543 * @param {Object} config The config object
29547 Roo.bootstrap.menu.Separator = function(config){
29548 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29551 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29553 getAutoCreate : function(){
29556 cls: 'dropdown-divider divider'
29574 * @class Roo.bootstrap.Tooltip
29575 * Bootstrap Tooltip class
29576 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29577 * to determine which dom element triggers the tooltip.
29579 * It needs to add support for additional attributes like tooltip-position
29582 * Create a new Toolti
29583 * @param {Object} config The config object
29586 Roo.bootstrap.Tooltip = function(config){
29587 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29589 this.alignment = Roo.bootstrap.Tooltip.alignment;
29591 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29592 this.alignment = config.alignment;
29597 Roo.apply(Roo.bootstrap.Tooltip, {
29599 * @function init initialize tooltip monitoring.
29603 currentTip : false,
29604 currentRegion : false,
29610 Roo.get(document).on('mouseover', this.enter ,this);
29611 Roo.get(document).on('mouseout', this.leave, this);
29614 this.currentTip = new Roo.bootstrap.Tooltip();
29617 enter : function(ev)
29619 var dom = ev.getTarget();
29621 //Roo.log(['enter',dom]);
29622 var el = Roo.fly(dom);
29623 if (this.currentEl) {
29625 //Roo.log(this.currentEl);
29626 //Roo.log(this.currentEl.contains(dom));
29627 if (this.currentEl == el) {
29630 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29636 if (this.currentTip.el) {
29637 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29641 if(!el || el.dom == document){
29647 if (!el.attr('tooltip')) {
29648 pel = el.findParent("[tooltip]");
29650 bindEl = Roo.get(pel);
29656 // you can not look for children, as if el is the body.. then everythign is the child..
29657 if (!pel && !el.attr('tooltip')) { //
29658 if (!el.select("[tooltip]").elements.length) {
29661 // is the mouse over this child...?
29662 bindEl = el.select("[tooltip]").first();
29663 var xy = ev.getXY();
29664 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29665 //Roo.log("not in region.");
29668 //Roo.log("child element over..");
29671 this.currentEl = el;
29672 this.currentTip.bind(bindEl);
29673 this.currentRegion = Roo.lib.Region.getRegion(dom);
29674 this.currentTip.enter();
29677 leave : function(ev)
29679 var dom = ev.getTarget();
29680 //Roo.log(['leave',dom]);
29681 if (!this.currentEl) {
29686 if (dom != this.currentEl.dom) {
29689 var xy = ev.getXY();
29690 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29693 // only activate leave if mouse cursor is outside... bounding box..
29698 if (this.currentTip) {
29699 this.currentTip.leave();
29701 //Roo.log('clear currentEl');
29702 this.currentEl = false;
29707 'left' : ['r-l', [-2,0], 'right'],
29708 'right' : ['l-r', [2,0], 'left'],
29709 'bottom' : ['t-b', [0,2], 'top'],
29710 'top' : [ 'b-t', [0,-2], 'bottom']
29716 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29721 delay : null, // can be { show : 300 , hide: 500}
29725 hoverState : null, //???
29727 placement : 'bottom',
29731 getAutoCreate : function(){
29738 cls : 'tooltip-arrow arrow'
29741 cls : 'tooltip-inner'
29748 bind : function(el)
29753 initEvents : function()
29755 this.arrowEl = this.el.select('.arrow', true).first();
29756 this.innerEl = this.el.select('.tooltip-inner', true).first();
29759 enter : function () {
29761 if (this.timeout != null) {
29762 clearTimeout(this.timeout);
29765 this.hoverState = 'in';
29766 //Roo.log("enter - show");
29767 if (!this.delay || !this.delay.show) {
29772 this.timeout = setTimeout(function () {
29773 if (_t.hoverState == 'in') {
29776 }, this.delay.show);
29780 clearTimeout(this.timeout);
29782 this.hoverState = 'out';
29783 if (!this.delay || !this.delay.hide) {
29789 this.timeout = setTimeout(function () {
29790 //Roo.log("leave - timeout");
29792 if (_t.hoverState == 'out') {
29794 Roo.bootstrap.Tooltip.currentEl = false;
29799 show : function (msg)
29802 this.render(document.body);
29805 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29807 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29809 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29811 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29812 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29814 var placement = typeof this.placement == 'function' ?
29815 this.placement.call(this, this.el, on_el) :
29818 var autoToken = /\s?auto?\s?/i;
29819 var autoPlace = autoToken.test(placement);
29821 placement = placement.replace(autoToken, '') || 'top';
29825 //this.el.setXY([0,0]);
29827 //this.el.dom.style.display='block';
29829 //this.el.appendTo(on_el);
29831 var p = this.getPosition();
29832 var box = this.el.getBox();
29838 var align = this.alignment[placement];
29840 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29842 if(placement == 'top' || placement == 'bottom'){
29844 placement = 'right';
29847 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29848 placement = 'left';
29851 var scroll = Roo.select('body', true).first().getScroll();
29853 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29857 align = this.alignment[placement];
29859 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29863 var elems = document.getElementsByTagName('div');
29864 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29865 for (var i = 0; i < elems.length; i++) {
29866 var zindex = Number.parseInt(
29867 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29870 if (zindex > highest) {
29877 this.el.dom.style.zIndex = highest;
29879 this.el.alignTo(this.bindEl, align[0],align[1]);
29880 //var arrow = this.el.select('.arrow',true).first();
29881 //arrow.set(align[2],
29883 this.el.addClass(placement);
29884 this.el.addClass("bs-tooltip-"+ placement);
29886 this.el.addClass('in fade show');
29888 this.hoverState = null;
29890 if (this.el.hasClass('fade')) {
29905 //this.el.setXY([0,0]);
29906 this.el.removeClass(['show', 'in']);
29922 * @class Roo.bootstrap.LocationPicker
29923 * @extends Roo.bootstrap.Component
29924 * Bootstrap LocationPicker class
29925 * @cfg {Number} latitude Position when init default 0
29926 * @cfg {Number} longitude Position when init default 0
29927 * @cfg {Number} zoom default 15
29928 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29929 * @cfg {Boolean} mapTypeControl default false
29930 * @cfg {Boolean} disableDoubleClickZoom default false
29931 * @cfg {Boolean} scrollwheel default true
29932 * @cfg {Boolean} streetViewControl default false
29933 * @cfg {Number} radius default 0
29934 * @cfg {String} locationName
29935 * @cfg {Boolean} draggable default true
29936 * @cfg {Boolean} enableAutocomplete default false
29937 * @cfg {Boolean} enableReverseGeocode default true
29938 * @cfg {String} markerTitle
29941 * Create a new LocationPicker
29942 * @param {Object} config The config object
29946 Roo.bootstrap.LocationPicker = function(config){
29948 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29953 * Fires when the picker initialized.
29954 * @param {Roo.bootstrap.LocationPicker} this
29955 * @param {Google Location} location
29959 * @event positionchanged
29960 * Fires when the picker position changed.
29961 * @param {Roo.bootstrap.LocationPicker} this
29962 * @param {Google Location} location
29964 positionchanged : true,
29967 * Fires when the map resize.
29968 * @param {Roo.bootstrap.LocationPicker} this
29973 * Fires when the map show.
29974 * @param {Roo.bootstrap.LocationPicker} this
29979 * Fires when the map hide.
29980 * @param {Roo.bootstrap.LocationPicker} this
29985 * Fires when click the map.
29986 * @param {Roo.bootstrap.LocationPicker} this
29987 * @param {Map event} e
29991 * @event mapRightClick
29992 * Fires when right click the map.
29993 * @param {Roo.bootstrap.LocationPicker} this
29994 * @param {Map event} e
29996 mapRightClick : true,
29998 * @event markerClick
29999 * Fires when click the marker.
30000 * @param {Roo.bootstrap.LocationPicker} this
30001 * @param {Map event} e
30003 markerClick : true,
30005 * @event markerRightClick
30006 * Fires when right click the marker.
30007 * @param {Roo.bootstrap.LocationPicker} this
30008 * @param {Map event} e
30010 markerRightClick : true,
30012 * @event OverlayViewDraw
30013 * Fires when OverlayView Draw
30014 * @param {Roo.bootstrap.LocationPicker} this
30016 OverlayViewDraw : true,
30018 * @event OverlayViewOnAdd
30019 * Fires when OverlayView Draw
30020 * @param {Roo.bootstrap.LocationPicker} this
30022 OverlayViewOnAdd : true,
30024 * @event OverlayViewOnRemove
30025 * Fires when OverlayView Draw
30026 * @param {Roo.bootstrap.LocationPicker} this
30028 OverlayViewOnRemove : true,
30030 * @event OverlayViewShow
30031 * Fires when OverlayView Draw
30032 * @param {Roo.bootstrap.LocationPicker} this
30033 * @param {Pixel} cpx
30035 OverlayViewShow : true,
30037 * @event OverlayViewHide
30038 * Fires when OverlayView Draw
30039 * @param {Roo.bootstrap.LocationPicker} this
30041 OverlayViewHide : true,
30043 * @event loadexception
30044 * Fires when load google lib failed.
30045 * @param {Roo.bootstrap.LocationPicker} this
30047 loadexception : true
30052 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30054 gMapContext: false,
30060 mapTypeControl: false,
30061 disableDoubleClickZoom: false,
30063 streetViewControl: false,
30067 enableAutocomplete: false,
30068 enableReverseGeocode: true,
30071 getAutoCreate: function()
30076 cls: 'roo-location-picker'
30082 initEvents: function(ct, position)
30084 if(!this.el.getWidth() || this.isApplied()){
30088 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30093 initial: function()
30095 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30096 this.fireEvent('loadexception', this);
30100 if(!this.mapTypeId){
30101 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30104 this.gMapContext = this.GMapContext();
30106 this.initOverlayView();
30108 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30112 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30113 _this.setPosition(_this.gMapContext.marker.position);
30116 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30117 _this.fireEvent('mapClick', this, event);
30121 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30122 _this.fireEvent('mapRightClick', this, event);
30126 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30127 _this.fireEvent('markerClick', this, event);
30131 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30132 _this.fireEvent('markerRightClick', this, event);
30136 this.setPosition(this.gMapContext.location);
30138 this.fireEvent('initial', this, this.gMapContext.location);
30141 initOverlayView: function()
30145 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30149 _this.fireEvent('OverlayViewDraw', _this);
30154 _this.fireEvent('OverlayViewOnAdd', _this);
30157 onRemove: function()
30159 _this.fireEvent('OverlayViewOnRemove', _this);
30162 show: function(cpx)
30164 _this.fireEvent('OverlayViewShow', _this, cpx);
30169 _this.fireEvent('OverlayViewHide', _this);
30175 fromLatLngToContainerPixel: function(event)
30177 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30180 isApplied: function()
30182 return this.getGmapContext() == false ? false : true;
30185 getGmapContext: function()
30187 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30190 GMapContext: function()
30192 var position = new google.maps.LatLng(this.latitude, this.longitude);
30194 var _map = new google.maps.Map(this.el.dom, {
30197 mapTypeId: this.mapTypeId,
30198 mapTypeControl: this.mapTypeControl,
30199 disableDoubleClickZoom: this.disableDoubleClickZoom,
30200 scrollwheel: this.scrollwheel,
30201 streetViewControl: this.streetViewControl,
30202 locationName: this.locationName,
30203 draggable: this.draggable,
30204 enableAutocomplete: this.enableAutocomplete,
30205 enableReverseGeocode: this.enableReverseGeocode
30208 var _marker = new google.maps.Marker({
30209 position: position,
30211 title: this.markerTitle,
30212 draggable: this.draggable
30219 location: position,
30220 radius: this.radius,
30221 locationName: this.locationName,
30222 addressComponents: {
30223 formatted_address: null,
30224 addressLine1: null,
30225 addressLine2: null,
30227 streetNumber: null,
30231 stateOrProvince: null
30234 domContainer: this.el.dom,
30235 geodecoder: new google.maps.Geocoder()
30239 drawCircle: function(center, radius, options)
30241 if (this.gMapContext.circle != null) {
30242 this.gMapContext.circle.setMap(null);
30246 options = Roo.apply({}, options, {
30247 strokeColor: "#0000FF",
30248 strokeOpacity: .35,
30250 fillColor: "#0000FF",
30254 options.map = this.gMapContext.map;
30255 options.radius = radius;
30256 options.center = center;
30257 this.gMapContext.circle = new google.maps.Circle(options);
30258 return this.gMapContext.circle;
30264 setPosition: function(location)
30266 this.gMapContext.location = location;
30267 this.gMapContext.marker.setPosition(location);
30268 this.gMapContext.map.panTo(location);
30269 this.drawCircle(location, this.gMapContext.radius, {});
30273 if (this.gMapContext.settings.enableReverseGeocode) {
30274 this.gMapContext.geodecoder.geocode({
30275 latLng: this.gMapContext.location
30276 }, function(results, status) {
30278 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30279 _this.gMapContext.locationName = results[0].formatted_address;
30280 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30282 _this.fireEvent('positionchanged', this, location);
30289 this.fireEvent('positionchanged', this, location);
30294 google.maps.event.trigger(this.gMapContext.map, "resize");
30296 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30298 this.fireEvent('resize', this);
30301 setPositionByLatLng: function(latitude, longitude)
30303 this.setPosition(new google.maps.LatLng(latitude, longitude));
30306 getCurrentPosition: function()
30309 latitude: this.gMapContext.location.lat(),
30310 longitude: this.gMapContext.location.lng()
30314 getAddressName: function()
30316 return this.gMapContext.locationName;
30319 getAddressComponents: function()
30321 return this.gMapContext.addressComponents;
30324 address_component_from_google_geocode: function(address_components)
30328 for (var i = 0; i < address_components.length; i++) {
30329 var component = address_components[i];
30330 if (component.types.indexOf("postal_code") >= 0) {
30331 result.postalCode = component.short_name;
30332 } else if (component.types.indexOf("street_number") >= 0) {
30333 result.streetNumber = component.short_name;
30334 } else if (component.types.indexOf("route") >= 0) {
30335 result.streetName = component.short_name;
30336 } else if (component.types.indexOf("neighborhood") >= 0) {
30337 result.city = component.short_name;
30338 } else if (component.types.indexOf("locality") >= 0) {
30339 result.city = component.short_name;
30340 } else if (component.types.indexOf("sublocality") >= 0) {
30341 result.district = component.short_name;
30342 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30343 result.stateOrProvince = component.short_name;
30344 } else if (component.types.indexOf("country") >= 0) {
30345 result.country = component.short_name;
30349 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30350 result.addressLine2 = "";
30354 setZoomLevel: function(zoom)
30356 this.gMapContext.map.setZoom(zoom);
30369 this.fireEvent('show', this);
30380 this.fireEvent('hide', this);
30385 Roo.apply(Roo.bootstrap.LocationPicker, {
30387 OverlayView : function(map, options)
30389 options = options || {};
30396 * @class Roo.bootstrap.Alert
30397 * @extends Roo.bootstrap.Component
30398 * Bootstrap Alert class - shows an alert area box
30400 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30401 Enter a valid email address
30404 * @cfg {String} title The title of alert
30405 * @cfg {String} html The content of alert
30406 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30407 * @cfg {String} fa font-awesomeicon
30408 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30409 * @cfg {Boolean} close true to show a x closer
30413 * Create a new alert
30414 * @param {Object} config The config object
30418 Roo.bootstrap.Alert = function(config){
30419 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30423 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30429 faicon: false, // BC
30433 getAutoCreate : function()
30445 style : this.close ? '' : 'display:none'
30449 cls : 'roo-alert-icon'
30454 cls : 'roo-alert-title',
30459 cls : 'roo-alert-text',
30466 cfg.cn[0].cls += ' fa ' + this.faicon;
30469 cfg.cn[0].cls += ' fa ' + this.fa;
30473 cfg.cls += ' alert-' + this.weight;
30479 initEvents: function()
30481 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30482 this.titleEl = this.el.select('.roo-alert-title',true).first();
30483 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30484 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30485 if (this.seconds > 0) {
30486 this.hide.defer(this.seconds, this);
30490 * Set the Title Message HTML
30491 * @param {String} html
30493 setTitle : function(str)
30495 this.titleEl.dom.innerHTML = str;
30499 * Set the Body Message HTML
30500 * @param {String} html
30502 setHtml : function(str)
30504 this.htmlEl.dom.innerHTML = str;
30507 * Set the Weight of the alert
30508 * @param {String} (success|info|warning|danger) weight
30511 setWeight : function(weight)
30514 this.el.removeClass('alert-' + this.weight);
30517 this.weight = weight;
30519 this.el.addClass('alert-' + this.weight);
30522 * Set the Icon of the alert
30523 * @param {String} see fontawsome names (name without the 'fa-' bit)
30525 setIcon : function(icon)
30528 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30531 this.faicon = icon;
30533 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30558 * @class Roo.bootstrap.UploadCropbox
30559 * @extends Roo.bootstrap.Component
30560 * Bootstrap UploadCropbox class
30561 * @cfg {String} emptyText show when image has been loaded
30562 * @cfg {String} rotateNotify show when image too small to rotate
30563 * @cfg {Number} errorTimeout default 3000
30564 * @cfg {Number} minWidth default 300
30565 * @cfg {Number} minHeight default 300
30566 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30567 * @cfg {Boolean} isDocument (true|false) default false
30568 * @cfg {String} url action url
30569 * @cfg {String} paramName default 'imageUpload'
30570 * @cfg {String} method default POST
30571 * @cfg {Boolean} loadMask (true|false) default true
30572 * @cfg {Boolean} loadingText default 'Loading...'
30575 * Create a new UploadCropbox
30576 * @param {Object} config The config object
30579 Roo.bootstrap.UploadCropbox = function(config){
30580 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30584 * @event beforeselectfile
30585 * Fire before select file
30586 * @param {Roo.bootstrap.UploadCropbox} this
30588 "beforeselectfile" : true,
30591 * Fire after initEvent
30592 * @param {Roo.bootstrap.UploadCropbox} this
30597 * Fire after initEvent
30598 * @param {Roo.bootstrap.UploadCropbox} this
30599 * @param {String} data
30604 * Fire when preparing the file data
30605 * @param {Roo.bootstrap.UploadCropbox} this
30606 * @param {Object} file
30611 * Fire when get exception
30612 * @param {Roo.bootstrap.UploadCropbox} this
30613 * @param {XMLHttpRequest} xhr
30615 "exception" : true,
30617 * @event beforeloadcanvas
30618 * Fire before load the canvas
30619 * @param {Roo.bootstrap.UploadCropbox} this
30620 * @param {String} src
30622 "beforeloadcanvas" : true,
30625 * Fire when trash image
30626 * @param {Roo.bootstrap.UploadCropbox} this
30631 * Fire when download the image
30632 * @param {Roo.bootstrap.UploadCropbox} this
30636 * @event footerbuttonclick
30637 * Fire when footerbuttonclick
30638 * @param {Roo.bootstrap.UploadCropbox} this
30639 * @param {String} type
30641 "footerbuttonclick" : true,
30645 * @param {Roo.bootstrap.UploadCropbox} this
30650 * Fire when rotate the image
30651 * @param {Roo.bootstrap.UploadCropbox} this
30652 * @param {String} pos
30657 * Fire when inspect the file
30658 * @param {Roo.bootstrap.UploadCropbox} this
30659 * @param {Object} file
30664 * Fire when xhr upload the file
30665 * @param {Roo.bootstrap.UploadCropbox} this
30666 * @param {Object} data
30671 * Fire when arrange the file data
30672 * @param {Roo.bootstrap.UploadCropbox} this
30673 * @param {Object} formData
30678 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30681 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30683 emptyText : 'Click to upload image',
30684 rotateNotify : 'Image is too small to rotate',
30685 errorTimeout : 3000,
30699 cropType : 'image/jpeg',
30701 canvasLoaded : false,
30702 isDocument : false,
30704 paramName : 'imageUpload',
30706 loadingText : 'Loading...',
30709 getAutoCreate : function()
30713 cls : 'roo-upload-cropbox',
30717 cls : 'roo-upload-cropbox-selector',
30722 cls : 'roo-upload-cropbox-body',
30723 style : 'cursor:pointer',
30727 cls : 'roo-upload-cropbox-preview'
30731 cls : 'roo-upload-cropbox-thumb'
30735 cls : 'roo-upload-cropbox-empty-notify',
30736 html : this.emptyText
30740 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30741 html : this.rotateNotify
30747 cls : 'roo-upload-cropbox-footer',
30750 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30760 onRender : function(ct, position)
30762 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30764 if (this.buttons.length) {
30766 Roo.each(this.buttons, function(bb) {
30768 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30770 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30776 this.maskEl = this.el;
30780 initEvents : function()
30782 this.urlAPI = (window.createObjectURL && window) ||
30783 (window.URL && URL.revokeObjectURL && URL) ||
30784 (window.webkitURL && webkitURL);
30786 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30787 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30789 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30790 this.selectorEl.hide();
30792 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30793 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30795 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30796 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30797 this.thumbEl.hide();
30799 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30800 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30802 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30803 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30804 this.errorEl.hide();
30806 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30807 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30808 this.footerEl.hide();
30810 this.setThumbBoxSize();
30816 this.fireEvent('initial', this);
30823 window.addEventListener("resize", function() { _this.resize(); } );
30825 this.bodyEl.on('click', this.beforeSelectFile, this);
30828 this.bodyEl.on('touchstart', this.onTouchStart, this);
30829 this.bodyEl.on('touchmove', this.onTouchMove, this);
30830 this.bodyEl.on('touchend', this.onTouchEnd, this);
30834 this.bodyEl.on('mousedown', this.onMouseDown, this);
30835 this.bodyEl.on('mousemove', this.onMouseMove, this);
30836 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30837 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30838 Roo.get(document).on('mouseup', this.onMouseUp, this);
30841 this.selectorEl.on('change', this.onFileSelected, this);
30847 this.baseScale = 1;
30849 this.baseRotate = 1;
30850 this.dragable = false;
30851 this.pinching = false;
30854 this.cropData = false;
30855 this.notifyEl.dom.innerHTML = this.emptyText;
30857 this.selectorEl.dom.value = '';
30861 resize : function()
30863 if(this.fireEvent('resize', this) != false){
30864 this.setThumbBoxPosition();
30865 this.setCanvasPosition();
30869 onFooterButtonClick : function(e, el, o, type)
30872 case 'rotate-left' :
30873 this.onRotateLeft(e);
30875 case 'rotate-right' :
30876 this.onRotateRight(e);
30879 this.beforeSelectFile(e);
30894 this.fireEvent('footerbuttonclick', this, type);
30897 beforeSelectFile : function(e)
30899 e.preventDefault();
30901 if(this.fireEvent('beforeselectfile', this) != false){
30902 this.selectorEl.dom.click();
30906 onFileSelected : function(e)
30908 e.preventDefault();
30910 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30914 var file = this.selectorEl.dom.files[0];
30916 if(this.fireEvent('inspect', this, file) != false){
30917 this.prepare(file);
30922 trash : function(e)
30924 this.fireEvent('trash', this);
30927 download : function(e)
30929 this.fireEvent('download', this);
30932 loadCanvas : function(src)
30934 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30938 this.imageEl = document.createElement('img');
30942 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30944 this.imageEl.src = src;
30948 onLoadCanvas : function()
30950 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30951 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30953 this.bodyEl.un('click', this.beforeSelectFile, this);
30955 this.notifyEl.hide();
30956 this.thumbEl.show();
30957 this.footerEl.show();
30959 this.baseRotateLevel();
30961 if(this.isDocument){
30962 this.setThumbBoxSize();
30965 this.setThumbBoxPosition();
30967 this.baseScaleLevel();
30973 this.canvasLoaded = true;
30976 this.maskEl.unmask();
30981 setCanvasPosition : function()
30983 if(!this.canvasEl){
30987 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30988 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30990 this.previewEl.setLeft(pw);
30991 this.previewEl.setTop(ph);
30995 onMouseDown : function(e)
30999 this.dragable = true;
31000 this.pinching = false;
31002 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31003 this.dragable = false;
31007 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31008 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31012 onMouseMove : function(e)
31016 if(!this.canvasLoaded){
31020 if (!this.dragable){
31024 var minX = Math.ceil(this.thumbEl.getLeft(true));
31025 var minY = Math.ceil(this.thumbEl.getTop(true));
31027 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31028 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31030 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31031 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31033 x = x - this.mouseX;
31034 y = y - this.mouseY;
31036 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31037 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31039 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31040 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31042 this.previewEl.setLeft(bgX);
31043 this.previewEl.setTop(bgY);
31045 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31046 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31049 onMouseUp : function(e)
31053 this.dragable = false;
31056 onMouseWheel : function(e)
31060 this.startScale = this.scale;
31062 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31064 if(!this.zoomable()){
31065 this.scale = this.startScale;
31074 zoomable : function()
31076 var minScale = this.thumbEl.getWidth() / this.minWidth;
31078 if(this.minWidth < this.minHeight){
31079 minScale = this.thumbEl.getHeight() / this.minHeight;
31082 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31083 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31087 (this.rotate == 0 || this.rotate == 180) &&
31089 width > this.imageEl.OriginWidth ||
31090 height > this.imageEl.OriginHeight ||
31091 (width < this.minWidth && height < this.minHeight)
31099 (this.rotate == 90 || this.rotate == 270) &&
31101 width > this.imageEl.OriginWidth ||
31102 height > this.imageEl.OriginHeight ||
31103 (width < this.minHeight && height < this.minWidth)
31110 !this.isDocument &&
31111 (this.rotate == 0 || this.rotate == 180) &&
31113 width < this.minWidth ||
31114 width > this.imageEl.OriginWidth ||
31115 height < this.minHeight ||
31116 height > this.imageEl.OriginHeight
31123 !this.isDocument &&
31124 (this.rotate == 90 || this.rotate == 270) &&
31126 width < this.minHeight ||
31127 width > this.imageEl.OriginWidth ||
31128 height < this.minWidth ||
31129 height > this.imageEl.OriginHeight
31139 onRotateLeft : function(e)
31141 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31143 var minScale = this.thumbEl.getWidth() / this.minWidth;
31145 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31146 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31148 this.startScale = this.scale;
31150 while (this.getScaleLevel() < minScale){
31152 this.scale = this.scale + 1;
31154 if(!this.zoomable()){
31159 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31160 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31165 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31172 this.scale = this.startScale;
31174 this.onRotateFail();
31179 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31181 if(this.isDocument){
31182 this.setThumbBoxSize();
31183 this.setThumbBoxPosition();
31184 this.setCanvasPosition();
31189 this.fireEvent('rotate', this, 'left');
31193 onRotateRight : function(e)
31195 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31197 var minScale = this.thumbEl.getWidth() / this.minWidth;
31199 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31200 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31202 this.startScale = this.scale;
31204 while (this.getScaleLevel() < minScale){
31206 this.scale = this.scale + 1;
31208 if(!this.zoomable()){
31213 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31214 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31219 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31226 this.scale = this.startScale;
31228 this.onRotateFail();
31233 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31235 if(this.isDocument){
31236 this.setThumbBoxSize();
31237 this.setThumbBoxPosition();
31238 this.setCanvasPosition();
31243 this.fireEvent('rotate', this, 'right');
31246 onRotateFail : function()
31248 this.errorEl.show(true);
31252 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31257 this.previewEl.dom.innerHTML = '';
31259 var canvasEl = document.createElement("canvas");
31261 var contextEl = canvasEl.getContext("2d");
31263 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31264 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31265 var center = this.imageEl.OriginWidth / 2;
31267 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31268 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31269 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31270 center = this.imageEl.OriginHeight / 2;
31273 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31275 contextEl.translate(center, center);
31276 contextEl.rotate(this.rotate * Math.PI / 180);
31278 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31280 this.canvasEl = document.createElement("canvas");
31282 this.contextEl = this.canvasEl.getContext("2d");
31284 switch (this.rotate) {
31287 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31288 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31290 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31295 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31296 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31298 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31299 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);
31303 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31308 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31309 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31311 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31312 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);
31316 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);
31321 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31322 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31324 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31325 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31329 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);
31336 this.previewEl.appendChild(this.canvasEl);
31338 this.setCanvasPosition();
31343 if(!this.canvasLoaded){
31347 var imageCanvas = document.createElement("canvas");
31349 var imageContext = imageCanvas.getContext("2d");
31351 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31352 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31354 var center = imageCanvas.width / 2;
31356 imageContext.translate(center, center);
31358 imageContext.rotate(this.rotate * Math.PI / 180);
31360 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31362 var canvas = document.createElement("canvas");
31364 var context = canvas.getContext("2d");
31366 canvas.width = this.minWidth;
31367 canvas.height = this.minHeight;
31369 switch (this.rotate) {
31372 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31373 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31375 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31376 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31378 var targetWidth = this.minWidth - 2 * x;
31379 var targetHeight = this.minHeight - 2 * y;
31383 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31384 scale = targetWidth / width;
31387 if(x > 0 && y == 0){
31388 scale = targetHeight / height;
31391 if(x > 0 && y > 0){
31392 scale = targetWidth / width;
31394 if(width < height){
31395 scale = targetHeight / height;
31399 context.scale(scale, scale);
31401 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31402 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31404 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31405 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31407 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31412 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31413 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31415 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31416 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31418 var targetWidth = this.minWidth - 2 * x;
31419 var targetHeight = this.minHeight - 2 * y;
31423 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31424 scale = targetWidth / width;
31427 if(x > 0 && y == 0){
31428 scale = targetHeight / height;
31431 if(x > 0 && y > 0){
31432 scale = targetWidth / width;
31434 if(width < height){
31435 scale = targetHeight / height;
31439 context.scale(scale, scale);
31441 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31442 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31444 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31445 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31447 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31449 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31454 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31455 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31457 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31458 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31460 var targetWidth = this.minWidth - 2 * x;
31461 var targetHeight = this.minHeight - 2 * y;
31465 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31466 scale = targetWidth / width;
31469 if(x > 0 && y == 0){
31470 scale = targetHeight / height;
31473 if(x > 0 && y > 0){
31474 scale = targetWidth / width;
31476 if(width < height){
31477 scale = targetHeight / height;
31481 context.scale(scale, scale);
31483 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31484 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31486 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31487 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31489 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31490 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31492 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31497 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31498 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31500 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31501 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31503 var targetWidth = this.minWidth - 2 * x;
31504 var targetHeight = this.minHeight - 2 * y;
31508 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31509 scale = targetWidth / width;
31512 if(x > 0 && y == 0){
31513 scale = targetHeight / height;
31516 if(x > 0 && y > 0){
31517 scale = targetWidth / width;
31519 if(width < height){
31520 scale = targetHeight / height;
31524 context.scale(scale, scale);
31526 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31527 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31529 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31530 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31532 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31534 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31541 this.cropData = canvas.toDataURL(this.cropType);
31543 if(this.fireEvent('crop', this, this.cropData) !== false){
31544 this.process(this.file, this.cropData);
31551 setThumbBoxSize : function()
31555 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31556 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31557 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31559 this.minWidth = width;
31560 this.minHeight = height;
31562 if(this.rotate == 90 || this.rotate == 270){
31563 this.minWidth = height;
31564 this.minHeight = width;
31569 width = Math.ceil(this.minWidth * height / this.minHeight);
31571 if(this.minWidth > this.minHeight){
31573 height = Math.ceil(this.minHeight * width / this.minWidth);
31576 this.thumbEl.setStyle({
31577 width : width + 'px',
31578 height : height + 'px'
31585 setThumbBoxPosition : function()
31587 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31588 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31590 this.thumbEl.setLeft(x);
31591 this.thumbEl.setTop(y);
31595 baseRotateLevel : function()
31597 this.baseRotate = 1;
31600 typeof(this.exif) != 'undefined' &&
31601 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31602 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31604 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31607 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31611 baseScaleLevel : function()
31615 if(this.isDocument){
31617 if(this.baseRotate == 6 || this.baseRotate == 8){
31619 height = this.thumbEl.getHeight();
31620 this.baseScale = height / this.imageEl.OriginWidth;
31622 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31623 width = this.thumbEl.getWidth();
31624 this.baseScale = width / this.imageEl.OriginHeight;
31630 height = this.thumbEl.getHeight();
31631 this.baseScale = height / this.imageEl.OriginHeight;
31633 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31634 width = this.thumbEl.getWidth();
31635 this.baseScale = width / this.imageEl.OriginWidth;
31641 if(this.baseRotate == 6 || this.baseRotate == 8){
31643 width = this.thumbEl.getHeight();
31644 this.baseScale = width / this.imageEl.OriginHeight;
31646 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31647 height = this.thumbEl.getWidth();
31648 this.baseScale = height / this.imageEl.OriginHeight;
31651 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31652 height = this.thumbEl.getWidth();
31653 this.baseScale = height / this.imageEl.OriginHeight;
31655 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31656 width = this.thumbEl.getHeight();
31657 this.baseScale = width / this.imageEl.OriginWidth;
31664 width = this.thumbEl.getWidth();
31665 this.baseScale = width / this.imageEl.OriginWidth;
31667 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31668 height = this.thumbEl.getHeight();
31669 this.baseScale = height / this.imageEl.OriginHeight;
31672 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31674 height = this.thumbEl.getHeight();
31675 this.baseScale = height / this.imageEl.OriginHeight;
31677 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31678 width = this.thumbEl.getWidth();
31679 this.baseScale = width / this.imageEl.OriginWidth;
31687 getScaleLevel : function()
31689 return this.baseScale * Math.pow(1.1, this.scale);
31692 onTouchStart : function(e)
31694 if(!this.canvasLoaded){
31695 this.beforeSelectFile(e);
31699 var touches = e.browserEvent.touches;
31705 if(touches.length == 1){
31706 this.onMouseDown(e);
31710 if(touches.length != 2){
31716 for(var i = 0, finger; finger = touches[i]; i++){
31717 coords.push(finger.pageX, finger.pageY);
31720 var x = Math.pow(coords[0] - coords[2], 2);
31721 var y = Math.pow(coords[1] - coords[3], 2);
31723 this.startDistance = Math.sqrt(x + y);
31725 this.startScale = this.scale;
31727 this.pinching = true;
31728 this.dragable = false;
31732 onTouchMove : function(e)
31734 if(!this.pinching && !this.dragable){
31738 var touches = e.browserEvent.touches;
31745 this.onMouseMove(e);
31751 for(var i = 0, finger; finger = touches[i]; i++){
31752 coords.push(finger.pageX, finger.pageY);
31755 var x = Math.pow(coords[0] - coords[2], 2);
31756 var y = Math.pow(coords[1] - coords[3], 2);
31758 this.endDistance = Math.sqrt(x + y);
31760 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31762 if(!this.zoomable()){
31763 this.scale = this.startScale;
31771 onTouchEnd : function(e)
31773 this.pinching = false;
31774 this.dragable = false;
31778 process : function(file, crop)
31781 this.maskEl.mask(this.loadingText);
31784 this.xhr = new XMLHttpRequest();
31786 file.xhr = this.xhr;
31788 this.xhr.open(this.method, this.url, true);
31791 "Accept": "application/json",
31792 "Cache-Control": "no-cache",
31793 "X-Requested-With": "XMLHttpRequest"
31796 for (var headerName in headers) {
31797 var headerValue = headers[headerName];
31799 this.xhr.setRequestHeader(headerName, headerValue);
31805 this.xhr.onload = function()
31807 _this.xhrOnLoad(_this.xhr);
31810 this.xhr.onerror = function()
31812 _this.xhrOnError(_this.xhr);
31815 var formData = new FormData();
31817 formData.append('returnHTML', 'NO');
31820 formData.append('crop', crop);
31823 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31824 formData.append(this.paramName, file, file.name);
31827 if(typeof(file.filename) != 'undefined'){
31828 formData.append('filename', file.filename);
31831 if(typeof(file.mimetype) != 'undefined'){
31832 formData.append('mimetype', file.mimetype);
31835 if(this.fireEvent('arrange', this, formData) != false){
31836 this.xhr.send(formData);
31840 xhrOnLoad : function(xhr)
31843 this.maskEl.unmask();
31846 if (xhr.readyState !== 4) {
31847 this.fireEvent('exception', this, xhr);
31851 var response = Roo.decode(xhr.responseText);
31853 if(!response.success){
31854 this.fireEvent('exception', this, xhr);
31858 var response = Roo.decode(xhr.responseText);
31860 this.fireEvent('upload', this, response);
31864 xhrOnError : function()
31867 this.maskEl.unmask();
31870 Roo.log('xhr on error');
31872 var response = Roo.decode(xhr.responseText);
31878 prepare : function(file)
31881 this.maskEl.mask(this.loadingText);
31887 if(typeof(file) === 'string'){
31888 this.loadCanvas(file);
31892 if(!file || !this.urlAPI){
31897 this.cropType = file.type;
31901 if(this.fireEvent('prepare', this, this.file) != false){
31903 var reader = new FileReader();
31905 reader.onload = function (e) {
31906 if (e.target.error) {
31907 Roo.log(e.target.error);
31911 var buffer = e.target.result,
31912 dataView = new DataView(buffer),
31914 maxOffset = dataView.byteLength - 4,
31918 if (dataView.getUint16(0) === 0xffd8) {
31919 while (offset < maxOffset) {
31920 markerBytes = dataView.getUint16(offset);
31922 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31923 markerLength = dataView.getUint16(offset + 2) + 2;
31924 if (offset + markerLength > dataView.byteLength) {
31925 Roo.log('Invalid meta data: Invalid segment size.');
31929 if(markerBytes == 0xffe1){
31930 _this.parseExifData(
31937 offset += markerLength;
31947 var url = _this.urlAPI.createObjectURL(_this.file);
31949 _this.loadCanvas(url);
31954 reader.readAsArrayBuffer(this.file);
31960 parseExifData : function(dataView, offset, length)
31962 var tiffOffset = offset + 10,
31966 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31967 // No Exif data, might be XMP data instead
31971 // Check for the ASCII code for "Exif" (0x45786966):
31972 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31973 // No Exif data, might be XMP data instead
31976 if (tiffOffset + 8 > dataView.byteLength) {
31977 Roo.log('Invalid Exif data: Invalid segment size.');
31980 // Check for the two null bytes:
31981 if (dataView.getUint16(offset + 8) !== 0x0000) {
31982 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31985 // Check the byte alignment:
31986 switch (dataView.getUint16(tiffOffset)) {
31988 littleEndian = true;
31991 littleEndian = false;
31994 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31997 // Check for the TIFF tag marker (0x002A):
31998 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31999 Roo.log('Invalid Exif data: Missing TIFF marker.');
32002 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32003 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32005 this.parseExifTags(
32008 tiffOffset + dirOffset,
32013 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32018 if (dirOffset + 6 > dataView.byteLength) {
32019 Roo.log('Invalid Exif data: Invalid directory offset.');
32022 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32023 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32024 if (dirEndOffset + 4 > dataView.byteLength) {
32025 Roo.log('Invalid Exif data: Invalid directory size.');
32028 for (i = 0; i < tagsNumber; i += 1) {
32032 dirOffset + 2 + 12 * i, // tag offset
32036 // Return the offset to the next directory:
32037 return dataView.getUint32(dirEndOffset, littleEndian);
32040 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32042 var tag = dataView.getUint16(offset, littleEndian);
32044 this.exif[tag] = this.getExifValue(
32048 dataView.getUint16(offset + 2, littleEndian), // tag type
32049 dataView.getUint32(offset + 4, littleEndian), // tag length
32054 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32056 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32065 Roo.log('Invalid Exif data: Invalid tag type.');
32069 tagSize = tagType.size * length;
32070 // Determine if the value is contained in the dataOffset bytes,
32071 // or if the value at the dataOffset is a pointer to the actual data:
32072 dataOffset = tagSize > 4 ?
32073 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32074 if (dataOffset + tagSize > dataView.byteLength) {
32075 Roo.log('Invalid Exif data: Invalid data offset.');
32078 if (length === 1) {
32079 return tagType.getValue(dataView, dataOffset, littleEndian);
32082 for (i = 0; i < length; i += 1) {
32083 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32086 if (tagType.ascii) {
32088 // Concatenate the chars:
32089 for (i = 0; i < values.length; i += 1) {
32091 // Ignore the terminating NULL byte(s):
32092 if (c === '\u0000') {
32104 Roo.apply(Roo.bootstrap.UploadCropbox, {
32106 'Orientation': 0x0112
32110 1: 0, //'top-left',
32112 3: 180, //'bottom-right',
32113 // 4: 'bottom-left',
32115 6: 90, //'right-top',
32116 // 7: 'right-bottom',
32117 8: 270 //'left-bottom'
32121 // byte, 8-bit unsigned int:
32123 getValue: function (dataView, dataOffset) {
32124 return dataView.getUint8(dataOffset);
32128 // ascii, 8-bit byte:
32130 getValue: function (dataView, dataOffset) {
32131 return String.fromCharCode(dataView.getUint8(dataOffset));
32136 // short, 16 bit int:
32138 getValue: function (dataView, dataOffset, littleEndian) {
32139 return dataView.getUint16(dataOffset, littleEndian);
32143 // long, 32 bit int:
32145 getValue: function (dataView, dataOffset, littleEndian) {
32146 return dataView.getUint32(dataOffset, littleEndian);
32150 // rational = two long values, first is numerator, second is denominator:
32152 getValue: function (dataView, dataOffset, littleEndian) {
32153 return dataView.getUint32(dataOffset, littleEndian) /
32154 dataView.getUint32(dataOffset + 4, littleEndian);
32158 // slong, 32 bit signed int:
32160 getValue: function (dataView, dataOffset, littleEndian) {
32161 return dataView.getInt32(dataOffset, littleEndian);
32165 // srational, two slongs, first is numerator, second is denominator:
32167 getValue: function (dataView, dataOffset, littleEndian) {
32168 return dataView.getInt32(dataOffset, littleEndian) /
32169 dataView.getInt32(dataOffset + 4, littleEndian);
32179 cls : 'btn-group roo-upload-cropbox-rotate-left',
32180 action : 'rotate-left',
32184 cls : 'btn btn-default',
32185 html : '<i class="fa fa-undo"></i>'
32191 cls : 'btn-group roo-upload-cropbox-picture',
32192 action : 'picture',
32196 cls : 'btn btn-default',
32197 html : '<i class="fa fa-picture-o"></i>'
32203 cls : 'btn-group roo-upload-cropbox-rotate-right',
32204 action : 'rotate-right',
32208 cls : 'btn btn-default',
32209 html : '<i class="fa fa-repeat"></i>'
32217 cls : 'btn-group roo-upload-cropbox-rotate-left',
32218 action : 'rotate-left',
32222 cls : 'btn btn-default',
32223 html : '<i class="fa fa-undo"></i>'
32229 cls : 'btn-group roo-upload-cropbox-download',
32230 action : 'download',
32234 cls : 'btn btn-default',
32235 html : '<i class="fa fa-download"></i>'
32241 cls : 'btn-group roo-upload-cropbox-crop',
32246 cls : 'btn btn-default',
32247 html : '<i class="fa fa-crop"></i>'
32253 cls : 'btn-group roo-upload-cropbox-trash',
32258 cls : 'btn btn-default',
32259 html : '<i class="fa fa-trash"></i>'
32265 cls : 'btn-group roo-upload-cropbox-rotate-right',
32266 action : 'rotate-right',
32270 cls : 'btn btn-default',
32271 html : '<i class="fa fa-repeat"></i>'
32279 cls : 'btn-group roo-upload-cropbox-rotate-left',
32280 action : 'rotate-left',
32284 cls : 'btn btn-default',
32285 html : '<i class="fa fa-undo"></i>'
32291 cls : 'btn-group roo-upload-cropbox-rotate-right',
32292 action : 'rotate-right',
32296 cls : 'btn btn-default',
32297 html : '<i class="fa fa-repeat"></i>'
32310 * @class Roo.bootstrap.DocumentManager
32311 * @extends Roo.bootstrap.Component
32312 * Bootstrap DocumentManager class
32313 * @cfg {String} paramName default 'imageUpload'
32314 * @cfg {String} toolTipName default 'filename'
32315 * @cfg {String} method default POST
32316 * @cfg {String} url action url
32317 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32318 * @cfg {Boolean} multiple multiple upload default true
32319 * @cfg {Number} thumbSize default 300
32320 * @cfg {String} fieldLabel
32321 * @cfg {Number} labelWidth default 4
32322 * @cfg {String} labelAlign (left|top) default left
32323 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32324 * @cfg {Number} labellg set the width of label (1-12)
32325 * @cfg {Number} labelmd set the width of label (1-12)
32326 * @cfg {Number} labelsm set the width of label (1-12)
32327 * @cfg {Number} labelxs set the width of label (1-12)
32330 * Create a new DocumentManager
32331 * @param {Object} config The config object
32334 Roo.bootstrap.DocumentManager = function(config){
32335 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32338 this.delegates = [];
32343 * Fire when initial the DocumentManager
32344 * @param {Roo.bootstrap.DocumentManager} this
32349 * inspect selected file
32350 * @param {Roo.bootstrap.DocumentManager} this
32351 * @param {File} file
32356 * Fire when xhr load exception
32357 * @param {Roo.bootstrap.DocumentManager} this
32358 * @param {XMLHttpRequest} xhr
32360 "exception" : true,
32362 * @event afterupload
32363 * Fire when xhr load exception
32364 * @param {Roo.bootstrap.DocumentManager} this
32365 * @param {XMLHttpRequest} xhr
32367 "afterupload" : true,
32370 * prepare the form data
32371 * @param {Roo.bootstrap.DocumentManager} this
32372 * @param {Object} formData
32377 * Fire when remove the file
32378 * @param {Roo.bootstrap.DocumentManager} this
32379 * @param {Object} file
32384 * Fire after refresh the file
32385 * @param {Roo.bootstrap.DocumentManager} this
32390 * Fire after click the image
32391 * @param {Roo.bootstrap.DocumentManager} this
32392 * @param {Object} file
32397 * Fire when upload a image and editable set to true
32398 * @param {Roo.bootstrap.DocumentManager} this
32399 * @param {Object} file
32403 * @event beforeselectfile
32404 * Fire before select file
32405 * @param {Roo.bootstrap.DocumentManager} this
32407 "beforeselectfile" : true,
32410 * Fire before process file
32411 * @param {Roo.bootstrap.DocumentManager} this
32412 * @param {Object} file
32416 * @event previewrendered
32417 * Fire when preview rendered
32418 * @param {Roo.bootstrap.DocumentManager} this
32419 * @param {Object} file
32421 "previewrendered" : true,
32424 "previewResize" : true
32429 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32438 paramName : 'imageUpload',
32439 toolTipName : 'filename',
32442 labelAlign : 'left',
32452 getAutoCreate : function()
32454 var managerWidget = {
32456 cls : 'roo-document-manager',
32460 cls : 'roo-document-manager-selector',
32465 cls : 'roo-document-manager-uploader',
32469 cls : 'roo-document-manager-upload-btn',
32470 html : '<i class="fa fa-plus"></i>'
32481 cls : 'column col-md-12',
32486 if(this.fieldLabel.length){
32491 cls : 'column col-md-12',
32492 html : this.fieldLabel
32496 cls : 'column col-md-12',
32501 if(this.labelAlign == 'left'){
32506 html : this.fieldLabel
32515 if(this.labelWidth > 12){
32516 content[0].style = "width: " + this.labelWidth + 'px';
32519 if(this.labelWidth < 13 && this.labelmd == 0){
32520 this.labelmd = this.labelWidth;
32523 if(this.labellg > 0){
32524 content[0].cls += ' col-lg-' + this.labellg;
32525 content[1].cls += ' col-lg-' + (12 - this.labellg);
32528 if(this.labelmd > 0){
32529 content[0].cls += ' col-md-' + this.labelmd;
32530 content[1].cls += ' col-md-' + (12 - this.labelmd);
32533 if(this.labelsm > 0){
32534 content[0].cls += ' col-sm-' + this.labelsm;
32535 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32538 if(this.labelxs > 0){
32539 content[0].cls += ' col-xs-' + this.labelxs;
32540 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32548 cls : 'row clearfix',
32556 initEvents : function()
32558 this.managerEl = this.el.select('.roo-document-manager', true).first();
32559 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32561 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32562 this.selectorEl.hide();
32565 this.selectorEl.attr('multiple', 'multiple');
32568 this.selectorEl.on('change', this.onFileSelected, this);
32570 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32571 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32573 this.uploader.on('click', this.onUploaderClick, this);
32575 this.renderProgressDialog();
32579 window.addEventListener("resize", function() { _this.refresh(); } );
32581 this.fireEvent('initial', this);
32584 renderProgressDialog : function()
32588 this.progressDialog = new Roo.bootstrap.Modal({
32589 cls : 'roo-document-manager-progress-dialog',
32590 allow_close : false,
32601 btnclick : function() {
32602 _this.uploadCancel();
32608 this.progressDialog.render(Roo.get(document.body));
32610 this.progress = new Roo.bootstrap.Progress({
32611 cls : 'roo-document-manager-progress',
32616 this.progress.render(this.progressDialog.getChildContainer());
32618 this.progressBar = new Roo.bootstrap.ProgressBar({
32619 cls : 'roo-document-manager-progress-bar',
32622 aria_valuemax : 12,
32626 this.progressBar.render(this.progress.getChildContainer());
32629 onUploaderClick : function(e)
32631 e.preventDefault();
32633 if(this.fireEvent('beforeselectfile', this) != false){
32634 this.selectorEl.dom.click();
32639 onFileSelected : function(e)
32641 e.preventDefault();
32643 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32647 Roo.each(this.selectorEl.dom.files, function(file){
32648 if(this.fireEvent('inspect', this, file) != false){
32649 this.files.push(file);
32659 this.selectorEl.dom.value = '';
32661 if(!this.files || !this.files.length){
32665 if(this.boxes > 0 && this.files.length > this.boxes){
32666 this.files = this.files.slice(0, this.boxes);
32669 this.uploader.show();
32671 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32672 this.uploader.hide();
32681 Roo.each(this.files, function(file){
32683 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32684 var f = this.renderPreview(file);
32689 if(file.type.indexOf('image') != -1){
32690 this.delegates.push(
32692 _this.process(file);
32693 }).createDelegate(this)
32701 _this.process(file);
32702 }).createDelegate(this)
32707 this.files = files;
32709 this.delegates = this.delegates.concat(docs);
32711 if(!this.delegates.length){
32716 this.progressBar.aria_valuemax = this.delegates.length;
32723 arrange : function()
32725 if(!this.delegates.length){
32726 this.progressDialog.hide();
32731 var delegate = this.delegates.shift();
32733 this.progressDialog.show();
32735 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32737 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32742 refresh : function()
32744 this.uploader.show();
32746 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32747 this.uploader.hide();
32750 Roo.isTouch ? this.closable(false) : this.closable(true);
32752 this.fireEvent('refresh', this);
32755 onRemove : function(e, el, o)
32757 e.preventDefault();
32759 this.fireEvent('remove', this, o);
32763 remove : function(o)
32767 Roo.each(this.files, function(file){
32768 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32777 this.files = files;
32784 Roo.each(this.files, function(file){
32789 file.target.remove();
32798 onClick : function(e, el, o)
32800 e.preventDefault();
32802 this.fireEvent('click', this, o);
32806 closable : function(closable)
32808 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32810 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32822 xhrOnLoad : function(xhr)
32824 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32828 if (xhr.readyState !== 4) {
32830 this.fireEvent('exception', this, xhr);
32834 var response = Roo.decode(xhr.responseText);
32836 if(!response.success){
32838 this.fireEvent('exception', this, xhr);
32842 var file = this.renderPreview(response.data);
32844 this.files.push(file);
32848 this.fireEvent('afterupload', this, xhr);
32852 xhrOnError : function(xhr)
32854 Roo.log('xhr on error');
32856 var response = Roo.decode(xhr.responseText);
32863 process : function(file)
32865 if(this.fireEvent('process', this, file) !== false){
32866 if(this.editable && file.type.indexOf('image') != -1){
32867 this.fireEvent('edit', this, file);
32871 this.uploadStart(file, false);
32878 uploadStart : function(file, crop)
32880 this.xhr = new XMLHttpRequest();
32882 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32887 file.xhr = this.xhr;
32889 this.managerEl.createChild({
32891 cls : 'roo-document-manager-loading',
32895 tooltip : file.name,
32896 cls : 'roo-document-manager-thumb',
32897 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32903 this.xhr.open(this.method, this.url, true);
32906 "Accept": "application/json",
32907 "Cache-Control": "no-cache",
32908 "X-Requested-With": "XMLHttpRequest"
32911 for (var headerName in headers) {
32912 var headerValue = headers[headerName];
32914 this.xhr.setRequestHeader(headerName, headerValue);
32920 this.xhr.onload = function()
32922 _this.xhrOnLoad(_this.xhr);
32925 this.xhr.onerror = function()
32927 _this.xhrOnError(_this.xhr);
32930 var formData = new FormData();
32932 formData.append('returnHTML', 'NO');
32935 formData.append('crop', crop);
32938 formData.append(this.paramName, file, file.name);
32945 if(this.fireEvent('prepare', this, formData, options) != false){
32947 if(options.manually){
32951 this.xhr.send(formData);
32955 this.uploadCancel();
32958 uploadCancel : function()
32964 this.delegates = [];
32966 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32973 renderPreview : function(file)
32975 if(typeof(file.target) != 'undefined' && file.target){
32979 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32981 var previewEl = this.managerEl.createChild({
32983 cls : 'roo-document-manager-preview',
32987 tooltip : file[this.toolTipName],
32988 cls : 'roo-document-manager-thumb',
32989 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32994 html : '<i class="fa fa-times-circle"></i>'
32999 var close = previewEl.select('button.close', true).first();
33001 close.on('click', this.onRemove, this, file);
33003 file.target = previewEl;
33005 var image = previewEl.select('img', true).first();
33009 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33011 image.on('click', this.onClick, this, file);
33013 this.fireEvent('previewrendered', this, file);
33019 onPreviewLoad : function(file, image)
33021 if(typeof(file.target) == 'undefined' || !file.target){
33025 var width = image.dom.naturalWidth || image.dom.width;
33026 var height = image.dom.naturalHeight || image.dom.height;
33028 if(!this.previewResize) {
33032 if(width > height){
33033 file.target.addClass('wide');
33037 file.target.addClass('tall');
33042 uploadFromSource : function(file, crop)
33044 this.xhr = new XMLHttpRequest();
33046 this.managerEl.createChild({
33048 cls : 'roo-document-manager-loading',
33052 tooltip : file.name,
33053 cls : 'roo-document-manager-thumb',
33054 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33060 this.xhr.open(this.method, this.url, true);
33063 "Accept": "application/json",
33064 "Cache-Control": "no-cache",
33065 "X-Requested-With": "XMLHttpRequest"
33068 for (var headerName in headers) {
33069 var headerValue = headers[headerName];
33071 this.xhr.setRequestHeader(headerName, headerValue);
33077 this.xhr.onload = function()
33079 _this.xhrOnLoad(_this.xhr);
33082 this.xhr.onerror = function()
33084 _this.xhrOnError(_this.xhr);
33087 var formData = new FormData();
33089 formData.append('returnHTML', 'NO');
33091 formData.append('crop', crop);
33093 if(typeof(file.filename) != 'undefined'){
33094 formData.append('filename', file.filename);
33097 if(typeof(file.mimetype) != 'undefined'){
33098 formData.append('mimetype', file.mimetype);
33103 if(this.fireEvent('prepare', this, formData) != false){
33104 this.xhr.send(formData);
33114 * @class Roo.bootstrap.DocumentViewer
33115 * @extends Roo.bootstrap.Component
33116 * Bootstrap DocumentViewer class
33117 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33118 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33121 * Create a new DocumentViewer
33122 * @param {Object} config The config object
33125 Roo.bootstrap.DocumentViewer = function(config){
33126 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33131 * Fire after initEvent
33132 * @param {Roo.bootstrap.DocumentViewer} this
33138 * @param {Roo.bootstrap.DocumentViewer} this
33143 * Fire after download button
33144 * @param {Roo.bootstrap.DocumentViewer} this
33149 * Fire after trash button
33150 * @param {Roo.bootstrap.DocumentViewer} this
33157 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33159 showDownload : true,
33163 getAutoCreate : function()
33167 cls : 'roo-document-viewer',
33171 cls : 'roo-document-viewer-body',
33175 cls : 'roo-document-viewer-thumb',
33179 cls : 'roo-document-viewer-image'
33187 cls : 'roo-document-viewer-footer',
33190 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33194 cls : 'btn-group roo-document-viewer-download',
33198 cls : 'btn btn-default',
33199 html : '<i class="fa fa-download"></i>'
33205 cls : 'btn-group roo-document-viewer-trash',
33209 cls : 'btn btn-default',
33210 html : '<i class="fa fa-trash"></i>'
33223 initEvents : function()
33225 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33226 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33228 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33229 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33231 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33232 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33234 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33235 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33237 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33238 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33240 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33241 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33243 this.bodyEl.on('click', this.onClick, this);
33244 this.downloadBtn.on('click', this.onDownload, this);
33245 this.trashBtn.on('click', this.onTrash, this);
33247 this.downloadBtn.hide();
33248 this.trashBtn.hide();
33250 if(this.showDownload){
33251 this.downloadBtn.show();
33254 if(this.showTrash){
33255 this.trashBtn.show();
33258 if(!this.showDownload && !this.showTrash) {
33259 this.footerEl.hide();
33264 initial : function()
33266 this.fireEvent('initial', this);
33270 onClick : function(e)
33272 e.preventDefault();
33274 this.fireEvent('click', this);
33277 onDownload : function(e)
33279 e.preventDefault();
33281 this.fireEvent('download', this);
33284 onTrash : function(e)
33286 e.preventDefault();
33288 this.fireEvent('trash', this);
33300 * @class Roo.bootstrap.NavProgressBar
33301 * @extends Roo.bootstrap.Component
33302 * Bootstrap NavProgressBar class
33305 * Create a new nav progress bar
33306 * @param {Object} config The config object
33309 Roo.bootstrap.NavProgressBar = function(config){
33310 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33312 this.bullets = this.bullets || [];
33314 // Roo.bootstrap.NavProgressBar.register(this);
33318 * Fires when the active item changes
33319 * @param {Roo.bootstrap.NavProgressBar} this
33320 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33321 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33328 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33333 getAutoCreate : function()
33335 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33339 cls : 'roo-navigation-bar-group',
33343 cls : 'roo-navigation-top-bar'
33347 cls : 'roo-navigation-bullets-bar',
33351 cls : 'roo-navigation-bar'
33358 cls : 'roo-navigation-bottom-bar'
33368 initEvents: function()
33373 onRender : function(ct, position)
33375 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33377 if(this.bullets.length){
33378 Roo.each(this.bullets, function(b){
33387 addItem : function(cfg)
33389 var item = new Roo.bootstrap.NavProgressItem(cfg);
33391 item.parentId = this.id;
33392 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33395 var top = new Roo.bootstrap.Element({
33397 cls : 'roo-navigation-bar-text'
33400 var bottom = new Roo.bootstrap.Element({
33402 cls : 'roo-navigation-bar-text'
33405 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33406 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33408 var topText = new Roo.bootstrap.Element({
33410 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33413 var bottomText = new Roo.bootstrap.Element({
33415 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33418 topText.onRender(top.el, null);
33419 bottomText.onRender(bottom.el, null);
33422 item.bottomEl = bottom;
33425 this.barItems.push(item);
33430 getActive : function()
33432 var active = false;
33434 Roo.each(this.barItems, function(v){
33436 if (!v.isActive()) {
33448 setActiveItem : function(item)
33452 Roo.each(this.barItems, function(v){
33453 if (v.rid == item.rid) {
33457 if (v.isActive()) {
33458 v.setActive(false);
33463 item.setActive(true);
33465 this.fireEvent('changed', this, item, prev);
33468 getBarItem: function(rid)
33472 Roo.each(this.barItems, function(e) {
33473 if (e.rid != rid) {
33484 indexOfItem : function(item)
33488 Roo.each(this.barItems, function(v, i){
33490 if (v.rid != item.rid) {
33501 setActiveNext : function()
33503 var i = this.indexOfItem(this.getActive());
33505 if (i > this.barItems.length) {
33509 this.setActiveItem(this.barItems[i+1]);
33512 setActivePrev : function()
33514 var i = this.indexOfItem(this.getActive());
33520 this.setActiveItem(this.barItems[i-1]);
33523 format : function()
33525 if(!this.barItems.length){
33529 var width = 100 / this.barItems.length;
33531 Roo.each(this.barItems, function(i){
33532 i.el.setStyle('width', width + '%');
33533 i.topEl.el.setStyle('width', width + '%');
33534 i.bottomEl.el.setStyle('width', width + '%');
33543 * Nav Progress Item
33548 * @class Roo.bootstrap.NavProgressItem
33549 * @extends Roo.bootstrap.Component
33550 * Bootstrap NavProgressItem class
33551 * @cfg {String} rid the reference id
33552 * @cfg {Boolean} active (true|false) Is item active default false
33553 * @cfg {Boolean} disabled (true|false) Is item active default false
33554 * @cfg {String} html
33555 * @cfg {String} position (top|bottom) text position default bottom
33556 * @cfg {String} icon show icon instead of number
33559 * Create a new NavProgressItem
33560 * @param {Object} config The config object
33562 Roo.bootstrap.NavProgressItem = function(config){
33563 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33568 * The raw click event for the entire grid.
33569 * @param {Roo.bootstrap.NavProgressItem} this
33570 * @param {Roo.EventObject} e
33577 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33583 position : 'bottom',
33586 getAutoCreate : function()
33588 var iconCls = 'roo-navigation-bar-item-icon';
33590 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33594 cls: 'roo-navigation-bar-item',
33604 cfg.cls += ' active';
33607 cfg.cls += ' disabled';
33613 disable : function()
33615 this.setDisabled(true);
33618 enable : function()
33620 this.setDisabled(false);
33623 initEvents: function()
33625 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33627 this.iconEl.on('click', this.onClick, this);
33630 onClick : function(e)
33632 e.preventDefault();
33638 if(this.fireEvent('click', this, e) === false){
33642 this.parent().setActiveItem(this);
33645 isActive: function ()
33647 return this.active;
33650 setActive : function(state)
33652 if(this.active == state){
33656 this.active = state;
33659 this.el.addClass('active');
33663 this.el.removeClass('active');
33668 setDisabled : function(state)
33670 if(this.disabled == state){
33674 this.disabled = state;
33677 this.el.addClass('disabled');
33681 this.el.removeClass('disabled');
33684 tooltipEl : function()
33686 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33699 * @class Roo.bootstrap.FieldLabel
33700 * @extends Roo.bootstrap.Component
33701 * Bootstrap FieldLabel class
33702 * @cfg {String} html contents of the element
33703 * @cfg {String} tag tag of the element default label
33704 * @cfg {String} cls class of the element
33705 * @cfg {String} target label target
33706 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33707 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33708 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33709 * @cfg {String} iconTooltip default "This field is required"
33710 * @cfg {String} indicatorpos (left|right) default left
33713 * Create a new FieldLabel
33714 * @param {Object} config The config object
33717 Roo.bootstrap.FieldLabel = function(config){
33718 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33723 * Fires after the field has been marked as invalid.
33724 * @param {Roo.form.FieldLabel} this
33725 * @param {String} msg The validation message
33730 * Fires after the field has been validated with no errors.
33731 * @param {Roo.form.FieldLabel} this
33737 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33744 invalidClass : 'has-warning',
33745 validClass : 'has-success',
33746 iconTooltip : 'This field is required',
33747 indicatorpos : 'left',
33749 getAutoCreate : function(){
33752 if (!this.allowBlank) {
33758 cls : 'roo-bootstrap-field-label ' + this.cls,
33763 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33764 tooltip : this.iconTooltip
33773 if(this.indicatorpos == 'right'){
33776 cls : 'roo-bootstrap-field-label ' + this.cls,
33785 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33786 tooltip : this.iconTooltip
33795 initEvents: function()
33797 Roo.bootstrap.Element.superclass.initEvents.call(this);
33799 this.indicator = this.indicatorEl();
33801 if(this.indicator){
33802 this.indicator.removeClass('visible');
33803 this.indicator.addClass('invisible');
33806 Roo.bootstrap.FieldLabel.register(this);
33809 indicatorEl : function()
33811 var indicator = this.el.select('i.roo-required-indicator',true).first();
33822 * Mark this field as valid
33824 markValid : function()
33826 if(this.indicator){
33827 this.indicator.removeClass('visible');
33828 this.indicator.addClass('invisible');
33830 if (Roo.bootstrap.version == 3) {
33831 this.el.removeClass(this.invalidClass);
33832 this.el.addClass(this.validClass);
33834 this.el.removeClass('is-invalid');
33835 this.el.addClass('is-valid');
33839 this.fireEvent('valid', this);
33843 * Mark this field as invalid
33844 * @param {String} msg The validation message
33846 markInvalid : function(msg)
33848 if(this.indicator){
33849 this.indicator.removeClass('invisible');
33850 this.indicator.addClass('visible');
33852 if (Roo.bootstrap.version == 3) {
33853 this.el.removeClass(this.validClass);
33854 this.el.addClass(this.invalidClass);
33856 this.el.removeClass('is-valid');
33857 this.el.addClass('is-invalid');
33861 this.fireEvent('invalid', this, msg);
33867 Roo.apply(Roo.bootstrap.FieldLabel, {
33872 * register a FieldLabel Group
33873 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33875 register : function(label)
33877 if(this.groups.hasOwnProperty(label.target)){
33881 this.groups[label.target] = label;
33885 * fetch a FieldLabel Group based on the target
33886 * @param {string} target
33887 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33889 get: function(target) {
33890 if (typeof(this.groups[target]) == 'undefined') {
33894 return this.groups[target] ;
33903 * page DateSplitField.
33909 * @class Roo.bootstrap.DateSplitField
33910 * @extends Roo.bootstrap.Component
33911 * Bootstrap DateSplitField class
33912 * @cfg {string} fieldLabel - the label associated
33913 * @cfg {Number} labelWidth set the width of label (0-12)
33914 * @cfg {String} labelAlign (top|left)
33915 * @cfg {Boolean} dayAllowBlank (true|false) default false
33916 * @cfg {Boolean} monthAllowBlank (true|false) default false
33917 * @cfg {Boolean} yearAllowBlank (true|false) default false
33918 * @cfg {string} dayPlaceholder
33919 * @cfg {string} monthPlaceholder
33920 * @cfg {string} yearPlaceholder
33921 * @cfg {string} dayFormat default 'd'
33922 * @cfg {string} monthFormat default 'm'
33923 * @cfg {string} yearFormat default 'Y'
33924 * @cfg {Number} labellg set the width of label (1-12)
33925 * @cfg {Number} labelmd set the width of label (1-12)
33926 * @cfg {Number} labelsm set the width of label (1-12)
33927 * @cfg {Number} labelxs set the width of label (1-12)
33931 * Create a new DateSplitField
33932 * @param {Object} config The config object
33935 Roo.bootstrap.DateSplitField = function(config){
33936 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33942 * getting the data of years
33943 * @param {Roo.bootstrap.DateSplitField} this
33944 * @param {Object} years
33949 * getting the data of days
33950 * @param {Roo.bootstrap.DateSplitField} this
33951 * @param {Object} days
33956 * Fires after the field has been marked as invalid.
33957 * @param {Roo.form.Field} this
33958 * @param {String} msg The validation message
33963 * Fires after the field has been validated with no errors.
33964 * @param {Roo.form.Field} this
33970 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33973 labelAlign : 'top',
33975 dayAllowBlank : false,
33976 monthAllowBlank : false,
33977 yearAllowBlank : false,
33978 dayPlaceholder : '',
33979 monthPlaceholder : '',
33980 yearPlaceholder : '',
33984 isFormField : true,
33990 getAutoCreate : function()
33994 cls : 'row roo-date-split-field-group',
33999 cls : 'form-hidden-field roo-date-split-field-group-value',
34005 var labelCls = 'col-md-12';
34006 var contentCls = 'col-md-4';
34008 if(this.fieldLabel){
34012 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34016 html : this.fieldLabel
34021 if(this.labelAlign == 'left'){
34023 if(this.labelWidth > 12){
34024 label.style = "width: " + this.labelWidth + 'px';
34027 if(this.labelWidth < 13 && this.labelmd == 0){
34028 this.labelmd = this.labelWidth;
34031 if(this.labellg > 0){
34032 labelCls = ' col-lg-' + this.labellg;
34033 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34036 if(this.labelmd > 0){
34037 labelCls = ' col-md-' + this.labelmd;
34038 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34041 if(this.labelsm > 0){
34042 labelCls = ' col-sm-' + this.labelsm;
34043 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34046 if(this.labelxs > 0){
34047 labelCls = ' col-xs-' + this.labelxs;
34048 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34052 label.cls += ' ' + labelCls;
34054 cfg.cn.push(label);
34057 Roo.each(['day', 'month', 'year'], function(t){
34060 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34067 inputEl: function ()
34069 return this.el.select('.roo-date-split-field-group-value', true).first();
34072 onRender : function(ct, position)
34076 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34078 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34080 this.dayField = new Roo.bootstrap.ComboBox({
34081 allowBlank : this.dayAllowBlank,
34082 alwaysQuery : true,
34083 displayField : 'value',
34086 forceSelection : true,
34088 placeholder : this.dayPlaceholder,
34089 selectOnFocus : true,
34090 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34091 triggerAction : 'all',
34093 valueField : 'value',
34094 store : new Roo.data.SimpleStore({
34095 data : (function() {
34097 _this.fireEvent('days', _this, days);
34100 fields : [ 'value' ]
34103 select : function (_self, record, index)
34105 _this.setValue(_this.getValue());
34110 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34112 this.monthField = new Roo.bootstrap.MonthField({
34113 after : '<i class=\"fa fa-calendar\"></i>',
34114 allowBlank : this.monthAllowBlank,
34115 placeholder : this.monthPlaceholder,
34118 render : function (_self)
34120 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34121 e.preventDefault();
34125 select : function (_self, oldvalue, newvalue)
34127 _this.setValue(_this.getValue());
34132 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34134 this.yearField = new Roo.bootstrap.ComboBox({
34135 allowBlank : this.yearAllowBlank,
34136 alwaysQuery : true,
34137 displayField : 'value',
34140 forceSelection : true,
34142 placeholder : this.yearPlaceholder,
34143 selectOnFocus : true,
34144 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34145 triggerAction : 'all',
34147 valueField : 'value',
34148 store : new Roo.data.SimpleStore({
34149 data : (function() {
34151 _this.fireEvent('years', _this, years);
34154 fields : [ 'value' ]
34157 select : function (_self, record, index)
34159 _this.setValue(_this.getValue());
34164 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34167 setValue : function(v, format)
34169 this.inputEl.dom.value = v;
34171 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34173 var d = Date.parseDate(v, f);
34180 this.setDay(d.format(this.dayFormat));
34181 this.setMonth(d.format(this.monthFormat));
34182 this.setYear(d.format(this.yearFormat));
34189 setDay : function(v)
34191 this.dayField.setValue(v);
34192 this.inputEl.dom.value = this.getValue();
34197 setMonth : function(v)
34199 this.monthField.setValue(v, true);
34200 this.inputEl.dom.value = this.getValue();
34205 setYear : function(v)
34207 this.yearField.setValue(v);
34208 this.inputEl.dom.value = this.getValue();
34213 getDay : function()
34215 return this.dayField.getValue();
34218 getMonth : function()
34220 return this.monthField.getValue();
34223 getYear : function()
34225 return this.yearField.getValue();
34228 getValue : function()
34230 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34232 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34242 this.inputEl.dom.value = '';
34247 validate : function()
34249 var d = this.dayField.validate();
34250 var m = this.monthField.validate();
34251 var y = this.yearField.validate();
34256 (!this.dayAllowBlank && !d) ||
34257 (!this.monthAllowBlank && !m) ||
34258 (!this.yearAllowBlank && !y)
34263 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34272 this.markInvalid();
34277 markValid : function()
34280 var label = this.el.select('label', true).first();
34281 var icon = this.el.select('i.fa-star', true).first();
34287 this.fireEvent('valid', this);
34291 * Mark this field as invalid
34292 * @param {String} msg The validation message
34294 markInvalid : function(msg)
34297 var label = this.el.select('label', true).first();
34298 var icon = this.el.select('i.fa-star', true).first();
34300 if(label && !icon){
34301 this.el.select('.roo-date-split-field-label', true).createChild({
34303 cls : 'text-danger fa fa-lg fa-star',
34304 tooltip : 'This field is required',
34305 style : 'margin-right:5px;'
34309 this.fireEvent('invalid', this, msg);
34312 clearInvalid : function()
34314 var label = this.el.select('label', true).first();
34315 var icon = this.el.select('i.fa-star', true).first();
34321 this.fireEvent('valid', this);
34324 getName: function()
34334 * http://masonry.desandro.com
34336 * The idea is to render all the bricks based on vertical width...
34338 * The original code extends 'outlayer' - we might need to use that....
34344 * @class Roo.bootstrap.LayoutMasonry
34345 * @extends Roo.bootstrap.Component
34346 * Bootstrap Layout Masonry class
34349 * Create a new Element
34350 * @param {Object} config The config object
34353 Roo.bootstrap.LayoutMasonry = function(config){
34355 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34359 Roo.bootstrap.LayoutMasonry.register(this);
34365 * Fire after layout the items
34366 * @param {Roo.bootstrap.LayoutMasonry} this
34367 * @param {Roo.EventObject} e
34374 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34377 * @cfg {Boolean} isLayoutInstant = no animation?
34379 isLayoutInstant : false, // needed?
34382 * @cfg {Number} boxWidth width of the columns
34387 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34392 * @cfg {Number} padWidth padding below box..
34397 * @cfg {Number} gutter gutter width..
34402 * @cfg {Number} maxCols maximum number of columns
34408 * @cfg {Boolean} isAutoInitial defalut true
34410 isAutoInitial : true,
34415 * @cfg {Boolean} isHorizontal defalut false
34417 isHorizontal : false,
34419 currentSize : null,
34425 bricks: null, //CompositeElement
34429 _isLayoutInited : false,
34431 // isAlternative : false, // only use for vertical layout...
34434 * @cfg {Number} alternativePadWidth padding below box..
34436 alternativePadWidth : 50,
34438 selectedBrick : [],
34440 getAutoCreate : function(){
34442 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34446 cls: 'blog-masonary-wrapper ' + this.cls,
34448 cls : 'mas-boxes masonary'
34455 getChildContainer: function( )
34457 if (this.boxesEl) {
34458 return this.boxesEl;
34461 this.boxesEl = this.el.select('.mas-boxes').first();
34463 return this.boxesEl;
34467 initEvents : function()
34471 if(this.isAutoInitial){
34472 Roo.log('hook children rendered');
34473 this.on('childrenrendered', function() {
34474 Roo.log('children rendered');
34480 initial : function()
34482 this.selectedBrick = [];
34484 this.currentSize = this.el.getBox(true);
34486 Roo.EventManager.onWindowResize(this.resize, this);
34488 if(!this.isAutoInitial){
34496 //this.layout.defer(500,this);
34500 resize : function()
34502 var cs = this.el.getBox(true);
34505 this.currentSize.width == cs.width &&
34506 this.currentSize.x == cs.x &&
34507 this.currentSize.height == cs.height &&
34508 this.currentSize.y == cs.y
34510 Roo.log("no change in with or X or Y");
34514 this.currentSize = cs;
34520 layout : function()
34522 this._resetLayout();
34524 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34526 this.layoutItems( isInstant );
34528 this._isLayoutInited = true;
34530 this.fireEvent('layout', this);
34534 _resetLayout : function()
34536 if(this.isHorizontal){
34537 this.horizontalMeasureColumns();
34541 this.verticalMeasureColumns();
34545 verticalMeasureColumns : function()
34547 this.getContainerWidth();
34549 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34550 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34554 var boxWidth = this.boxWidth + this.padWidth;
34556 if(this.containerWidth < this.boxWidth){
34557 boxWidth = this.containerWidth
34560 var containerWidth = this.containerWidth;
34562 var cols = Math.floor(containerWidth / boxWidth);
34564 this.cols = Math.max( cols, 1 );
34566 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34568 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34570 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34572 this.colWidth = boxWidth + avail - this.padWidth;
34574 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34575 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34578 horizontalMeasureColumns : function()
34580 this.getContainerWidth();
34582 var boxWidth = this.boxWidth;
34584 if(this.containerWidth < boxWidth){
34585 boxWidth = this.containerWidth;
34588 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34590 this.el.setHeight(boxWidth);
34594 getContainerWidth : function()
34596 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34599 layoutItems : function( isInstant )
34601 Roo.log(this.bricks);
34603 var items = Roo.apply([], this.bricks);
34605 if(this.isHorizontal){
34606 this._horizontalLayoutItems( items , isInstant );
34610 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34611 // this._verticalAlternativeLayoutItems( items , isInstant );
34615 this._verticalLayoutItems( items , isInstant );
34619 _verticalLayoutItems : function ( items , isInstant)
34621 if ( !items || !items.length ) {
34626 ['xs', 'xs', 'xs', 'tall'],
34627 ['xs', 'xs', 'tall'],
34628 ['xs', 'xs', 'sm'],
34629 ['xs', 'xs', 'xs'],
34635 ['sm', 'xs', 'xs'],
34639 ['tall', 'xs', 'xs', 'xs'],
34640 ['tall', 'xs', 'xs'],
34652 Roo.each(items, function(item, k){
34654 switch (item.size) {
34655 // these layouts take up a full box,
34666 boxes.push([item]);
34689 var filterPattern = function(box, length)
34697 var pattern = box.slice(0, length);
34701 Roo.each(pattern, function(i){
34702 format.push(i.size);
34705 Roo.each(standard, function(s){
34707 if(String(s) != String(format)){
34716 if(!match && length == 1){
34721 filterPattern(box, length - 1);
34725 queue.push(pattern);
34727 box = box.slice(length, box.length);
34729 filterPattern(box, 4);
34735 Roo.each(boxes, function(box, k){
34741 if(box.length == 1){
34746 filterPattern(box, 4);
34750 this._processVerticalLayoutQueue( queue, isInstant );
34754 // _verticalAlternativeLayoutItems : function( items , isInstant )
34756 // if ( !items || !items.length ) {
34760 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34764 _horizontalLayoutItems : function ( items , isInstant)
34766 if ( !items || !items.length || items.length < 3) {
34772 var eItems = items.slice(0, 3);
34774 items = items.slice(3, items.length);
34777 ['xs', 'xs', 'xs', 'wide'],
34778 ['xs', 'xs', 'wide'],
34779 ['xs', 'xs', 'sm'],
34780 ['xs', 'xs', 'xs'],
34786 ['sm', 'xs', 'xs'],
34790 ['wide', 'xs', 'xs', 'xs'],
34791 ['wide', 'xs', 'xs'],
34804 Roo.each(items, function(item, k){
34806 switch (item.size) {
34817 boxes.push([item]);
34841 var filterPattern = function(box, length)
34849 var pattern = box.slice(0, length);
34853 Roo.each(pattern, function(i){
34854 format.push(i.size);
34857 Roo.each(standard, function(s){
34859 if(String(s) != String(format)){
34868 if(!match && length == 1){
34873 filterPattern(box, length - 1);
34877 queue.push(pattern);
34879 box = box.slice(length, box.length);
34881 filterPattern(box, 4);
34887 Roo.each(boxes, function(box, k){
34893 if(box.length == 1){
34898 filterPattern(box, 4);
34905 var pos = this.el.getBox(true);
34909 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34911 var hit_end = false;
34913 Roo.each(queue, function(box){
34917 Roo.each(box, function(b){
34919 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34929 Roo.each(box, function(b){
34931 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34934 mx = Math.max(mx, b.x);
34938 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34942 Roo.each(box, function(b){
34944 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34958 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34961 /** Sets position of item in DOM
34962 * @param {Element} item
34963 * @param {Number} x - horizontal position
34964 * @param {Number} y - vertical position
34965 * @param {Boolean} isInstant - disables transitions
34967 _processVerticalLayoutQueue : function( queue, isInstant )
34969 var pos = this.el.getBox(true);
34974 for (var i = 0; i < this.cols; i++){
34978 Roo.each(queue, function(box, k){
34980 var col = k % this.cols;
34982 Roo.each(box, function(b,kk){
34984 b.el.position('absolute');
34986 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34987 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34989 if(b.size == 'md-left' || b.size == 'md-right'){
34990 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34991 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34994 b.el.setWidth(width);
34995 b.el.setHeight(height);
34997 b.el.select('iframe',true).setSize(width,height);
35001 for (var i = 0; i < this.cols; i++){
35003 if(maxY[i] < maxY[col]){
35008 col = Math.min(col, i);
35012 x = pos.x + col * (this.colWidth + this.padWidth);
35016 var positions = [];
35018 switch (box.length){
35020 positions = this.getVerticalOneBoxColPositions(x, y, box);
35023 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35026 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35029 positions = this.getVerticalFourBoxColPositions(x, y, box);
35035 Roo.each(box, function(b,kk){
35037 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35039 var sz = b.el.getSize();
35041 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35049 for (var i = 0; i < this.cols; i++){
35050 mY = Math.max(mY, maxY[i]);
35053 this.el.setHeight(mY - pos.y);
35057 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35059 // var pos = this.el.getBox(true);
35062 // var maxX = pos.right;
35064 // var maxHeight = 0;
35066 // Roo.each(items, function(item, k){
35070 // item.el.position('absolute');
35072 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35074 // item.el.setWidth(width);
35076 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35078 // item.el.setHeight(height);
35081 // item.el.setXY([x, y], isInstant ? false : true);
35083 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35086 // y = y + height + this.alternativePadWidth;
35088 // maxHeight = maxHeight + height + this.alternativePadWidth;
35092 // this.el.setHeight(maxHeight);
35096 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35098 var pos = this.el.getBox(true);
35103 var maxX = pos.right;
35105 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35107 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35109 Roo.each(queue, function(box, k){
35111 Roo.each(box, function(b, kk){
35113 b.el.position('absolute');
35115 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35116 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35118 if(b.size == 'md-left' || b.size == 'md-right'){
35119 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35120 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35123 b.el.setWidth(width);
35124 b.el.setHeight(height);
35132 var positions = [];
35134 switch (box.length){
35136 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35139 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35142 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35145 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35151 Roo.each(box, function(b,kk){
35153 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35155 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35163 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35165 Roo.each(eItems, function(b,k){
35167 b.size = (k == 0) ? 'sm' : 'xs';
35168 b.x = (k == 0) ? 2 : 1;
35169 b.y = (k == 0) ? 2 : 1;
35171 b.el.position('absolute');
35173 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35175 b.el.setWidth(width);
35177 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35179 b.el.setHeight(height);
35183 var positions = [];
35186 x : maxX - this.unitWidth * 2 - this.gutter,
35191 x : maxX - this.unitWidth,
35192 y : minY + (this.unitWidth + this.gutter) * 2
35196 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35200 Roo.each(eItems, function(b,k){
35202 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35208 getVerticalOneBoxColPositions : function(x, y, box)
35212 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35214 if(box[0].size == 'md-left'){
35218 if(box[0].size == 'md-right'){
35223 x : x + (this.unitWidth + this.gutter) * rand,
35230 getVerticalTwoBoxColPositions : function(x, y, box)
35234 if(box[0].size == 'xs'){
35238 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35242 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35256 x : x + (this.unitWidth + this.gutter) * 2,
35257 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35264 getVerticalThreeBoxColPositions : function(x, y, box)
35268 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35276 x : x + (this.unitWidth + this.gutter) * 1,
35281 x : x + (this.unitWidth + this.gutter) * 2,
35289 if(box[0].size == 'xs' && box[1].size == 'xs'){
35298 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35302 x : x + (this.unitWidth + this.gutter) * 1,
35316 x : x + (this.unitWidth + this.gutter) * 2,
35321 x : x + (this.unitWidth + this.gutter) * 2,
35322 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35329 getVerticalFourBoxColPositions : function(x, y, box)
35333 if(box[0].size == 'xs'){
35342 y : y + (this.unitHeight + this.gutter) * 1
35347 y : y + (this.unitHeight + this.gutter) * 2
35351 x : x + (this.unitWidth + this.gutter) * 1,
35365 x : x + (this.unitWidth + this.gutter) * 2,
35370 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35371 y : y + (this.unitHeight + this.gutter) * 1
35375 x : x + (this.unitWidth + this.gutter) * 2,
35376 y : y + (this.unitWidth + this.gutter) * 2
35383 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35387 if(box[0].size == 'md-left'){
35389 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35396 if(box[0].size == 'md-right'){
35398 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35399 y : minY + (this.unitWidth + this.gutter) * 1
35405 var rand = Math.floor(Math.random() * (4 - box[0].y));
35408 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35409 y : minY + (this.unitWidth + this.gutter) * rand
35416 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35420 if(box[0].size == 'xs'){
35423 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35428 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35429 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35437 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35442 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35443 y : minY + (this.unitWidth + this.gutter) * 2
35450 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35454 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35457 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35462 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35463 y : minY + (this.unitWidth + this.gutter) * 1
35467 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35468 y : minY + (this.unitWidth + this.gutter) * 2
35475 if(box[0].size == 'xs' && box[1].size == 'xs'){
35478 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35483 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35488 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35489 y : minY + (this.unitWidth + this.gutter) * 1
35497 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35502 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35503 y : minY + (this.unitWidth + this.gutter) * 2
35507 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35508 y : minY + (this.unitWidth + this.gutter) * 2
35515 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35519 if(box[0].size == 'xs'){
35522 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35527 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].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) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35537 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35538 y : minY + (this.unitWidth + this.gutter) * 1
35546 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35551 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35552 y : minY + (this.unitWidth + this.gutter) * 2
35556 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].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) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35562 y : minY + (this.unitWidth + this.gutter) * 2
35570 * remove a Masonry Brick
35571 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35573 removeBrick : function(brick_id)
35579 for (var i = 0; i<this.bricks.length; i++) {
35580 if (this.bricks[i].id == brick_id) {
35581 this.bricks.splice(i,1);
35582 this.el.dom.removeChild(Roo.get(brick_id).dom);
35589 * adds a Masonry Brick
35590 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35592 addBrick : function(cfg)
35594 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35595 //this.register(cn);
35596 cn.parentId = this.id;
35597 cn.render(this.el);
35602 * register a Masonry Brick
35603 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35606 register : function(brick)
35608 this.bricks.push(brick);
35609 brick.masonryId = this.id;
35613 * clear all the Masonry Brick
35615 clearAll : function()
35618 //this.getChildContainer().dom.innerHTML = "";
35619 this.el.dom.innerHTML = '';
35622 getSelected : function()
35624 if (!this.selectedBrick) {
35628 return this.selectedBrick;
35632 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35636 * register a Masonry Layout
35637 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35640 register : function(layout)
35642 this.groups[layout.id] = layout;
35645 * fetch a Masonry Layout based on the masonry layout ID
35646 * @param {string} the masonry layout to add
35647 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35650 get: function(layout_id) {
35651 if (typeof(this.groups[layout_id]) == 'undefined') {
35654 return this.groups[layout_id] ;
35666 * http://masonry.desandro.com
35668 * The idea is to render all the bricks based on vertical width...
35670 * The original code extends 'outlayer' - we might need to use that....
35676 * @class Roo.bootstrap.LayoutMasonryAuto
35677 * @extends Roo.bootstrap.Component
35678 * Bootstrap Layout Masonry class
35681 * Create a new Element
35682 * @param {Object} config The config object
35685 Roo.bootstrap.LayoutMasonryAuto = function(config){
35686 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35689 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35692 * @cfg {Boolean} isFitWidth - resize the width..
35694 isFitWidth : false, // options..
35696 * @cfg {Boolean} isOriginLeft = left align?
35698 isOriginLeft : true,
35700 * @cfg {Boolean} isOriginTop = top align?
35702 isOriginTop : false,
35704 * @cfg {Boolean} isLayoutInstant = no animation?
35706 isLayoutInstant : false, // needed?
35708 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35710 isResizingContainer : true,
35712 * @cfg {Number} columnWidth width of the columns
35718 * @cfg {Number} maxCols maximum number of columns
35723 * @cfg {Number} padHeight padding below box..
35729 * @cfg {Boolean} isAutoInitial defalut true
35732 isAutoInitial : true,
35738 initialColumnWidth : 0,
35739 currentSize : null,
35741 colYs : null, // array.
35748 bricks: null, //CompositeElement
35749 cols : 0, // array?
35750 // element : null, // wrapped now this.el
35751 _isLayoutInited : null,
35754 getAutoCreate : function(){
35758 cls: 'blog-masonary-wrapper ' + this.cls,
35760 cls : 'mas-boxes masonary'
35767 getChildContainer: function( )
35769 if (this.boxesEl) {
35770 return this.boxesEl;
35773 this.boxesEl = this.el.select('.mas-boxes').first();
35775 return this.boxesEl;
35779 initEvents : function()
35783 if(this.isAutoInitial){
35784 Roo.log('hook children rendered');
35785 this.on('childrenrendered', function() {
35786 Roo.log('children rendered');
35793 initial : function()
35795 this.reloadItems();
35797 this.currentSize = this.el.getBox(true);
35799 /// was window resize... - let's see if this works..
35800 Roo.EventManager.onWindowResize(this.resize, this);
35802 if(!this.isAutoInitial){
35807 this.layout.defer(500,this);
35810 reloadItems: function()
35812 this.bricks = this.el.select('.masonry-brick', true);
35814 this.bricks.each(function(b) {
35815 //Roo.log(b.getSize());
35816 if (!b.attr('originalwidth')) {
35817 b.attr('originalwidth', b.getSize().width);
35822 Roo.log(this.bricks.elements.length);
35825 resize : function()
35828 var cs = this.el.getBox(true);
35830 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35831 Roo.log("no change in with or X");
35834 this.currentSize = cs;
35838 layout : function()
35841 this._resetLayout();
35842 //this._manageStamps();
35844 // don't animate first layout
35845 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35846 this.layoutItems( isInstant );
35848 // flag for initalized
35849 this._isLayoutInited = true;
35852 layoutItems : function( isInstant )
35854 //var items = this._getItemsForLayout( this.items );
35855 // original code supports filtering layout items.. we just ignore it..
35857 this._layoutItems( this.bricks , isInstant );
35859 this._postLayout();
35861 _layoutItems : function ( items , isInstant)
35863 //this.fireEvent( 'layout', this, items );
35866 if ( !items || !items.elements.length ) {
35867 // no items, emit event with empty array
35872 items.each(function(item) {
35873 Roo.log("layout item");
35875 // get x/y object from method
35876 var position = this._getItemLayoutPosition( item );
35878 position.item = item;
35879 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35880 queue.push( position );
35883 this._processLayoutQueue( queue );
35885 /** Sets position of item in DOM
35886 * @param {Element} item
35887 * @param {Number} x - horizontal position
35888 * @param {Number} y - vertical position
35889 * @param {Boolean} isInstant - disables transitions
35891 _processLayoutQueue : function( queue )
35893 for ( var i=0, len = queue.length; i < len; i++ ) {
35894 var obj = queue[i];
35895 obj.item.position('absolute');
35896 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35902 * Any logic you want to do after each layout,
35903 * i.e. size the container
35905 _postLayout : function()
35907 this.resizeContainer();
35910 resizeContainer : function()
35912 if ( !this.isResizingContainer ) {
35915 var size = this._getContainerSize();
35917 this.el.setSize(size.width,size.height);
35918 this.boxesEl.setSize(size.width,size.height);
35924 _resetLayout : function()
35926 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35927 this.colWidth = this.el.getWidth();
35928 //this.gutter = this.el.getWidth();
35930 this.measureColumns();
35936 this.colYs.push( 0 );
35942 measureColumns : function()
35944 this.getContainerWidth();
35945 // if columnWidth is 0, default to outerWidth of first item
35946 if ( !this.columnWidth ) {
35947 var firstItem = this.bricks.first();
35948 Roo.log(firstItem);
35949 this.columnWidth = this.containerWidth;
35950 if (firstItem && firstItem.attr('originalwidth') ) {
35951 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35953 // columnWidth fall back to item of first element
35954 Roo.log("set column width?");
35955 this.initialColumnWidth = this.columnWidth ;
35957 // if first elem has no width, default to size of container
35962 if (this.initialColumnWidth) {
35963 this.columnWidth = this.initialColumnWidth;
35968 // column width is fixed at the top - however if container width get's smaller we should
35971 // this bit calcs how man columns..
35973 var columnWidth = this.columnWidth += this.gutter;
35975 // calculate columns
35976 var containerWidth = this.containerWidth + this.gutter;
35978 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35979 // fix rounding errors, typically with gutters
35980 var excess = columnWidth - containerWidth % columnWidth;
35983 // if overshoot is less than a pixel, round up, otherwise floor it
35984 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35985 cols = Math[ mathMethod ]( cols );
35986 this.cols = Math.max( cols, 1 );
35987 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35989 // padding positioning..
35990 var totalColWidth = this.cols * this.columnWidth;
35991 var padavail = this.containerWidth - totalColWidth;
35992 // so for 2 columns - we need 3 'pads'
35994 var padNeeded = (1+this.cols) * this.padWidth;
35996 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35998 this.columnWidth += padExtra
35999 //this.padWidth = Math.floor(padavail / ( this.cols));
36001 // adjust colum width so that padding is fixed??
36003 // we have 3 columns ... total = width * 3
36004 // we have X left over... that should be used by
36006 //if (this.expandC) {
36014 getContainerWidth : function()
36016 /* // container is parent if fit width
36017 var container = this.isFitWidth ? this.element.parentNode : this.element;
36018 // check that this.size and size are there
36019 // IE8 triggers resize on body size change, so they might not be
36021 var size = getSize( container ); //FIXME
36022 this.containerWidth = size && size.innerWidth; //FIXME
36025 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36029 _getItemLayoutPosition : function( item ) // what is item?
36031 // we resize the item to our columnWidth..
36033 item.setWidth(this.columnWidth);
36034 item.autoBoxAdjust = false;
36036 var sz = item.getSize();
36038 // how many columns does this brick span
36039 var remainder = this.containerWidth % this.columnWidth;
36041 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36042 // round if off by 1 pixel, otherwise use ceil
36043 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36044 colSpan = Math.min( colSpan, this.cols );
36046 // normally this should be '1' as we dont' currently allow multi width columns..
36048 var colGroup = this._getColGroup( colSpan );
36049 // get the minimum Y value from the columns
36050 var minimumY = Math.min.apply( Math, colGroup );
36051 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36053 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36055 // position the brick
36057 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36058 y: this.currentSize.y + minimumY + this.padHeight
36062 // apply setHeight to necessary columns
36063 var setHeight = minimumY + sz.height + this.padHeight;
36064 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36066 var setSpan = this.cols + 1 - colGroup.length;
36067 for ( var i = 0; i < setSpan; i++ ) {
36068 this.colYs[ shortColIndex + i ] = setHeight ;
36075 * @param {Number} colSpan - number of columns the element spans
36076 * @returns {Array} colGroup
36078 _getColGroup : function( colSpan )
36080 if ( colSpan < 2 ) {
36081 // if brick spans only one column, use all the column Ys
36086 // how many different places could this brick fit horizontally
36087 var groupCount = this.cols + 1 - colSpan;
36088 // for each group potential horizontal position
36089 for ( var i = 0; i < groupCount; i++ ) {
36090 // make an array of colY values for that one group
36091 var groupColYs = this.colYs.slice( i, i + colSpan );
36092 // and get the max value of the array
36093 colGroup[i] = Math.max.apply( Math, groupColYs );
36098 _manageStamp : function( stamp )
36100 var stampSize = stamp.getSize();
36101 var offset = stamp.getBox();
36102 // get the columns that this stamp affects
36103 var firstX = this.isOriginLeft ? offset.x : offset.right;
36104 var lastX = firstX + stampSize.width;
36105 var firstCol = Math.floor( firstX / this.columnWidth );
36106 firstCol = Math.max( 0, firstCol );
36108 var lastCol = Math.floor( lastX / this.columnWidth );
36109 // lastCol should not go over if multiple of columnWidth #425
36110 lastCol -= lastX % this.columnWidth ? 0 : 1;
36111 lastCol = Math.min( this.cols - 1, lastCol );
36113 // set colYs to bottom of the stamp
36114 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36117 for ( var i = firstCol; i <= lastCol; i++ ) {
36118 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36123 _getContainerSize : function()
36125 this.maxY = Math.max.apply( Math, this.colYs );
36130 if ( this.isFitWidth ) {
36131 size.width = this._getContainerFitWidth();
36137 _getContainerFitWidth : function()
36139 var unusedCols = 0;
36140 // count unused columns
36143 if ( this.colYs[i] !== 0 ) {
36148 // fit container to columns that have been used
36149 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36152 needsResizeLayout : function()
36154 var previousWidth = this.containerWidth;
36155 this.getContainerWidth();
36156 return previousWidth !== this.containerWidth;
36171 * @class Roo.bootstrap.MasonryBrick
36172 * @extends Roo.bootstrap.Component
36173 * Bootstrap MasonryBrick class
36176 * Create a new MasonryBrick
36177 * @param {Object} config The config object
36180 Roo.bootstrap.MasonryBrick = function(config){
36182 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36184 Roo.bootstrap.MasonryBrick.register(this);
36190 * When a MasonryBrick is clcik
36191 * @param {Roo.bootstrap.MasonryBrick} this
36192 * @param {Roo.EventObject} e
36198 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36201 * @cfg {String} title
36205 * @cfg {String} html
36209 * @cfg {String} bgimage
36213 * @cfg {String} videourl
36217 * @cfg {String} cls
36221 * @cfg {String} href
36225 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36230 * @cfg {String} placetitle (center|bottom)
36235 * @cfg {Boolean} isFitContainer defalut true
36237 isFitContainer : true,
36240 * @cfg {Boolean} preventDefault defalut false
36242 preventDefault : false,
36245 * @cfg {Boolean} inverse defalut false
36247 maskInverse : false,
36249 getAutoCreate : function()
36251 if(!this.isFitContainer){
36252 return this.getSplitAutoCreate();
36255 var cls = 'masonry-brick masonry-brick-full';
36257 if(this.href.length){
36258 cls += ' masonry-brick-link';
36261 if(this.bgimage.length){
36262 cls += ' masonry-brick-image';
36265 if(this.maskInverse){
36266 cls += ' mask-inverse';
36269 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36270 cls += ' enable-mask';
36274 cls += ' masonry-' + this.size + '-brick';
36277 if(this.placetitle.length){
36279 switch (this.placetitle) {
36281 cls += ' masonry-center-title';
36284 cls += ' masonry-bottom-title';
36291 if(!this.html.length && !this.bgimage.length){
36292 cls += ' masonry-center-title';
36295 if(!this.html.length && this.bgimage.length){
36296 cls += ' masonry-bottom-title';
36301 cls += ' ' + this.cls;
36305 tag: (this.href.length) ? 'a' : 'div',
36310 cls: 'masonry-brick-mask'
36314 cls: 'masonry-brick-paragraph',
36320 if(this.href.length){
36321 cfg.href = this.href;
36324 var cn = cfg.cn[1].cn;
36326 if(this.title.length){
36329 cls: 'masonry-brick-title',
36334 if(this.html.length){
36337 cls: 'masonry-brick-text',
36342 if (!this.title.length && !this.html.length) {
36343 cfg.cn[1].cls += ' hide';
36346 if(this.bgimage.length){
36349 cls: 'masonry-brick-image-view',
36354 if(this.videourl.length){
36355 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36356 // youtube support only?
36359 cls: 'masonry-brick-image-view',
36362 allowfullscreen : true
36370 getSplitAutoCreate : function()
36372 var cls = 'masonry-brick masonry-brick-split';
36374 if(this.href.length){
36375 cls += ' masonry-brick-link';
36378 if(this.bgimage.length){
36379 cls += ' masonry-brick-image';
36383 cls += ' masonry-' + this.size + '-brick';
36386 switch (this.placetitle) {
36388 cls += ' masonry-center-title';
36391 cls += ' masonry-bottom-title';
36394 if(!this.bgimage.length){
36395 cls += ' masonry-center-title';
36398 if(this.bgimage.length){
36399 cls += ' masonry-bottom-title';
36405 cls += ' ' + this.cls;
36409 tag: (this.href.length) ? 'a' : 'div',
36414 cls: 'masonry-brick-split-head',
36418 cls: 'masonry-brick-paragraph',
36425 cls: 'masonry-brick-split-body',
36431 if(this.href.length){
36432 cfg.href = this.href;
36435 if(this.title.length){
36436 cfg.cn[0].cn[0].cn.push({
36438 cls: 'masonry-brick-title',
36443 if(this.html.length){
36444 cfg.cn[1].cn.push({
36446 cls: 'masonry-brick-text',
36451 if(this.bgimage.length){
36452 cfg.cn[0].cn.push({
36454 cls: 'masonry-brick-image-view',
36459 if(this.videourl.length){
36460 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36461 // youtube support only?
36462 cfg.cn[0].cn.cn.push({
36464 cls: 'masonry-brick-image-view',
36467 allowfullscreen : true
36474 initEvents: function()
36476 switch (this.size) {
36509 this.el.on('touchstart', this.onTouchStart, this);
36510 this.el.on('touchmove', this.onTouchMove, this);
36511 this.el.on('touchend', this.onTouchEnd, this);
36512 this.el.on('contextmenu', this.onContextMenu, this);
36514 this.el.on('mouseenter' ,this.enter, this);
36515 this.el.on('mouseleave', this.leave, this);
36516 this.el.on('click', this.onClick, this);
36519 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36520 this.parent().bricks.push(this);
36525 onClick: function(e, el)
36527 var time = this.endTimer - this.startTimer;
36528 // Roo.log(e.preventDefault());
36531 e.preventDefault();
36536 if(!this.preventDefault){
36540 e.preventDefault();
36542 if (this.activeClass != '') {
36543 this.selectBrick();
36546 this.fireEvent('click', this, e);
36549 enter: function(e, el)
36551 e.preventDefault();
36553 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36557 if(this.bgimage.length && this.html.length){
36558 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36562 leave: function(e, el)
36564 e.preventDefault();
36566 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36570 if(this.bgimage.length && this.html.length){
36571 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36575 onTouchStart: function(e, el)
36577 // e.preventDefault();
36579 this.touchmoved = false;
36581 if(!this.isFitContainer){
36585 if(!this.bgimage.length || !this.html.length){
36589 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36591 this.timer = new Date().getTime();
36595 onTouchMove: function(e, el)
36597 this.touchmoved = true;
36600 onContextMenu : function(e,el)
36602 e.preventDefault();
36603 e.stopPropagation();
36607 onTouchEnd: function(e, el)
36609 // e.preventDefault();
36611 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36618 if(!this.bgimage.length || !this.html.length){
36620 if(this.href.length){
36621 window.location.href = this.href;
36627 if(!this.isFitContainer){
36631 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36633 window.location.href = this.href;
36636 //selection on single brick only
36637 selectBrick : function() {
36639 if (!this.parentId) {
36643 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36644 var index = m.selectedBrick.indexOf(this.id);
36647 m.selectedBrick.splice(index,1);
36648 this.el.removeClass(this.activeClass);
36652 for(var i = 0; i < m.selectedBrick.length; i++) {
36653 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36654 b.el.removeClass(b.activeClass);
36657 m.selectedBrick = [];
36659 m.selectedBrick.push(this.id);
36660 this.el.addClass(this.activeClass);
36664 isSelected : function(){
36665 return this.el.hasClass(this.activeClass);
36670 Roo.apply(Roo.bootstrap.MasonryBrick, {
36673 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36675 * register a Masonry Brick
36676 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36679 register : function(brick)
36681 //this.groups[brick.id] = brick;
36682 this.groups.add(brick.id, brick);
36685 * fetch a masonry brick based on the masonry brick ID
36686 * @param {string} the masonry brick to add
36687 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36690 get: function(brick_id)
36692 // if (typeof(this.groups[brick_id]) == 'undefined') {
36695 // return this.groups[brick_id] ;
36697 if(this.groups.key(brick_id)) {
36698 return this.groups.key(brick_id);
36716 * @class Roo.bootstrap.Brick
36717 * @extends Roo.bootstrap.Component
36718 * Bootstrap Brick class
36721 * Create a new Brick
36722 * @param {Object} config The config object
36725 Roo.bootstrap.Brick = function(config){
36726 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36732 * When a Brick is click
36733 * @param {Roo.bootstrap.Brick} this
36734 * @param {Roo.EventObject} e
36740 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36743 * @cfg {String} title
36747 * @cfg {String} html
36751 * @cfg {String} bgimage
36755 * @cfg {String} cls
36759 * @cfg {String} href
36763 * @cfg {String} video
36767 * @cfg {Boolean} square
36771 getAutoCreate : function()
36773 var cls = 'roo-brick';
36775 if(this.href.length){
36776 cls += ' roo-brick-link';
36779 if(this.bgimage.length){
36780 cls += ' roo-brick-image';
36783 if(!this.html.length && !this.bgimage.length){
36784 cls += ' roo-brick-center-title';
36787 if(!this.html.length && this.bgimage.length){
36788 cls += ' roo-brick-bottom-title';
36792 cls += ' ' + this.cls;
36796 tag: (this.href.length) ? 'a' : 'div',
36801 cls: 'roo-brick-paragraph',
36807 if(this.href.length){
36808 cfg.href = this.href;
36811 var cn = cfg.cn[0].cn;
36813 if(this.title.length){
36816 cls: 'roo-brick-title',
36821 if(this.html.length){
36824 cls: 'roo-brick-text',
36831 if(this.bgimage.length){
36834 cls: 'roo-brick-image-view',
36842 initEvents: function()
36844 if(this.title.length || this.html.length){
36845 this.el.on('mouseenter' ,this.enter, this);
36846 this.el.on('mouseleave', this.leave, this);
36849 Roo.EventManager.onWindowResize(this.resize, this);
36851 if(this.bgimage.length){
36852 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36853 this.imageEl.on('load', this.onImageLoad, this);
36860 onImageLoad : function()
36865 resize : function()
36867 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36869 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36871 if(this.bgimage.length){
36872 var image = this.el.select('.roo-brick-image-view', true).first();
36874 image.setWidth(paragraph.getWidth());
36877 image.setHeight(paragraph.getWidth());
36880 this.el.setHeight(image.getHeight());
36881 paragraph.setHeight(image.getHeight());
36887 enter: function(e, el)
36889 e.preventDefault();
36891 if(this.bgimage.length){
36892 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36893 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36897 leave: function(e, el)
36899 e.preventDefault();
36901 if(this.bgimage.length){
36902 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36903 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36918 * @class Roo.bootstrap.NumberField
36919 * @extends Roo.bootstrap.Input
36920 * Bootstrap NumberField class
36926 * Create a new NumberField
36927 * @param {Object} config The config object
36930 Roo.bootstrap.NumberField = function(config){
36931 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36934 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36937 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36939 allowDecimals : true,
36941 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36943 decimalSeparator : ".",
36945 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36947 decimalPrecision : 2,
36949 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36951 allowNegative : true,
36954 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36958 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36960 minValue : Number.NEGATIVE_INFINITY,
36962 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36964 maxValue : Number.MAX_VALUE,
36966 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36968 minText : "The minimum value for this field is {0}",
36970 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36972 maxText : "The maximum value for this field is {0}",
36974 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36975 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36977 nanText : "{0} is not a valid number",
36979 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36981 thousandsDelimiter : false,
36983 * @cfg {String} valueAlign alignment of value
36985 valueAlign : "left",
36987 getAutoCreate : function()
36989 var hiddenInput = {
36993 cls: 'hidden-number-input'
36997 hiddenInput.name = this.name;
37002 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37004 this.name = hiddenInput.name;
37006 if(cfg.cn.length > 0) {
37007 cfg.cn.push(hiddenInput);
37014 initEvents : function()
37016 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37018 var allowed = "0123456789";
37020 if(this.allowDecimals){
37021 allowed += this.decimalSeparator;
37024 if(this.allowNegative){
37028 if(this.thousandsDelimiter) {
37032 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37034 var keyPress = function(e){
37036 var k = e.getKey();
37038 var c = e.getCharCode();
37041 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37042 allowed.indexOf(String.fromCharCode(c)) === -1
37048 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37052 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37057 this.el.on("keypress", keyPress, this);
37060 validateValue : function(value)
37063 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37067 var num = this.parseValue(value);
37070 this.markInvalid(String.format(this.nanText, value));
37074 if(num < this.minValue){
37075 this.markInvalid(String.format(this.minText, this.minValue));
37079 if(num > this.maxValue){
37080 this.markInvalid(String.format(this.maxText, this.maxValue));
37087 getValue : function()
37089 var v = this.hiddenEl().getValue();
37091 return this.fixPrecision(this.parseValue(v));
37094 parseValue : function(value)
37096 if(this.thousandsDelimiter) {
37098 r = new RegExp(",", "g");
37099 value = value.replace(r, "");
37102 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37103 return isNaN(value) ? '' : value;
37106 fixPrecision : function(value)
37108 if(this.thousandsDelimiter) {
37110 r = new RegExp(",", "g");
37111 value = value.replace(r, "");
37114 var nan = isNaN(value);
37116 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37117 return nan ? '' : value;
37119 return parseFloat(value).toFixed(this.decimalPrecision);
37122 setValue : function(v)
37124 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37130 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37132 this.inputEl().dom.value = (v == '') ? '' :
37133 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37135 if(!this.allowZero && v === '0') {
37136 this.hiddenEl().dom.value = '';
37137 this.inputEl().dom.value = '';
37144 decimalPrecisionFcn : function(v)
37146 return Math.floor(v);
37149 beforeBlur : function()
37151 var v = this.parseValue(this.getRawValue());
37153 if(v || v === 0 || v === ''){
37158 hiddenEl : function()
37160 return this.el.select('input.hidden-number-input',true).first();
37172 * @class Roo.bootstrap.DocumentSlider
37173 * @extends Roo.bootstrap.Component
37174 * Bootstrap DocumentSlider class
37177 * Create a new DocumentViewer
37178 * @param {Object} config The config object
37181 Roo.bootstrap.DocumentSlider = function(config){
37182 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37189 * Fire after initEvent
37190 * @param {Roo.bootstrap.DocumentSlider} this
37195 * Fire after update
37196 * @param {Roo.bootstrap.DocumentSlider} this
37202 * @param {Roo.bootstrap.DocumentSlider} this
37208 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37214 getAutoCreate : function()
37218 cls : 'roo-document-slider',
37222 cls : 'roo-document-slider-header',
37226 cls : 'roo-document-slider-header-title'
37232 cls : 'roo-document-slider-body',
37236 cls : 'roo-document-slider-prev',
37240 cls : 'fa fa-chevron-left'
37246 cls : 'roo-document-slider-thumb',
37250 cls : 'roo-document-slider-image'
37256 cls : 'roo-document-slider-next',
37260 cls : 'fa fa-chevron-right'
37272 initEvents : function()
37274 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37275 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37277 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37278 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37280 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37281 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37283 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37284 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37286 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37287 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37289 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37290 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37292 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37293 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37295 this.thumbEl.on('click', this.onClick, this);
37297 this.prevIndicator.on('click', this.prev, this);
37299 this.nextIndicator.on('click', this.next, this);
37303 initial : function()
37305 if(this.files.length){
37306 this.indicator = 1;
37310 this.fireEvent('initial', this);
37313 update : function()
37315 this.imageEl.attr('src', this.files[this.indicator - 1]);
37317 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37319 this.prevIndicator.show();
37321 if(this.indicator == 1){
37322 this.prevIndicator.hide();
37325 this.nextIndicator.show();
37327 if(this.indicator == this.files.length){
37328 this.nextIndicator.hide();
37331 this.thumbEl.scrollTo('top');
37333 this.fireEvent('update', this);
37336 onClick : function(e)
37338 e.preventDefault();
37340 this.fireEvent('click', this);
37345 e.preventDefault();
37347 this.indicator = Math.max(1, this.indicator - 1);
37354 e.preventDefault();
37356 this.indicator = Math.min(this.files.length, this.indicator + 1);
37370 * @class Roo.bootstrap.RadioSet
37371 * @extends Roo.bootstrap.Input
37372 * Bootstrap RadioSet class
37373 * @cfg {String} indicatorpos (left|right) default left
37374 * @cfg {Boolean} inline (true|false) inline the element (default true)
37375 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37377 * Create a new RadioSet
37378 * @param {Object} config The config object
37381 Roo.bootstrap.RadioSet = function(config){
37383 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37387 Roo.bootstrap.RadioSet.register(this);
37392 * Fires when the element is checked or unchecked.
37393 * @param {Roo.bootstrap.RadioSet} this This radio
37394 * @param {Roo.bootstrap.Radio} item The checked item
37399 * Fires when the element is click.
37400 * @param {Roo.bootstrap.RadioSet} this This radio set
37401 * @param {Roo.bootstrap.Radio} item The checked item
37402 * @param {Roo.EventObject} e The event object
37409 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37417 indicatorpos : 'left',
37419 getAutoCreate : function()
37423 cls : 'roo-radio-set-label',
37427 html : this.fieldLabel
37431 if (Roo.bootstrap.version == 3) {
37434 if(this.indicatorpos == 'left'){
37437 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37438 tooltip : 'This field is required'
37443 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37444 tooltip : 'This field is required'
37450 cls : 'roo-radio-set-items'
37453 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37455 if (align === 'left' && this.fieldLabel.length) {
37458 cls : "roo-radio-set-right",
37464 if(this.labelWidth > 12){
37465 label.style = "width: " + this.labelWidth + 'px';
37468 if(this.labelWidth < 13 && this.labelmd == 0){
37469 this.labelmd = this.labelWidth;
37472 if(this.labellg > 0){
37473 label.cls += ' col-lg-' + this.labellg;
37474 items.cls += ' col-lg-' + (12 - this.labellg);
37477 if(this.labelmd > 0){
37478 label.cls += ' col-md-' + this.labelmd;
37479 items.cls += ' col-md-' + (12 - this.labelmd);
37482 if(this.labelsm > 0){
37483 label.cls += ' col-sm-' + this.labelsm;
37484 items.cls += ' col-sm-' + (12 - this.labelsm);
37487 if(this.labelxs > 0){
37488 label.cls += ' col-xs-' + this.labelxs;
37489 items.cls += ' col-xs-' + (12 - this.labelxs);
37495 cls : 'roo-radio-set',
37499 cls : 'roo-radio-set-input',
37502 value : this.value ? this.value : ''
37509 if(this.weight.length){
37510 cfg.cls += ' roo-radio-' + this.weight;
37514 cfg.cls += ' roo-radio-set-inline';
37518 ['xs','sm','md','lg'].map(function(size){
37519 if (settings[size]) {
37520 cfg.cls += ' col-' + size + '-' + settings[size];
37528 initEvents : function()
37530 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37531 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37533 if(!this.fieldLabel.length){
37534 this.labelEl.hide();
37537 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37538 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37540 this.indicator = this.indicatorEl();
37542 if(this.indicator){
37543 this.indicator.addClass('invisible');
37546 this.originalValue = this.getValue();
37550 inputEl: function ()
37552 return this.el.select('.roo-radio-set-input', true).first();
37555 getChildContainer : function()
37557 return this.itemsEl;
37560 register : function(item)
37562 this.radioes.push(item);
37566 validate : function()
37568 if(this.getVisibilityEl().hasClass('hidden')){
37574 Roo.each(this.radioes, function(i){
37583 if(this.allowBlank) {
37587 if(this.disabled || valid){
37592 this.markInvalid();
37597 markValid : function()
37599 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37600 this.indicatorEl().removeClass('visible');
37601 this.indicatorEl().addClass('invisible');
37605 if (Roo.bootstrap.version == 3) {
37606 this.el.removeClass([this.invalidClass, this.validClass]);
37607 this.el.addClass(this.validClass);
37609 this.el.removeClass(['is-invalid','is-valid']);
37610 this.el.addClass(['is-valid']);
37612 this.fireEvent('valid', this);
37615 markInvalid : function(msg)
37617 if(this.allowBlank || this.disabled){
37621 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37622 this.indicatorEl().removeClass('invisible');
37623 this.indicatorEl().addClass('visible');
37625 if (Roo.bootstrap.version == 3) {
37626 this.el.removeClass([this.invalidClass, this.validClass]);
37627 this.el.addClass(this.invalidClass);
37629 this.el.removeClass(['is-invalid','is-valid']);
37630 this.el.addClass(['is-invalid']);
37633 this.fireEvent('invalid', this, msg);
37637 setValue : function(v, suppressEvent)
37639 if(this.value === v){
37646 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37649 Roo.each(this.radioes, function(i){
37651 i.el.removeClass('checked');
37654 Roo.each(this.radioes, function(i){
37656 if(i.value === v || i.value.toString() === v.toString()){
37658 i.el.addClass('checked');
37660 if(suppressEvent !== true){
37661 this.fireEvent('check', this, i);
37672 clearInvalid : function(){
37674 if(!this.el || this.preventMark){
37678 this.el.removeClass([this.invalidClass]);
37680 this.fireEvent('valid', this);
37685 Roo.apply(Roo.bootstrap.RadioSet, {
37689 register : function(set)
37691 this.groups[set.name] = set;
37694 get: function(name)
37696 if (typeof(this.groups[name]) == 'undefined') {
37700 return this.groups[name] ;
37706 * Ext JS Library 1.1.1
37707 * Copyright(c) 2006-2007, Ext JS, LLC.
37709 * Originally Released Under LGPL - original licence link has changed is not relivant.
37712 * <script type="text/javascript">
37717 * @class Roo.bootstrap.SplitBar
37718 * @extends Roo.util.Observable
37719 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37723 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37724 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37725 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37726 split.minSize = 100;
37727 split.maxSize = 600;
37728 split.animate = true;
37729 split.on('moved', splitterMoved);
37732 * Create a new SplitBar
37733 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37734 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37735 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37736 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37737 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37738 position of the SplitBar).
37740 Roo.bootstrap.SplitBar = function(cfg){
37745 // dragElement : elm
37746 // resizingElement: el,
37748 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37749 // placement : Roo.bootstrap.SplitBar.LEFT ,
37750 // existingProxy ???
37753 this.el = Roo.get(cfg.dragElement, true);
37754 this.el.dom.unselectable = "on";
37756 this.resizingEl = Roo.get(cfg.resizingElement, true);
37760 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37761 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37764 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37767 * The minimum size of the resizing element. (Defaults to 0)
37773 * The maximum size of the resizing element. (Defaults to 2000)
37776 this.maxSize = 2000;
37779 * Whether to animate the transition to the new size
37782 this.animate = false;
37785 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37788 this.useShim = false;
37793 if(!cfg.existingProxy){
37795 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37797 this.proxy = Roo.get(cfg.existingProxy).dom;
37800 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37803 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37806 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37809 this.dragSpecs = {};
37812 * @private The adapter to use to positon and resize elements
37814 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37815 this.adapter.init(this);
37817 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37819 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37820 this.el.addClass("roo-splitbar-h");
37823 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37824 this.el.addClass("roo-splitbar-v");
37830 * Fires when the splitter is moved (alias for {@link #event-moved})
37831 * @param {Roo.bootstrap.SplitBar} this
37832 * @param {Number} newSize the new width or height
37837 * Fires when the splitter is moved
37838 * @param {Roo.bootstrap.SplitBar} this
37839 * @param {Number} newSize the new width or height
37843 * @event beforeresize
37844 * Fires before the splitter is dragged
37845 * @param {Roo.bootstrap.SplitBar} this
37847 "beforeresize" : true,
37849 "beforeapply" : true
37852 Roo.util.Observable.call(this);
37855 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37856 onStartProxyDrag : function(x, y){
37857 this.fireEvent("beforeresize", this);
37859 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37861 o.enableDisplayMode("block");
37862 // all splitbars share the same overlay
37863 Roo.bootstrap.SplitBar.prototype.overlay = o;
37865 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37866 this.overlay.show();
37867 Roo.get(this.proxy).setDisplayed("block");
37868 var size = this.adapter.getElementSize(this);
37869 this.activeMinSize = this.getMinimumSize();;
37870 this.activeMaxSize = this.getMaximumSize();;
37871 var c1 = size - this.activeMinSize;
37872 var c2 = Math.max(this.activeMaxSize - size, 0);
37873 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37874 this.dd.resetConstraints();
37875 this.dd.setXConstraint(
37876 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37877 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37879 this.dd.setYConstraint(0, 0);
37881 this.dd.resetConstraints();
37882 this.dd.setXConstraint(0, 0);
37883 this.dd.setYConstraint(
37884 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37885 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37888 this.dragSpecs.startSize = size;
37889 this.dragSpecs.startPoint = [x, y];
37890 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37894 * @private Called after the drag operation by the DDProxy
37896 onEndProxyDrag : function(e){
37897 Roo.get(this.proxy).setDisplayed(false);
37898 var endPoint = Roo.lib.Event.getXY(e);
37900 this.overlay.hide();
37903 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37904 newSize = this.dragSpecs.startSize +
37905 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37906 endPoint[0] - this.dragSpecs.startPoint[0] :
37907 this.dragSpecs.startPoint[0] - endPoint[0]
37910 newSize = this.dragSpecs.startSize +
37911 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37912 endPoint[1] - this.dragSpecs.startPoint[1] :
37913 this.dragSpecs.startPoint[1] - endPoint[1]
37916 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37917 if(newSize != this.dragSpecs.startSize){
37918 if(this.fireEvent('beforeapply', this, newSize) !== false){
37919 this.adapter.setElementSize(this, newSize);
37920 this.fireEvent("moved", this, newSize);
37921 this.fireEvent("resize", this, newSize);
37927 * Get the adapter this SplitBar uses
37928 * @return The adapter object
37930 getAdapter : function(){
37931 return this.adapter;
37935 * Set the adapter this SplitBar uses
37936 * @param {Object} adapter A SplitBar adapter object
37938 setAdapter : function(adapter){
37939 this.adapter = adapter;
37940 this.adapter.init(this);
37944 * Gets the minimum size for the resizing element
37945 * @return {Number} The minimum size
37947 getMinimumSize : function(){
37948 return this.minSize;
37952 * Sets the minimum size for the resizing element
37953 * @param {Number} minSize The minimum size
37955 setMinimumSize : function(minSize){
37956 this.minSize = minSize;
37960 * Gets the maximum size for the resizing element
37961 * @return {Number} The maximum size
37963 getMaximumSize : function(){
37964 return this.maxSize;
37968 * Sets the maximum size for the resizing element
37969 * @param {Number} maxSize The maximum size
37971 setMaximumSize : function(maxSize){
37972 this.maxSize = maxSize;
37976 * Sets the initialize size for the resizing element
37977 * @param {Number} size The initial size
37979 setCurrentSize : function(size){
37980 var oldAnimate = this.animate;
37981 this.animate = false;
37982 this.adapter.setElementSize(this, size);
37983 this.animate = oldAnimate;
37987 * Destroy this splitbar.
37988 * @param {Boolean} removeEl True to remove the element
37990 destroy : function(removeEl){
37992 this.shim.remove();
37995 this.proxy.parentNode.removeChild(this.proxy);
38003 * @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.
38005 Roo.bootstrap.SplitBar.createProxy = function(dir){
38006 var proxy = new Roo.Element(document.createElement("div"));
38007 proxy.unselectable();
38008 var cls = 'roo-splitbar-proxy';
38009 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38010 document.body.appendChild(proxy.dom);
38015 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38016 * Default Adapter. It assumes the splitter and resizing element are not positioned
38017 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38019 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38022 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38023 // do nothing for now
38024 init : function(s){
38028 * Called before drag operations to get the current size of the resizing element.
38029 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38031 getElementSize : function(s){
38032 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38033 return s.resizingEl.getWidth();
38035 return s.resizingEl.getHeight();
38040 * Called after drag operations to set the size of the resizing element.
38041 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38042 * @param {Number} newSize The new size to set
38043 * @param {Function} onComplete A function to be invoked when resizing is complete
38045 setElementSize : function(s, newSize, onComplete){
38046 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38048 s.resizingEl.setWidth(newSize);
38050 onComplete(s, newSize);
38053 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38058 s.resizingEl.setHeight(newSize);
38060 onComplete(s, newSize);
38063 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38070 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38071 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38072 * Adapter that moves the splitter element to align with the resized sizing element.
38073 * Used with an absolute positioned SplitBar.
38074 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38075 * document.body, make sure you assign an id to the body element.
38077 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38078 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38079 this.container = Roo.get(container);
38082 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38083 init : function(s){
38084 this.basic.init(s);
38087 getElementSize : function(s){
38088 return this.basic.getElementSize(s);
38091 setElementSize : function(s, newSize, onComplete){
38092 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38095 moveSplitter : function(s){
38096 var yes = Roo.bootstrap.SplitBar;
38097 switch(s.placement){
38099 s.el.setX(s.resizingEl.getRight());
38102 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38105 s.el.setY(s.resizingEl.getBottom());
38108 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38115 * Orientation constant - Create a vertical SplitBar
38119 Roo.bootstrap.SplitBar.VERTICAL = 1;
38122 * Orientation constant - Create a horizontal SplitBar
38126 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38129 * Placement constant - The resizing element is to the left of the splitter element
38133 Roo.bootstrap.SplitBar.LEFT = 1;
38136 * Placement constant - The resizing element is to the right of the splitter element
38140 Roo.bootstrap.SplitBar.RIGHT = 2;
38143 * Placement constant - The resizing element is positioned above the splitter element
38147 Roo.bootstrap.SplitBar.TOP = 3;
38150 * Placement constant - The resizing element is positioned under splitter element
38154 Roo.bootstrap.SplitBar.BOTTOM = 4;
38155 Roo.namespace("Roo.bootstrap.layout");/*
38157 * Ext JS Library 1.1.1
38158 * Copyright(c) 2006-2007, Ext JS, LLC.
38160 * Originally Released Under LGPL - original licence link has changed is not relivant.
38163 * <script type="text/javascript">
38167 * @class Roo.bootstrap.layout.Manager
38168 * @extends Roo.bootstrap.Component
38169 * Base class for layout managers.
38171 Roo.bootstrap.layout.Manager = function(config)
38173 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38179 /** false to disable window resize monitoring @type Boolean */
38180 this.monitorWindowResize = true;
38185 * Fires when a layout is performed.
38186 * @param {Roo.LayoutManager} this
38190 * @event regionresized
38191 * Fires when the user resizes a region.
38192 * @param {Roo.LayoutRegion} region The resized region
38193 * @param {Number} newSize The new size (width for east/west, height for north/south)
38195 "regionresized" : true,
38197 * @event regioncollapsed
38198 * Fires when a region is collapsed.
38199 * @param {Roo.LayoutRegion} region The collapsed region
38201 "regioncollapsed" : true,
38203 * @event regionexpanded
38204 * Fires when a region is expanded.
38205 * @param {Roo.LayoutRegion} region The expanded region
38207 "regionexpanded" : true
38209 this.updating = false;
38212 this.el = Roo.get(config.el);
38218 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38223 monitorWindowResize : true,
38229 onRender : function(ct, position)
38232 this.el = Roo.get(ct);
38235 //this.fireEvent('render',this);
38239 initEvents: function()
38243 // ie scrollbar fix
38244 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38245 document.body.scroll = "no";
38246 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38247 this.el.position('relative');
38249 this.id = this.el.id;
38250 this.el.addClass("roo-layout-container");
38251 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38252 if(this.el.dom != document.body ) {
38253 this.el.on('resize', this.layout,this);
38254 this.el.on('show', this.layout,this);
38260 * Returns true if this layout is currently being updated
38261 * @return {Boolean}
38263 isUpdating : function(){
38264 return this.updating;
38268 * Suspend the LayoutManager from doing auto-layouts while
38269 * making multiple add or remove calls
38271 beginUpdate : function(){
38272 this.updating = true;
38276 * Restore auto-layouts and optionally disable the manager from performing a layout
38277 * @param {Boolean} noLayout true to disable a layout update
38279 endUpdate : function(noLayout){
38280 this.updating = false;
38286 layout: function(){
38290 onRegionResized : function(region, newSize){
38291 this.fireEvent("regionresized", region, newSize);
38295 onRegionCollapsed : function(region){
38296 this.fireEvent("regioncollapsed", region);
38299 onRegionExpanded : function(region){
38300 this.fireEvent("regionexpanded", region);
38304 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38305 * performs box-model adjustments.
38306 * @return {Object} The size as an object {width: (the width), height: (the height)}
38308 getViewSize : function()
38311 if(this.el.dom != document.body){
38312 size = this.el.getSize();
38314 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38316 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38317 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38322 * Returns the Element this layout is bound to.
38323 * @return {Roo.Element}
38325 getEl : function(){
38330 * Returns the specified region.
38331 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38332 * @return {Roo.LayoutRegion}
38334 getRegion : function(target){
38335 return this.regions[target.toLowerCase()];
38338 onWindowResize : function(){
38339 if(this.monitorWindowResize){
38346 * Ext JS Library 1.1.1
38347 * Copyright(c) 2006-2007, Ext JS, LLC.
38349 * Originally Released Under LGPL - original licence link has changed is not relivant.
38352 * <script type="text/javascript">
38355 * @class Roo.bootstrap.layout.Border
38356 * @extends Roo.bootstrap.layout.Manager
38357 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38358 * please see: examples/bootstrap/nested.html<br><br>
38360 <b>The container the layout is rendered into can be either the body element or any other element.
38361 If it is not the body element, the container needs to either be an absolute positioned element,
38362 or you will need to add "position:relative" to the css of the container. You will also need to specify
38363 the container size if it is not the body element.</b>
38366 * Create a new Border
38367 * @param {Object} config Configuration options
38369 Roo.bootstrap.layout.Border = function(config){
38370 config = config || {};
38371 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38375 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38376 if(config[region]){
38377 config[region].region = region;
38378 this.addRegion(config[region]);
38384 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38386 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38388 parent : false, // this might point to a 'nest' or a ???
38391 * Creates and adds a new region if it doesn't already exist.
38392 * @param {String} target The target region key (north, south, east, west or center).
38393 * @param {Object} config The regions config object
38394 * @return {BorderLayoutRegion} The new region
38396 addRegion : function(config)
38398 if(!this.regions[config.region]){
38399 var r = this.factory(config);
38400 this.bindRegion(r);
38402 return this.regions[config.region];
38406 bindRegion : function(r){
38407 this.regions[r.config.region] = r;
38409 r.on("visibilitychange", this.layout, this);
38410 r.on("paneladded", this.layout, this);
38411 r.on("panelremoved", this.layout, this);
38412 r.on("invalidated", this.layout, this);
38413 r.on("resized", this.onRegionResized, this);
38414 r.on("collapsed", this.onRegionCollapsed, this);
38415 r.on("expanded", this.onRegionExpanded, this);
38419 * Performs a layout update.
38421 layout : function()
38423 if(this.updating) {
38427 // render all the rebions if they have not been done alreayd?
38428 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38429 if(this.regions[region] && !this.regions[region].bodyEl){
38430 this.regions[region].onRender(this.el)
38434 var size = this.getViewSize();
38435 var w = size.width;
38436 var h = size.height;
38441 //var x = 0, y = 0;
38443 var rs = this.regions;
38444 var north = rs["north"];
38445 var south = rs["south"];
38446 var west = rs["west"];
38447 var east = rs["east"];
38448 var center = rs["center"];
38449 //if(this.hideOnLayout){ // not supported anymore
38450 //c.el.setStyle("display", "none");
38452 if(north && north.isVisible()){
38453 var b = north.getBox();
38454 var m = north.getMargins();
38455 b.width = w - (m.left+m.right);
38458 centerY = b.height + b.y + m.bottom;
38459 centerH -= centerY;
38460 north.updateBox(this.safeBox(b));
38462 if(south && south.isVisible()){
38463 var b = south.getBox();
38464 var m = south.getMargins();
38465 b.width = w - (m.left+m.right);
38467 var totalHeight = (b.height + m.top + m.bottom);
38468 b.y = h - totalHeight + m.top;
38469 centerH -= totalHeight;
38470 south.updateBox(this.safeBox(b));
38472 if(west && west.isVisible()){
38473 var b = west.getBox();
38474 var m = west.getMargins();
38475 b.height = centerH - (m.top+m.bottom);
38477 b.y = centerY + m.top;
38478 var totalWidth = (b.width + m.left + m.right);
38479 centerX += totalWidth;
38480 centerW -= totalWidth;
38481 west.updateBox(this.safeBox(b));
38483 if(east && east.isVisible()){
38484 var b = east.getBox();
38485 var m = east.getMargins();
38486 b.height = centerH - (m.top+m.bottom);
38487 var totalWidth = (b.width + m.left + m.right);
38488 b.x = w - totalWidth + m.left;
38489 b.y = centerY + m.top;
38490 centerW -= totalWidth;
38491 east.updateBox(this.safeBox(b));
38494 var m = center.getMargins();
38496 x: centerX + m.left,
38497 y: centerY + m.top,
38498 width: centerW - (m.left+m.right),
38499 height: centerH - (m.top+m.bottom)
38501 //if(this.hideOnLayout){
38502 //center.el.setStyle("display", "block");
38504 center.updateBox(this.safeBox(centerBox));
38507 this.fireEvent("layout", this);
38511 safeBox : function(box){
38512 box.width = Math.max(0, box.width);
38513 box.height = Math.max(0, box.height);
38518 * Adds a ContentPanel (or subclass) to this layout.
38519 * @param {String} target The target region key (north, south, east, west or center).
38520 * @param {Roo.ContentPanel} panel The panel to add
38521 * @return {Roo.ContentPanel} The added panel
38523 add : function(target, panel){
38525 target = target.toLowerCase();
38526 return this.regions[target].add(panel);
38530 * Remove a ContentPanel (or subclass) to this layout.
38531 * @param {String} target The target region key (north, south, east, west or center).
38532 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38533 * @return {Roo.ContentPanel} The removed panel
38535 remove : function(target, panel){
38536 target = target.toLowerCase();
38537 return this.regions[target].remove(panel);
38541 * Searches all regions for a panel with the specified id
38542 * @param {String} panelId
38543 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38545 findPanel : function(panelId){
38546 var rs = this.regions;
38547 for(var target in rs){
38548 if(typeof rs[target] != "function"){
38549 var p = rs[target].getPanel(panelId);
38559 * Searches all regions for a panel with the specified id and activates (shows) it.
38560 * @param {String/ContentPanel} panelId The panels id or the panel itself
38561 * @return {Roo.ContentPanel} The shown panel or null
38563 showPanel : function(panelId) {
38564 var rs = this.regions;
38565 for(var target in rs){
38566 var r = rs[target];
38567 if(typeof r != "function"){
38568 if(r.hasPanel(panelId)){
38569 return r.showPanel(panelId);
38577 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38578 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38581 restoreState : function(provider){
38583 provider = Roo.state.Manager;
38585 var sm = new Roo.LayoutStateManager();
38586 sm.init(this, provider);
38592 * Adds a xtype elements to the layout.
38596 xtype : 'ContentPanel',
38603 xtype : 'NestedLayoutPanel',
38609 items : [ ... list of content panels or nested layout panels.. ]
38613 * @param {Object} cfg Xtype definition of item to add.
38615 addxtype : function(cfg)
38617 // basically accepts a pannel...
38618 // can accept a layout region..!?!?
38619 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38622 // theory? children can only be panels??
38624 //if (!cfg.xtype.match(/Panel$/)) {
38629 if (typeof(cfg.region) == 'undefined') {
38630 Roo.log("Failed to add Panel, region was not set");
38634 var region = cfg.region;
38640 xitems = cfg.items;
38645 if ( region == 'center') {
38646 Roo.log("Center: " + cfg.title);
38652 case 'Content': // ContentPanel (el, cfg)
38653 case 'Scroll': // ContentPanel (el, cfg)
38655 cfg.autoCreate = cfg.autoCreate || true;
38656 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38658 // var el = this.el.createChild();
38659 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38662 this.add(region, ret);
38666 case 'TreePanel': // our new panel!
38667 cfg.el = this.el.createChild();
38668 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38669 this.add(region, ret);
38674 // create a new Layout (which is a Border Layout...
38676 var clayout = cfg.layout;
38677 clayout.el = this.el.createChild();
38678 clayout.items = clayout.items || [];
38682 // replace this exitems with the clayout ones..
38683 xitems = clayout.items;
38685 // force background off if it's in center...
38686 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38687 cfg.background = false;
38689 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38692 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38693 //console.log('adding nested layout panel ' + cfg.toSource());
38694 this.add(region, ret);
38695 nb = {}; /// find first...
38700 // needs grid and region
38702 //var el = this.getRegion(region).el.createChild();
38704 *var el = this.el.createChild();
38705 // create the grid first...
38706 cfg.grid.container = el;
38707 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38710 if (region == 'center' && this.active ) {
38711 cfg.background = false;
38714 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38716 this.add(region, ret);
38718 if (cfg.background) {
38719 // render grid on panel activation (if panel background)
38720 ret.on('activate', function(gp) {
38721 if (!gp.grid.rendered) {
38722 // gp.grid.render(el);
38726 // cfg.grid.render(el);
38732 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38733 // it was the old xcomponent building that caused this before.
38734 // espeically if border is the top element in the tree.
38744 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38746 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38747 this.add(region, ret);
38751 throw "Can not add '" + cfg.xtype + "' to Border";
38757 this.beginUpdate();
38761 Roo.each(xitems, function(i) {
38762 region = nb && i.region ? i.region : false;
38764 var add = ret.addxtype(i);
38767 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38768 if (!i.background) {
38769 abn[region] = nb[region] ;
38776 // make the last non-background panel active..
38777 //if (nb) { Roo.log(abn); }
38780 for(var r in abn) {
38781 region = this.getRegion(r);
38783 // tried using nb[r], but it does not work..
38785 region.showPanel(abn[r]);
38796 factory : function(cfg)
38799 var validRegions = Roo.bootstrap.layout.Border.regions;
38801 var target = cfg.region;
38804 var r = Roo.bootstrap.layout;
38808 return new r.North(cfg);
38810 return new r.South(cfg);
38812 return new r.East(cfg);
38814 return new r.West(cfg);
38816 return new r.Center(cfg);
38818 throw 'Layout region "'+target+'" not supported.';
38825 * Ext JS Library 1.1.1
38826 * Copyright(c) 2006-2007, Ext JS, LLC.
38828 * Originally Released Under LGPL - original licence link has changed is not relivant.
38831 * <script type="text/javascript">
38835 * @class Roo.bootstrap.layout.Basic
38836 * @extends Roo.util.Observable
38837 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38838 * and does not have a titlebar, tabs or any other features. All it does is size and position
38839 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38840 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38841 * @cfg {string} region the region that it inhabits..
38842 * @cfg {bool} skipConfig skip config?
38846 Roo.bootstrap.layout.Basic = function(config){
38848 this.mgr = config.mgr;
38850 this.position = config.region;
38852 var skipConfig = config.skipConfig;
38856 * @scope Roo.BasicLayoutRegion
38860 * @event beforeremove
38861 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38862 * @param {Roo.LayoutRegion} this
38863 * @param {Roo.ContentPanel} panel The panel
38864 * @param {Object} e The cancel event object
38866 "beforeremove" : true,
38868 * @event invalidated
38869 * Fires when the layout for this region is changed.
38870 * @param {Roo.LayoutRegion} this
38872 "invalidated" : true,
38874 * @event visibilitychange
38875 * Fires when this region is shown or hidden
38876 * @param {Roo.LayoutRegion} this
38877 * @param {Boolean} visibility true or false
38879 "visibilitychange" : true,
38881 * @event paneladded
38882 * Fires when a panel is added.
38883 * @param {Roo.LayoutRegion} this
38884 * @param {Roo.ContentPanel} panel The panel
38886 "paneladded" : true,
38888 * @event panelremoved
38889 * Fires when a panel is removed.
38890 * @param {Roo.LayoutRegion} this
38891 * @param {Roo.ContentPanel} panel The panel
38893 "panelremoved" : true,
38895 * @event beforecollapse
38896 * Fires when this region before collapse.
38897 * @param {Roo.LayoutRegion} this
38899 "beforecollapse" : true,
38902 * Fires when this region is collapsed.
38903 * @param {Roo.LayoutRegion} this
38905 "collapsed" : true,
38908 * Fires when this region is expanded.
38909 * @param {Roo.LayoutRegion} this
38914 * Fires when this region is slid into view.
38915 * @param {Roo.LayoutRegion} this
38917 "slideshow" : true,
38920 * Fires when this region slides out of view.
38921 * @param {Roo.LayoutRegion} this
38923 "slidehide" : true,
38925 * @event panelactivated
38926 * Fires when a panel is activated.
38927 * @param {Roo.LayoutRegion} this
38928 * @param {Roo.ContentPanel} panel The activated panel
38930 "panelactivated" : true,
38933 * Fires when the user resizes this region.
38934 * @param {Roo.LayoutRegion} this
38935 * @param {Number} newSize The new size (width for east/west, height for north/south)
38939 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38940 this.panels = new Roo.util.MixedCollection();
38941 this.panels.getKey = this.getPanelId.createDelegate(this);
38943 this.activePanel = null;
38944 // ensure listeners are added...
38946 if (config.listeners || config.events) {
38947 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38948 listeners : config.listeners || {},
38949 events : config.events || {}
38953 if(skipConfig !== true){
38954 this.applyConfig(config);
38958 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38960 getPanelId : function(p){
38964 applyConfig : function(config){
38965 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38966 this.config = config;
38971 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38972 * the width, for horizontal (north, south) the height.
38973 * @param {Number} newSize The new width or height
38975 resizeTo : function(newSize){
38976 var el = this.el ? this.el :
38977 (this.activePanel ? this.activePanel.getEl() : null);
38979 switch(this.position){
38982 el.setWidth(newSize);
38983 this.fireEvent("resized", this, newSize);
38987 el.setHeight(newSize);
38988 this.fireEvent("resized", this, newSize);
38994 getBox : function(){
38995 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38998 getMargins : function(){
38999 return this.margins;
39002 updateBox : function(box){
39004 var el = this.activePanel.getEl();
39005 el.dom.style.left = box.x + "px";
39006 el.dom.style.top = box.y + "px";
39007 this.activePanel.setSize(box.width, box.height);
39011 * Returns the container element for this region.
39012 * @return {Roo.Element}
39014 getEl : function(){
39015 return this.activePanel;
39019 * Returns true if this region is currently visible.
39020 * @return {Boolean}
39022 isVisible : function(){
39023 return this.activePanel ? true : false;
39026 setActivePanel : function(panel){
39027 panel = this.getPanel(panel);
39028 if(this.activePanel && this.activePanel != panel){
39029 this.activePanel.setActiveState(false);
39030 this.activePanel.getEl().setLeftTop(-10000,-10000);
39032 this.activePanel = panel;
39033 panel.setActiveState(true);
39035 panel.setSize(this.box.width, this.box.height);
39037 this.fireEvent("panelactivated", this, panel);
39038 this.fireEvent("invalidated");
39042 * Show the specified panel.
39043 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39044 * @return {Roo.ContentPanel} The shown panel or null
39046 showPanel : function(panel){
39047 panel = this.getPanel(panel);
39049 this.setActivePanel(panel);
39055 * Get the active panel for this region.
39056 * @return {Roo.ContentPanel} The active panel or null
39058 getActivePanel : function(){
39059 return this.activePanel;
39063 * Add the passed ContentPanel(s)
39064 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39065 * @return {Roo.ContentPanel} The panel added (if only one was added)
39067 add : function(panel){
39068 if(arguments.length > 1){
39069 for(var i = 0, len = arguments.length; i < len; i++) {
39070 this.add(arguments[i]);
39074 if(this.hasPanel(panel)){
39075 this.showPanel(panel);
39078 var el = panel.getEl();
39079 if(el.dom.parentNode != this.mgr.el.dom){
39080 this.mgr.el.dom.appendChild(el.dom);
39082 if(panel.setRegion){
39083 panel.setRegion(this);
39085 this.panels.add(panel);
39086 el.setStyle("position", "absolute");
39087 if(!panel.background){
39088 this.setActivePanel(panel);
39089 if(this.config.initialSize && this.panels.getCount()==1){
39090 this.resizeTo(this.config.initialSize);
39093 this.fireEvent("paneladded", this, panel);
39098 * Returns true if the panel is in this region.
39099 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39100 * @return {Boolean}
39102 hasPanel : function(panel){
39103 if(typeof panel == "object"){ // must be panel obj
39104 panel = panel.getId();
39106 return this.getPanel(panel) ? true : false;
39110 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39111 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39112 * @param {Boolean} preservePanel Overrides the config preservePanel option
39113 * @return {Roo.ContentPanel} The panel that was removed
39115 remove : function(panel, preservePanel){
39116 panel = this.getPanel(panel);
39121 this.fireEvent("beforeremove", this, panel, e);
39122 if(e.cancel === true){
39125 var panelId = panel.getId();
39126 this.panels.removeKey(panelId);
39131 * Returns the panel specified or null if it's not in this region.
39132 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39133 * @return {Roo.ContentPanel}
39135 getPanel : function(id){
39136 if(typeof id == "object"){ // must be panel obj
39139 return this.panels.get(id);
39143 * Returns this regions position (north/south/east/west/center).
39146 getPosition: function(){
39147 return this.position;
39151 * Ext JS Library 1.1.1
39152 * Copyright(c) 2006-2007, Ext JS, LLC.
39154 * Originally Released Under LGPL - original licence link has changed is not relivant.
39157 * <script type="text/javascript">
39161 * @class Roo.bootstrap.layout.Region
39162 * @extends Roo.bootstrap.layout.Basic
39163 * This class represents a region in a layout manager.
39165 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39166 * @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})
39167 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39168 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39169 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39170 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39171 * @cfg {String} title The title for the region (overrides panel titles)
39172 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39173 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39174 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39175 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39176 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39177 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39178 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39179 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39180 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39181 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39183 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39184 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39185 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39186 * @cfg {Number} width For East/West panels
39187 * @cfg {Number} height For North/South panels
39188 * @cfg {Boolean} split To show the splitter
39189 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39191 * @cfg {string} cls Extra CSS classes to add to region
39193 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39194 * @cfg {string} region the region that it inhabits..
39197 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39198 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39200 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39201 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39202 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39204 Roo.bootstrap.layout.Region = function(config)
39206 this.applyConfig(config);
39208 var mgr = config.mgr;
39209 var pos = config.region;
39210 config.skipConfig = true;
39211 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39214 this.onRender(mgr.el);
39217 this.visible = true;
39218 this.collapsed = false;
39219 this.unrendered_panels = [];
39222 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39224 position: '', // set by wrapper (eg. north/south etc..)
39225 unrendered_panels : null, // unrendered panels.
39227 tabPosition : false,
39229 mgr: false, // points to 'Border'
39232 createBody : function(){
39233 /** This region's body element
39234 * @type Roo.Element */
39235 this.bodyEl = this.el.createChild({
39237 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39241 onRender: function(ctr, pos)
39243 var dh = Roo.DomHelper;
39244 /** This region's container element
39245 * @type Roo.Element */
39246 this.el = dh.append(ctr.dom, {
39248 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39250 /** This region's title element
39251 * @type Roo.Element */
39253 this.titleEl = dh.append(this.el.dom, {
39255 unselectable: "on",
39256 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39258 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39259 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39263 this.titleEl.enableDisplayMode();
39264 /** This region's title text element
39265 * @type HTMLElement */
39266 this.titleTextEl = this.titleEl.dom.firstChild;
39267 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39269 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39270 this.closeBtn.enableDisplayMode();
39271 this.closeBtn.on("click", this.closeClicked, this);
39272 this.closeBtn.hide();
39274 this.createBody(this.config);
39275 if(this.config.hideWhenEmpty){
39277 this.on("paneladded", this.validateVisibility, this);
39278 this.on("panelremoved", this.validateVisibility, this);
39280 if(this.autoScroll){
39281 this.bodyEl.setStyle("overflow", "auto");
39283 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39285 //if(c.titlebar !== false){
39286 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39287 this.titleEl.hide();
39289 this.titleEl.show();
39290 if(this.config.title){
39291 this.titleTextEl.innerHTML = this.config.title;
39295 if(this.config.collapsed){
39296 this.collapse(true);
39298 if(this.config.hidden){
39302 if (this.unrendered_panels && this.unrendered_panels.length) {
39303 for (var i =0;i< this.unrendered_panels.length; i++) {
39304 this.add(this.unrendered_panels[i]);
39306 this.unrendered_panels = null;
39312 applyConfig : function(c)
39315 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39316 var dh = Roo.DomHelper;
39317 if(c.titlebar !== false){
39318 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39319 this.collapseBtn.on("click", this.collapse, this);
39320 this.collapseBtn.enableDisplayMode();
39322 if(c.showPin === true || this.showPin){
39323 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39324 this.stickBtn.enableDisplayMode();
39325 this.stickBtn.on("click", this.expand, this);
39326 this.stickBtn.hide();
39331 /** This region's collapsed element
39332 * @type Roo.Element */
39335 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39336 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39339 if(c.floatable !== false){
39340 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39341 this.collapsedEl.on("click", this.collapseClick, this);
39344 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39345 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39346 id: "message", unselectable: "on", style:{"float":"left"}});
39347 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39349 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39350 this.expandBtn.on("click", this.expand, this);
39354 if(this.collapseBtn){
39355 this.collapseBtn.setVisible(c.collapsible == true);
39358 this.cmargins = c.cmargins || this.cmargins ||
39359 (this.position == "west" || this.position == "east" ?
39360 {top: 0, left: 2, right:2, bottom: 0} :
39361 {top: 2, left: 0, right:0, bottom: 2});
39363 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39366 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39368 this.autoScroll = c.autoScroll || false;
39373 this.duration = c.duration || .30;
39374 this.slideDuration = c.slideDuration || .45;
39379 * Returns true if this region is currently visible.
39380 * @return {Boolean}
39382 isVisible : function(){
39383 return this.visible;
39387 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39388 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39390 //setCollapsedTitle : function(title){
39391 // title = title || " ";
39392 // if(this.collapsedTitleTextEl){
39393 // this.collapsedTitleTextEl.innerHTML = title;
39397 getBox : function(){
39399 // if(!this.collapsed){
39400 b = this.el.getBox(false, true);
39402 // b = this.collapsedEl.getBox(false, true);
39407 getMargins : function(){
39408 return this.margins;
39409 //return this.collapsed ? this.cmargins : this.margins;
39412 highlight : function(){
39413 this.el.addClass("x-layout-panel-dragover");
39416 unhighlight : function(){
39417 this.el.removeClass("x-layout-panel-dragover");
39420 updateBox : function(box)
39422 if (!this.bodyEl) {
39423 return; // not rendered yet..
39427 if(!this.collapsed){
39428 this.el.dom.style.left = box.x + "px";
39429 this.el.dom.style.top = box.y + "px";
39430 this.updateBody(box.width, box.height);
39432 this.collapsedEl.dom.style.left = box.x + "px";
39433 this.collapsedEl.dom.style.top = box.y + "px";
39434 this.collapsedEl.setSize(box.width, box.height);
39437 this.tabs.autoSizeTabs();
39441 updateBody : function(w, h)
39444 this.el.setWidth(w);
39445 w -= this.el.getBorderWidth("rl");
39446 if(this.config.adjustments){
39447 w += this.config.adjustments[0];
39450 if(h !== null && h > 0){
39451 this.el.setHeight(h);
39452 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39453 h -= this.el.getBorderWidth("tb");
39454 if(this.config.adjustments){
39455 h += this.config.adjustments[1];
39457 this.bodyEl.setHeight(h);
39459 h = this.tabs.syncHeight(h);
39462 if(this.panelSize){
39463 w = w !== null ? w : this.panelSize.width;
39464 h = h !== null ? h : this.panelSize.height;
39466 if(this.activePanel){
39467 var el = this.activePanel.getEl();
39468 w = w !== null ? w : el.getWidth();
39469 h = h !== null ? h : el.getHeight();
39470 this.panelSize = {width: w, height: h};
39471 this.activePanel.setSize(w, h);
39473 if(Roo.isIE && this.tabs){
39474 this.tabs.el.repaint();
39479 * Returns the container element for this region.
39480 * @return {Roo.Element}
39482 getEl : function(){
39487 * Hides this region.
39490 //if(!this.collapsed){
39491 this.el.dom.style.left = "-2000px";
39494 // this.collapsedEl.dom.style.left = "-2000px";
39495 // this.collapsedEl.hide();
39497 this.visible = false;
39498 this.fireEvent("visibilitychange", this, false);
39502 * Shows this region if it was previously hidden.
39505 //if(!this.collapsed){
39508 // this.collapsedEl.show();
39510 this.visible = true;
39511 this.fireEvent("visibilitychange", this, true);
39514 closeClicked : function(){
39515 if(this.activePanel){
39516 this.remove(this.activePanel);
39520 collapseClick : function(e){
39522 e.stopPropagation();
39525 e.stopPropagation();
39531 * Collapses this region.
39532 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39535 collapse : function(skipAnim, skipCheck = false){
39536 if(this.collapsed) {
39540 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39542 this.collapsed = true;
39544 this.split.el.hide();
39546 if(this.config.animate && skipAnim !== true){
39547 this.fireEvent("invalidated", this);
39548 this.animateCollapse();
39550 this.el.setLocation(-20000,-20000);
39552 this.collapsedEl.show();
39553 this.fireEvent("collapsed", this);
39554 this.fireEvent("invalidated", this);
39560 animateCollapse : function(){
39565 * Expands this region if it was previously collapsed.
39566 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39567 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39570 expand : function(e, skipAnim){
39572 e.stopPropagation();
39574 if(!this.collapsed || this.el.hasActiveFx()) {
39578 this.afterSlideIn();
39581 this.collapsed = false;
39582 if(this.config.animate && skipAnim !== true){
39583 this.animateExpand();
39587 this.split.el.show();
39589 this.collapsedEl.setLocation(-2000,-2000);
39590 this.collapsedEl.hide();
39591 this.fireEvent("invalidated", this);
39592 this.fireEvent("expanded", this);
39596 animateExpand : function(){
39600 initTabs : function()
39602 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39604 var ts = new Roo.bootstrap.panel.Tabs({
39605 el: this.bodyEl.dom,
39607 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39608 disableTooltips: this.config.disableTabTips,
39609 toolbar : this.config.toolbar
39612 if(this.config.hideTabs){
39613 ts.stripWrap.setDisplayed(false);
39616 ts.resizeTabs = this.config.resizeTabs === true;
39617 ts.minTabWidth = this.config.minTabWidth || 40;
39618 ts.maxTabWidth = this.config.maxTabWidth || 250;
39619 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39620 ts.monitorResize = false;
39621 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39622 ts.bodyEl.addClass('roo-layout-tabs-body');
39623 this.panels.each(this.initPanelAsTab, this);
39626 initPanelAsTab : function(panel){
39627 var ti = this.tabs.addTab(
39631 this.config.closeOnTab && panel.isClosable(),
39634 if(panel.tabTip !== undefined){
39635 ti.setTooltip(panel.tabTip);
39637 ti.on("activate", function(){
39638 this.setActivePanel(panel);
39641 if(this.config.closeOnTab){
39642 ti.on("beforeclose", function(t, e){
39644 this.remove(panel);
39648 panel.tabItem = ti;
39653 updatePanelTitle : function(panel, title)
39655 if(this.activePanel == panel){
39656 this.updateTitle(title);
39659 var ti = this.tabs.getTab(panel.getEl().id);
39661 if(panel.tabTip !== undefined){
39662 ti.setTooltip(panel.tabTip);
39667 updateTitle : function(title){
39668 if(this.titleTextEl && !this.config.title){
39669 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39673 setActivePanel : function(panel)
39675 panel = this.getPanel(panel);
39676 if(this.activePanel && this.activePanel != panel){
39677 if(this.activePanel.setActiveState(false) === false){
39681 this.activePanel = panel;
39682 panel.setActiveState(true);
39683 if(this.panelSize){
39684 panel.setSize(this.panelSize.width, this.panelSize.height);
39687 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39689 this.updateTitle(panel.getTitle());
39691 this.fireEvent("invalidated", this);
39693 this.fireEvent("panelactivated", this, panel);
39697 * Shows the specified panel.
39698 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39699 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39701 showPanel : function(panel)
39703 panel = this.getPanel(panel);
39706 var tab = this.tabs.getTab(panel.getEl().id);
39707 if(tab.isHidden()){
39708 this.tabs.unhideTab(tab.id);
39712 this.setActivePanel(panel);
39719 * Get the active panel for this region.
39720 * @return {Roo.ContentPanel} The active panel or null
39722 getActivePanel : function(){
39723 return this.activePanel;
39726 validateVisibility : function(){
39727 if(this.panels.getCount() < 1){
39728 this.updateTitle(" ");
39729 this.closeBtn.hide();
39732 if(!this.isVisible()){
39739 * Adds the passed ContentPanel(s) to this region.
39740 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39741 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39743 add : function(panel)
39745 if(arguments.length > 1){
39746 for(var i = 0, len = arguments.length; i < len; i++) {
39747 this.add(arguments[i]);
39752 // if we have not been rendered yet, then we can not really do much of this..
39753 if (!this.bodyEl) {
39754 this.unrendered_panels.push(panel);
39761 if(this.hasPanel(panel)){
39762 this.showPanel(panel);
39765 panel.setRegion(this);
39766 this.panels.add(panel);
39767 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39768 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39769 // and hide them... ???
39770 this.bodyEl.dom.appendChild(panel.getEl().dom);
39771 if(panel.background !== true){
39772 this.setActivePanel(panel);
39774 this.fireEvent("paneladded", this, panel);
39781 this.initPanelAsTab(panel);
39785 if(panel.background !== true){
39786 this.tabs.activate(panel.getEl().id);
39788 this.fireEvent("paneladded", this, panel);
39793 * Hides the tab for the specified panel.
39794 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39796 hidePanel : function(panel){
39797 if(this.tabs && (panel = this.getPanel(panel))){
39798 this.tabs.hideTab(panel.getEl().id);
39803 * Unhides the tab for a previously hidden panel.
39804 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39806 unhidePanel : function(panel){
39807 if(this.tabs && (panel = this.getPanel(panel))){
39808 this.tabs.unhideTab(panel.getEl().id);
39812 clearPanels : function(){
39813 while(this.panels.getCount() > 0){
39814 this.remove(this.panels.first());
39819 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39820 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39821 * @param {Boolean} preservePanel Overrides the config preservePanel option
39822 * @return {Roo.ContentPanel} The panel that was removed
39824 remove : function(panel, preservePanel)
39826 panel = this.getPanel(panel);
39831 this.fireEvent("beforeremove", this, panel, e);
39832 if(e.cancel === true){
39835 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39836 var panelId = panel.getId();
39837 this.panels.removeKey(panelId);
39839 document.body.appendChild(panel.getEl().dom);
39842 this.tabs.removeTab(panel.getEl().id);
39843 }else if (!preservePanel){
39844 this.bodyEl.dom.removeChild(panel.getEl().dom);
39846 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39847 var p = this.panels.first();
39848 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39849 tempEl.appendChild(p.getEl().dom);
39850 this.bodyEl.update("");
39851 this.bodyEl.dom.appendChild(p.getEl().dom);
39853 this.updateTitle(p.getTitle());
39855 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39856 this.setActivePanel(p);
39858 panel.setRegion(null);
39859 if(this.activePanel == panel){
39860 this.activePanel = null;
39862 if(this.config.autoDestroy !== false && preservePanel !== true){
39863 try{panel.destroy();}catch(e){}
39865 this.fireEvent("panelremoved", this, panel);
39870 * Returns the TabPanel component used by this region
39871 * @return {Roo.TabPanel}
39873 getTabs : function(){
39877 createTool : function(parentEl, className){
39878 var btn = Roo.DomHelper.append(parentEl, {
39880 cls: "x-layout-tools-button",
39883 cls: "roo-layout-tools-button-inner " + className,
39887 btn.addClassOnOver("roo-layout-tools-button-over");
39892 * Ext JS Library 1.1.1
39893 * Copyright(c) 2006-2007, Ext JS, LLC.
39895 * Originally Released Under LGPL - original licence link has changed is not relivant.
39898 * <script type="text/javascript">
39904 * @class Roo.SplitLayoutRegion
39905 * @extends Roo.LayoutRegion
39906 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39908 Roo.bootstrap.layout.Split = function(config){
39909 this.cursor = config.cursor;
39910 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39913 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39915 splitTip : "Drag to resize.",
39916 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39917 useSplitTips : false,
39919 applyConfig : function(config){
39920 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39923 onRender : function(ctr,pos) {
39925 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39926 if(!this.config.split){
39931 var splitEl = Roo.DomHelper.append(ctr.dom, {
39933 id: this.el.id + "-split",
39934 cls: "roo-layout-split roo-layout-split-"+this.position,
39937 /** The SplitBar for this region
39938 * @type Roo.SplitBar */
39939 // does not exist yet...
39940 Roo.log([this.position, this.orientation]);
39942 this.split = new Roo.bootstrap.SplitBar({
39943 dragElement : splitEl,
39944 resizingElement: this.el,
39945 orientation : this.orientation
39948 this.split.on("moved", this.onSplitMove, this);
39949 this.split.useShim = this.config.useShim === true;
39950 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39951 if(this.useSplitTips){
39952 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39954 //if(config.collapsible){
39955 // this.split.el.on("dblclick", this.collapse, this);
39958 if(typeof this.config.minSize != "undefined"){
39959 this.split.minSize = this.config.minSize;
39961 if(typeof this.config.maxSize != "undefined"){
39962 this.split.maxSize = this.config.maxSize;
39964 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39965 this.hideSplitter();
39970 getHMaxSize : function(){
39971 var cmax = this.config.maxSize || 10000;
39972 var center = this.mgr.getRegion("center");
39973 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39976 getVMaxSize : function(){
39977 var cmax = this.config.maxSize || 10000;
39978 var center = this.mgr.getRegion("center");
39979 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39982 onSplitMove : function(split, newSize){
39983 this.fireEvent("resized", this, newSize);
39987 * Returns the {@link Roo.SplitBar} for this region.
39988 * @return {Roo.SplitBar}
39990 getSplitBar : function(){
39995 this.hideSplitter();
39996 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39999 hideSplitter : function(){
40001 this.split.el.setLocation(-2000,-2000);
40002 this.split.el.hide();
40008 this.split.el.show();
40010 Roo.bootstrap.layout.Split.superclass.show.call(this);
40013 beforeSlide: function(){
40014 if(Roo.isGecko){// firefox overflow auto bug workaround
40015 this.bodyEl.clip();
40017 this.tabs.bodyEl.clip();
40019 if(this.activePanel){
40020 this.activePanel.getEl().clip();
40022 if(this.activePanel.beforeSlide){
40023 this.activePanel.beforeSlide();
40029 afterSlide : function(){
40030 if(Roo.isGecko){// firefox overflow auto bug workaround
40031 this.bodyEl.unclip();
40033 this.tabs.bodyEl.unclip();
40035 if(this.activePanel){
40036 this.activePanel.getEl().unclip();
40037 if(this.activePanel.afterSlide){
40038 this.activePanel.afterSlide();
40044 initAutoHide : function(){
40045 if(this.autoHide !== false){
40046 if(!this.autoHideHd){
40047 var st = new Roo.util.DelayedTask(this.slideIn, this);
40048 this.autoHideHd = {
40049 "mouseout": function(e){
40050 if(!e.within(this.el, true)){
40054 "mouseover" : function(e){
40060 this.el.on(this.autoHideHd);
40064 clearAutoHide : function(){
40065 if(this.autoHide !== false){
40066 this.el.un("mouseout", this.autoHideHd.mouseout);
40067 this.el.un("mouseover", this.autoHideHd.mouseover);
40071 clearMonitor : function(){
40072 Roo.get(document).un("click", this.slideInIf, this);
40075 // these names are backwards but not changed for compat
40076 slideOut : function(){
40077 if(this.isSlid || this.el.hasActiveFx()){
40080 this.isSlid = true;
40081 if(this.collapseBtn){
40082 this.collapseBtn.hide();
40084 this.closeBtnState = this.closeBtn.getStyle('display');
40085 this.closeBtn.hide();
40087 this.stickBtn.show();
40090 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40091 this.beforeSlide();
40092 this.el.setStyle("z-index", 10001);
40093 this.el.slideIn(this.getSlideAnchor(), {
40094 callback: function(){
40096 this.initAutoHide();
40097 Roo.get(document).on("click", this.slideInIf, this);
40098 this.fireEvent("slideshow", this);
40105 afterSlideIn : function(){
40106 this.clearAutoHide();
40107 this.isSlid = false;
40108 this.clearMonitor();
40109 this.el.setStyle("z-index", "");
40110 if(this.collapseBtn){
40111 this.collapseBtn.show();
40113 this.closeBtn.setStyle('display', this.closeBtnState);
40115 this.stickBtn.hide();
40117 this.fireEvent("slidehide", this);
40120 slideIn : function(cb){
40121 if(!this.isSlid || this.el.hasActiveFx()){
40125 this.isSlid = false;
40126 this.beforeSlide();
40127 this.el.slideOut(this.getSlideAnchor(), {
40128 callback: function(){
40129 this.el.setLeftTop(-10000, -10000);
40131 this.afterSlideIn();
40139 slideInIf : function(e){
40140 if(!e.within(this.el)){
40145 animateCollapse : function(){
40146 this.beforeSlide();
40147 this.el.setStyle("z-index", 20000);
40148 var anchor = this.getSlideAnchor();
40149 this.el.slideOut(anchor, {
40150 callback : function(){
40151 this.el.setStyle("z-index", "");
40152 this.collapsedEl.slideIn(anchor, {duration:.3});
40154 this.el.setLocation(-10000,-10000);
40156 this.fireEvent("collapsed", this);
40163 animateExpand : function(){
40164 this.beforeSlide();
40165 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40166 this.el.setStyle("z-index", 20000);
40167 this.collapsedEl.hide({
40170 this.el.slideIn(this.getSlideAnchor(), {
40171 callback : function(){
40172 this.el.setStyle("z-index", "");
40175 this.split.el.show();
40177 this.fireEvent("invalidated", this);
40178 this.fireEvent("expanded", this);
40206 getAnchor : function(){
40207 return this.anchors[this.position];
40210 getCollapseAnchor : function(){
40211 return this.canchors[this.position];
40214 getSlideAnchor : function(){
40215 return this.sanchors[this.position];
40218 getAlignAdj : function(){
40219 var cm = this.cmargins;
40220 switch(this.position){
40236 getExpandAdj : function(){
40237 var c = this.collapsedEl, cm = this.cmargins;
40238 switch(this.position){
40240 return [-(cm.right+c.getWidth()+cm.left), 0];
40243 return [cm.right+c.getWidth()+cm.left, 0];
40246 return [0, -(cm.top+cm.bottom+c.getHeight())];
40249 return [0, cm.top+cm.bottom+c.getHeight()];
40255 * Ext JS Library 1.1.1
40256 * Copyright(c) 2006-2007, Ext JS, LLC.
40258 * Originally Released Under LGPL - original licence link has changed is not relivant.
40261 * <script type="text/javascript">
40264 * These classes are private internal classes
40266 Roo.bootstrap.layout.Center = function(config){
40267 config.region = "center";
40268 Roo.bootstrap.layout.Region.call(this, config);
40269 this.visible = true;
40270 this.minWidth = config.minWidth || 20;
40271 this.minHeight = config.minHeight || 20;
40274 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40276 // center panel can't be hidden
40280 // center panel can't be hidden
40283 getMinWidth: function(){
40284 return this.minWidth;
40287 getMinHeight: function(){
40288 return this.minHeight;
40302 Roo.bootstrap.layout.North = function(config)
40304 config.region = 'north';
40305 config.cursor = 'n-resize';
40307 Roo.bootstrap.layout.Split.call(this, config);
40311 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40312 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40313 this.split.el.addClass("roo-layout-split-v");
40315 //var size = config.initialSize || config.height;
40316 //if(this.el && typeof size != "undefined"){
40317 // this.el.setHeight(size);
40320 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40322 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40325 onRender : function(ctr, pos)
40327 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40328 var size = this.config.initialSize || this.config.height;
40329 if(this.el && typeof size != "undefined"){
40330 this.el.setHeight(size);
40335 getBox : function(){
40336 if(this.collapsed){
40337 return this.collapsedEl.getBox();
40339 var box = this.el.getBox();
40341 box.height += this.split.el.getHeight();
40346 updateBox : function(box){
40347 if(this.split && !this.collapsed){
40348 box.height -= this.split.el.getHeight();
40349 this.split.el.setLeft(box.x);
40350 this.split.el.setTop(box.y+box.height);
40351 this.split.el.setWidth(box.width);
40353 if(this.collapsed){
40354 this.updateBody(box.width, null);
40356 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40364 Roo.bootstrap.layout.South = function(config){
40365 config.region = 'south';
40366 config.cursor = 's-resize';
40367 Roo.bootstrap.layout.Split.call(this, config);
40369 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40370 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40371 this.split.el.addClass("roo-layout-split-v");
40376 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40377 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40379 onRender : function(ctr, pos)
40381 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40382 var size = this.config.initialSize || this.config.height;
40383 if(this.el && typeof size != "undefined"){
40384 this.el.setHeight(size);
40389 getBox : function(){
40390 if(this.collapsed){
40391 return this.collapsedEl.getBox();
40393 var box = this.el.getBox();
40395 var sh = this.split.el.getHeight();
40402 updateBox : function(box){
40403 if(this.split && !this.collapsed){
40404 var sh = this.split.el.getHeight();
40407 this.split.el.setLeft(box.x);
40408 this.split.el.setTop(box.y-sh);
40409 this.split.el.setWidth(box.width);
40411 if(this.collapsed){
40412 this.updateBody(box.width, null);
40414 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40418 Roo.bootstrap.layout.East = function(config){
40419 config.region = "east";
40420 config.cursor = "e-resize";
40421 Roo.bootstrap.layout.Split.call(this, config);
40423 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40424 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40425 this.split.el.addClass("roo-layout-split-h");
40429 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40430 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40432 onRender : function(ctr, pos)
40434 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40435 var size = this.config.initialSize || this.config.width;
40436 if(this.el && typeof size != "undefined"){
40437 this.el.setWidth(size);
40442 getBox : function(){
40443 if(this.collapsed){
40444 return this.collapsedEl.getBox();
40446 var box = this.el.getBox();
40448 var sw = this.split.el.getWidth();
40455 updateBox : function(box){
40456 if(this.split && !this.collapsed){
40457 var sw = this.split.el.getWidth();
40459 this.split.el.setLeft(box.x);
40460 this.split.el.setTop(box.y);
40461 this.split.el.setHeight(box.height);
40464 if(this.collapsed){
40465 this.updateBody(null, box.height);
40467 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40471 Roo.bootstrap.layout.West = function(config){
40472 config.region = "west";
40473 config.cursor = "w-resize";
40475 Roo.bootstrap.layout.Split.call(this, config);
40477 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40478 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40479 this.split.el.addClass("roo-layout-split-h");
40483 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40484 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40486 onRender: function(ctr, pos)
40488 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40489 var size = this.config.initialSize || this.config.width;
40490 if(typeof size != "undefined"){
40491 this.el.setWidth(size);
40495 getBox : function(){
40496 if(this.collapsed){
40497 return this.collapsedEl.getBox();
40499 var box = this.el.getBox();
40500 if (box.width == 0) {
40501 box.width = this.config.width; // kludge?
40504 box.width += this.split.el.getWidth();
40509 updateBox : function(box){
40510 if(this.split && !this.collapsed){
40511 var sw = this.split.el.getWidth();
40513 this.split.el.setLeft(box.x+box.width);
40514 this.split.el.setTop(box.y);
40515 this.split.el.setHeight(box.height);
40517 if(this.collapsed){
40518 this.updateBody(null, box.height);
40520 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40522 });Roo.namespace("Roo.bootstrap.panel");/*
40524 * Ext JS Library 1.1.1
40525 * Copyright(c) 2006-2007, Ext JS, LLC.
40527 * Originally Released Under LGPL - original licence link has changed is not relivant.
40530 * <script type="text/javascript">
40533 * @class Roo.ContentPanel
40534 * @extends Roo.util.Observable
40535 * A basic ContentPanel element.
40536 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40537 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40538 * @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
40539 * @cfg {Boolean} closable True if the panel can be closed/removed
40540 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40541 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40542 * @cfg {Toolbar} toolbar A toolbar for this panel
40543 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40544 * @cfg {String} title The title for this panel
40545 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40546 * @cfg {String} url Calls {@link #setUrl} with this value
40547 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40548 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40549 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40550 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40551 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40552 * @cfg {Boolean} badges render the badges
40553 * @cfg {String} cls extra classes to use
40554 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40557 * Create a new ContentPanel.
40558 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40559 * @param {String/Object} config A string to set only the title or a config object
40560 * @param {String} content (optional) Set the HTML content for this panel
40561 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40563 Roo.bootstrap.panel.Content = function( config){
40565 this.tpl = config.tpl || false;
40567 var el = config.el;
40568 var content = config.content;
40570 if(config.autoCreate){ // xtype is available if this is called from factory
40573 this.el = Roo.get(el);
40574 if(!this.el && config && config.autoCreate){
40575 if(typeof config.autoCreate == "object"){
40576 if(!config.autoCreate.id){
40577 config.autoCreate.id = config.id||el;
40579 this.el = Roo.DomHelper.append(document.body,
40580 config.autoCreate, true);
40584 cls: (config.cls || '') +
40585 (config.background ? ' bg-' + config.background : '') +
40586 " roo-layout-inactive-content",
40589 if (config.iframe) {
40593 style : 'border: 0px',
40594 src : 'about:blank'
40600 elcfg.html = config.html;
40604 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40605 if (config.iframe) {
40606 this.iframeEl = this.el.select('iframe',true).first();
40611 this.closable = false;
40612 this.loaded = false;
40613 this.active = false;
40616 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40618 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40620 this.wrapEl = this.el; //this.el.wrap();
40622 if (config.toolbar.items) {
40623 ti = config.toolbar.items ;
40624 delete config.toolbar.items ;
40628 this.toolbar.render(this.wrapEl, 'before');
40629 for(var i =0;i < ti.length;i++) {
40630 // Roo.log(['add child', items[i]]);
40631 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40633 this.toolbar.items = nitems;
40634 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40635 delete config.toolbar;
40639 // xtype created footer. - not sure if will work as we normally have to render first..
40640 if (this.footer && !this.footer.el && this.footer.xtype) {
40641 if (!this.wrapEl) {
40642 this.wrapEl = this.el.wrap();
40645 this.footer.container = this.wrapEl.createChild();
40647 this.footer = Roo.factory(this.footer, Roo);
40652 if(typeof config == "string"){
40653 this.title = config;
40655 Roo.apply(this, config);
40659 this.resizeEl = Roo.get(this.resizeEl, true);
40661 this.resizeEl = this.el;
40663 // handle view.xtype
40671 * Fires when this panel is activated.
40672 * @param {Roo.ContentPanel} this
40676 * @event deactivate
40677 * Fires when this panel is activated.
40678 * @param {Roo.ContentPanel} this
40680 "deactivate" : true,
40684 * Fires when this panel is resized if fitToFrame is true.
40685 * @param {Roo.ContentPanel} this
40686 * @param {Number} width The width after any component adjustments
40687 * @param {Number} height The height after any component adjustments
40693 * Fires when this tab is created
40694 * @param {Roo.ContentPanel} this
40700 * Fires when this content is scrolled
40701 * @param {Roo.ContentPanel} this
40702 * @param {Event} scrollEvent
40713 if(this.autoScroll && !this.iframe){
40714 this.resizeEl.setStyle("overflow", "auto");
40715 this.resizeEl.on('scroll', this.onScroll, this);
40717 // fix randome scrolling
40718 //this.el.on('scroll', function() {
40719 // Roo.log('fix random scolling');
40720 // this.scrollTo('top',0);
40723 content = content || this.content;
40725 this.setContent(content);
40727 if(config && config.url){
40728 this.setUrl(this.url, this.params, this.loadOnce);
40733 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40735 if (this.view && typeof(this.view.xtype) != 'undefined') {
40736 this.view.el = this.el.appendChild(document.createElement("div"));
40737 this.view = Roo.factory(this.view);
40738 this.view.render && this.view.render(false, '');
40742 this.fireEvent('render', this);
40745 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40755 /* Resize Element - use this to work out scroll etc. */
40758 setRegion : function(region){
40759 this.region = region;
40760 this.setActiveClass(region && !this.background);
40764 setActiveClass: function(state)
40767 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40768 this.el.setStyle('position','relative');
40770 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40771 this.el.setStyle('position', 'absolute');
40776 * Returns the toolbar for this Panel if one was configured.
40777 * @return {Roo.Toolbar}
40779 getToolbar : function(){
40780 return this.toolbar;
40783 setActiveState : function(active)
40785 this.active = active;
40786 this.setActiveClass(active);
40788 if(this.fireEvent("deactivate", this) === false){
40793 this.fireEvent("activate", this);
40797 * Updates this panel's element (not for iframe)
40798 * @param {String} content The new content
40799 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40801 setContent : function(content, loadScripts){
40806 this.el.update(content, loadScripts);
40809 ignoreResize : function(w, h){
40810 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40813 this.lastSize = {width: w, height: h};
40818 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40819 * @return {Roo.UpdateManager} The UpdateManager
40821 getUpdateManager : function(){
40825 return this.el.getUpdateManager();
40828 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40829 * Does not work with IFRAME contents
40830 * @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:
40833 url: "your-url.php",
40834 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40835 callback: yourFunction,
40836 scope: yourObject, //(optional scope)
40839 text: "Loading...",
40845 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40846 * 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.
40847 * @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}
40848 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40849 * @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.
40850 * @return {Roo.ContentPanel} this
40858 var um = this.el.getUpdateManager();
40859 um.update.apply(um, arguments);
40865 * 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.
40866 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40867 * @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)
40868 * @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)
40869 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40871 setUrl : function(url, params, loadOnce){
40873 this.iframeEl.dom.src = url;
40877 if(this.refreshDelegate){
40878 this.removeListener("activate", this.refreshDelegate);
40880 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40881 this.on("activate", this.refreshDelegate);
40882 return this.el.getUpdateManager();
40885 _handleRefresh : function(url, params, loadOnce){
40886 if(!loadOnce || !this.loaded){
40887 var updater = this.el.getUpdateManager();
40888 updater.update(url, params, this._setLoaded.createDelegate(this));
40892 _setLoaded : function(){
40893 this.loaded = true;
40897 * Returns this panel's id
40900 getId : function(){
40905 * Returns this panel's element - used by regiosn to add.
40906 * @return {Roo.Element}
40908 getEl : function(){
40909 return this.wrapEl || this.el;
40914 adjustForComponents : function(width, height)
40916 //Roo.log('adjustForComponents ');
40917 if(this.resizeEl != this.el){
40918 width -= this.el.getFrameWidth('lr');
40919 height -= this.el.getFrameWidth('tb');
40922 var te = this.toolbar.getEl();
40923 te.setWidth(width);
40924 height -= te.getHeight();
40927 var te = this.footer.getEl();
40928 te.setWidth(width);
40929 height -= te.getHeight();
40933 if(this.adjustments){
40934 width += this.adjustments[0];
40935 height += this.adjustments[1];
40937 return {"width": width, "height": height};
40940 setSize : function(width, height){
40941 if(this.fitToFrame && !this.ignoreResize(width, height)){
40942 if(this.fitContainer && this.resizeEl != this.el){
40943 this.el.setSize(width, height);
40945 var size = this.adjustForComponents(width, height);
40947 this.iframeEl.setSize(width,height);
40950 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40951 this.fireEvent('resize', this, size.width, size.height);
40958 * Returns this panel's title
40961 getTitle : function(){
40963 if (typeof(this.title) != 'object') {
40968 for (var k in this.title) {
40969 if (!this.title.hasOwnProperty(k)) {
40973 if (k.indexOf('-') >= 0) {
40974 var s = k.split('-');
40975 for (var i = 0; i<s.length; i++) {
40976 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40979 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40986 * Set this panel's title
40987 * @param {String} title
40989 setTitle : function(title){
40990 this.title = title;
40992 this.region.updatePanelTitle(this, title);
40997 * Returns true is this panel was configured to be closable
40998 * @return {Boolean}
41000 isClosable : function(){
41001 return this.closable;
41004 beforeSlide : function(){
41006 this.resizeEl.clip();
41009 afterSlide : function(){
41011 this.resizeEl.unclip();
41015 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41016 * Will fail silently if the {@link #setUrl} method has not been called.
41017 * This does not activate the panel, just updates its content.
41019 refresh : function(){
41020 if(this.refreshDelegate){
41021 this.loaded = false;
41022 this.refreshDelegate();
41027 * Destroys this panel
41029 destroy : function(){
41030 this.el.removeAllListeners();
41031 var tempEl = document.createElement("span");
41032 tempEl.appendChild(this.el.dom);
41033 tempEl.innerHTML = "";
41039 * form - if the content panel contains a form - this is a reference to it.
41040 * @type {Roo.form.Form}
41044 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41045 * This contains a reference to it.
41051 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41061 * @param {Object} cfg Xtype definition of item to add.
41065 getChildContainer: function () {
41066 return this.getEl();
41070 onScroll : function(e)
41072 this.fireEvent('scroll', this, e);
41077 var ret = new Roo.factory(cfg);
41082 if (cfg.xtype.match(/^Form$/)) {
41085 //if (this.footer) {
41086 // el = this.footer.container.insertSibling(false, 'before');
41088 el = this.el.createChild();
41091 this.form = new Roo.form.Form(cfg);
41094 if ( this.form.allItems.length) {
41095 this.form.render(el.dom);
41099 // should only have one of theses..
41100 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41101 // views.. should not be just added - used named prop 'view''
41103 cfg.el = this.el.appendChild(document.createElement("div"));
41106 var ret = new Roo.factory(cfg);
41108 ret.render && ret.render(false, ''); // render blank..
41118 * @class Roo.bootstrap.panel.Grid
41119 * @extends Roo.bootstrap.panel.Content
41121 * Create a new GridPanel.
41122 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41123 * @param {Object} config A the config object
41129 Roo.bootstrap.panel.Grid = function(config)
41133 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41134 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41136 config.el = this.wrapper;
41137 //this.el = this.wrapper;
41139 if (config.container) {
41140 // ctor'ed from a Border/panel.grid
41143 this.wrapper.setStyle("overflow", "hidden");
41144 this.wrapper.addClass('roo-grid-container');
41149 if(config.toolbar){
41150 var tool_el = this.wrapper.createChild();
41151 this.toolbar = Roo.factory(config.toolbar);
41153 if (config.toolbar.items) {
41154 ti = config.toolbar.items ;
41155 delete config.toolbar.items ;
41159 this.toolbar.render(tool_el);
41160 for(var i =0;i < ti.length;i++) {
41161 // Roo.log(['add child', items[i]]);
41162 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41164 this.toolbar.items = nitems;
41166 delete config.toolbar;
41169 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41170 config.grid.scrollBody = true;;
41171 config.grid.monitorWindowResize = false; // turn off autosizing
41172 config.grid.autoHeight = false;
41173 config.grid.autoWidth = false;
41175 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41177 if (config.background) {
41178 // render grid on panel activation (if panel background)
41179 this.on('activate', function(gp) {
41180 if (!gp.grid.rendered) {
41181 gp.grid.render(this.wrapper);
41182 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41187 this.grid.render(this.wrapper);
41188 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41191 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41192 // ??? needed ??? config.el = this.wrapper;
41197 // xtype created footer. - not sure if will work as we normally have to render first..
41198 if (this.footer && !this.footer.el && this.footer.xtype) {
41200 var ctr = this.grid.getView().getFooterPanel(true);
41201 this.footer.dataSource = this.grid.dataSource;
41202 this.footer = Roo.factory(this.footer, Roo);
41203 this.footer.render(ctr);
41213 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41214 getId : function(){
41215 return this.grid.id;
41219 * Returns the grid for this panel
41220 * @return {Roo.bootstrap.Table}
41222 getGrid : function(){
41226 setSize : function(width, height){
41227 if(!this.ignoreResize(width, height)){
41228 var grid = this.grid;
41229 var size = this.adjustForComponents(width, height);
41230 // tfoot is not a footer?
41233 var gridel = grid.getGridEl();
41234 gridel.setSize(size.width, size.height);
41236 var tbd = grid.getGridEl().select('tbody', true).first();
41237 var thd = grid.getGridEl().select('thead',true).first();
41238 var tbf= grid.getGridEl().select('tfoot', true).first();
41241 size.height -= tbf.getHeight();
41244 size.height -= thd.getHeight();
41247 tbd.setSize(size.width, size.height );
41248 // this is for the account management tab -seems to work there.
41249 var thd = grid.getGridEl().select('thead',true).first();
41251 // tbd.setSize(size.width, size.height - thd.getHeight());
41260 beforeSlide : function(){
41261 this.grid.getView().scroller.clip();
41264 afterSlide : function(){
41265 this.grid.getView().scroller.unclip();
41268 destroy : function(){
41269 this.grid.destroy();
41271 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41276 * @class Roo.bootstrap.panel.Nest
41277 * @extends Roo.bootstrap.panel.Content
41279 * Create a new Panel, that can contain a layout.Border.
41282 * @param {Roo.BorderLayout} layout The layout for this panel
41283 * @param {String/Object} config A string to set only the title or a config object
41285 Roo.bootstrap.panel.Nest = function(config)
41287 // construct with only one argument..
41288 /* FIXME - implement nicer consturctors
41289 if (layout.layout) {
41291 layout = config.layout;
41292 delete config.layout;
41294 if (layout.xtype && !layout.getEl) {
41295 // then layout needs constructing..
41296 layout = Roo.factory(layout, Roo);
41300 config.el = config.layout.getEl();
41302 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41304 config.layout.monitorWindowResize = false; // turn off autosizing
41305 this.layout = config.layout;
41306 this.layout.getEl().addClass("roo-layout-nested-layout");
41307 this.layout.parent = this;
41314 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41316 setSize : function(width, height){
41317 if(!this.ignoreResize(width, height)){
41318 var size = this.adjustForComponents(width, height);
41319 var el = this.layout.getEl();
41320 if (size.height < 1) {
41321 el.setWidth(size.width);
41323 el.setSize(size.width, size.height);
41325 var touch = el.dom.offsetWidth;
41326 this.layout.layout();
41327 // ie requires a double layout on the first pass
41328 if(Roo.isIE && !this.initialized){
41329 this.initialized = true;
41330 this.layout.layout();
41335 // activate all subpanels if not currently active..
41337 setActiveState : function(active){
41338 this.active = active;
41339 this.setActiveClass(active);
41342 this.fireEvent("deactivate", this);
41346 this.fireEvent("activate", this);
41347 // not sure if this should happen before or after..
41348 if (!this.layout) {
41349 return; // should not happen..
41352 for (var r in this.layout.regions) {
41353 reg = this.layout.getRegion(r);
41354 if (reg.getActivePanel()) {
41355 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41356 reg.setActivePanel(reg.getActivePanel());
41359 if (!reg.panels.length) {
41362 reg.showPanel(reg.getPanel(0));
41371 * Returns the nested BorderLayout for this panel
41372 * @return {Roo.BorderLayout}
41374 getLayout : function(){
41375 return this.layout;
41379 * Adds a xtype elements to the layout of the nested panel
41383 xtype : 'ContentPanel',
41390 xtype : 'NestedLayoutPanel',
41396 items : [ ... list of content panels or nested layout panels.. ]
41400 * @param {Object} cfg Xtype definition of item to add.
41402 addxtype : function(cfg) {
41403 return this.layout.addxtype(cfg);
41408 * Ext JS Library 1.1.1
41409 * Copyright(c) 2006-2007, Ext JS, LLC.
41411 * Originally Released Under LGPL - original licence link has changed is not relivant.
41414 * <script type="text/javascript">
41417 * @class Roo.TabPanel
41418 * @extends Roo.util.Observable
41419 * A lightweight tab container.
41423 // basic tabs 1, built from existing content
41424 var tabs = new Roo.TabPanel("tabs1");
41425 tabs.addTab("script", "View Script");
41426 tabs.addTab("markup", "View Markup");
41427 tabs.activate("script");
41429 // more advanced tabs, built from javascript
41430 var jtabs = new Roo.TabPanel("jtabs");
41431 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41433 // set up the UpdateManager
41434 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41435 var updater = tab2.getUpdateManager();
41436 updater.setDefaultUrl("ajax1.htm");
41437 tab2.on('activate', updater.refresh, updater, true);
41439 // Use setUrl for Ajax loading
41440 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41441 tab3.setUrl("ajax2.htm", null, true);
41444 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41447 jtabs.activate("jtabs-1");
41450 * Create a new TabPanel.
41451 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41452 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41454 Roo.bootstrap.panel.Tabs = function(config){
41456 * The container element for this TabPanel.
41457 * @type Roo.Element
41459 this.el = Roo.get(config.el);
41462 if(typeof config == "boolean"){
41463 this.tabPosition = config ? "bottom" : "top";
41465 Roo.apply(this, config);
41469 if(this.tabPosition == "bottom"){
41470 // if tabs are at the bottom = create the body first.
41471 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41472 this.el.addClass("roo-tabs-bottom");
41474 // next create the tabs holders
41476 if (this.tabPosition == "west"){
41478 var reg = this.region; // fake it..
41480 if (!reg.mgr.parent) {
41483 reg = reg.mgr.parent.region;
41485 Roo.log("got nest?");
41487 if (reg.mgr.getRegion('west')) {
41488 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41489 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41490 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41491 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41492 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41500 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41501 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41502 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41503 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41508 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41511 // finally - if tabs are at the top, then create the body last..
41512 if(this.tabPosition != "bottom"){
41513 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41514 * @type Roo.Element
41516 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41517 this.el.addClass("roo-tabs-top");
41521 this.bodyEl.setStyle("position", "relative");
41523 this.active = null;
41524 this.activateDelegate = this.activate.createDelegate(this);
41529 * Fires when the active tab changes
41530 * @param {Roo.TabPanel} this
41531 * @param {Roo.TabPanelItem} activePanel The new active tab
41535 * @event beforetabchange
41536 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41537 * @param {Roo.TabPanel} this
41538 * @param {Object} e Set cancel to true on this object to cancel the tab change
41539 * @param {Roo.TabPanelItem} tab The tab being changed to
41541 "beforetabchange" : true
41544 Roo.EventManager.onWindowResize(this.onResize, this);
41545 this.cpad = this.el.getPadding("lr");
41546 this.hiddenCount = 0;
41549 // toolbar on the tabbar support...
41550 if (this.toolbar) {
41551 alert("no toolbar support yet");
41552 this.toolbar = false;
41554 var tcfg = this.toolbar;
41555 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41556 this.toolbar = new Roo.Toolbar(tcfg);
41557 if (Roo.isSafari) {
41558 var tbl = tcfg.container.child('table', true);
41559 tbl.setAttribute('width', '100%');
41567 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41570 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41572 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41574 tabPosition : "top",
41576 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41578 currentTabWidth : 0,
41580 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41584 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41588 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41590 preferredTabWidth : 175,
41592 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41594 resizeTabs : false,
41596 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41598 monitorResize : true,
41600 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41602 toolbar : false, // set by caller..
41604 region : false, /// set by caller
41606 disableTooltips : true, // not used yet...
41609 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41610 * @param {String} id The id of the div to use <b>or create</b>
41611 * @param {String} text The text for the tab
41612 * @param {String} content (optional) Content to put in the TabPanelItem body
41613 * @param {Boolean} closable (optional) True to create a close icon on the tab
41614 * @return {Roo.TabPanelItem} The created TabPanelItem
41616 addTab : function(id, text, content, closable, tpl)
41618 var item = new Roo.bootstrap.panel.TabItem({
41622 closable : closable,
41625 this.addTabItem(item);
41627 item.setContent(content);
41633 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41634 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41635 * @return {Roo.TabPanelItem}
41637 getTab : function(id){
41638 return this.items[id];
41642 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41643 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41645 hideTab : function(id){
41646 var t = this.items[id];
41649 this.hiddenCount++;
41650 this.autoSizeTabs();
41655 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41656 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41658 unhideTab : function(id){
41659 var t = this.items[id];
41661 t.setHidden(false);
41662 this.hiddenCount--;
41663 this.autoSizeTabs();
41668 * Adds an existing {@link Roo.TabPanelItem}.
41669 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41671 addTabItem : function(item)
41673 this.items[item.id] = item;
41674 this.items.push(item);
41675 this.autoSizeTabs();
41676 // if(this.resizeTabs){
41677 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41678 // this.autoSizeTabs();
41680 // item.autoSize();
41685 * Removes a {@link Roo.TabPanelItem}.
41686 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41688 removeTab : function(id){
41689 var items = this.items;
41690 var tab = items[id];
41691 if(!tab) { return; }
41692 var index = items.indexOf(tab);
41693 if(this.active == tab && items.length > 1){
41694 var newTab = this.getNextAvailable(index);
41699 this.stripEl.dom.removeChild(tab.pnode.dom);
41700 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41701 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41703 items.splice(index, 1);
41704 delete this.items[tab.id];
41705 tab.fireEvent("close", tab);
41706 tab.purgeListeners();
41707 this.autoSizeTabs();
41710 getNextAvailable : function(start){
41711 var items = this.items;
41713 // look for a next tab that will slide over to
41714 // replace the one being removed
41715 while(index < items.length){
41716 var item = items[++index];
41717 if(item && !item.isHidden()){
41721 // if one isn't found select the previous tab (on the left)
41724 var item = items[--index];
41725 if(item && !item.isHidden()){
41733 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41734 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41736 disableTab : function(id){
41737 var tab = this.items[id];
41738 if(tab && this.active != tab){
41744 * Enables a {@link Roo.TabPanelItem} that is disabled.
41745 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41747 enableTab : function(id){
41748 var tab = this.items[id];
41753 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41754 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41755 * @return {Roo.TabPanelItem} The TabPanelItem.
41757 activate : function(id)
41759 //Roo.log('activite:' + id);
41761 var tab = this.items[id];
41765 if(tab == this.active || tab.disabled){
41769 this.fireEvent("beforetabchange", this, e, tab);
41770 if(e.cancel !== true && !tab.disabled){
41772 this.active.hide();
41774 this.active = this.items[id];
41775 this.active.show();
41776 this.fireEvent("tabchange", this, this.active);
41782 * Gets the active {@link Roo.TabPanelItem}.
41783 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41785 getActiveTab : function(){
41786 return this.active;
41790 * Updates the tab body element to fit the height of the container element
41791 * for overflow scrolling
41792 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41794 syncHeight : function(targetHeight){
41795 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41796 var bm = this.bodyEl.getMargins();
41797 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41798 this.bodyEl.setHeight(newHeight);
41802 onResize : function(){
41803 if(this.monitorResize){
41804 this.autoSizeTabs();
41809 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41811 beginUpdate : function(){
41812 this.updating = true;
41816 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41818 endUpdate : function(){
41819 this.updating = false;
41820 this.autoSizeTabs();
41824 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41826 autoSizeTabs : function()
41828 var count = this.items.length;
41829 var vcount = count - this.hiddenCount;
41832 this.stripEl.hide();
41834 this.stripEl.show();
41837 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41842 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41843 var availWidth = Math.floor(w / vcount);
41844 var b = this.stripBody;
41845 if(b.getWidth() > w){
41846 var tabs = this.items;
41847 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41848 if(availWidth < this.minTabWidth){
41849 /*if(!this.sleft){ // incomplete scrolling code
41850 this.createScrollButtons();
41853 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41856 if(this.currentTabWidth < this.preferredTabWidth){
41857 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41863 * Returns the number of tabs in this TabPanel.
41866 getCount : function(){
41867 return this.items.length;
41871 * Resizes all the tabs to the passed width
41872 * @param {Number} The new width
41874 setTabWidth : function(width){
41875 this.currentTabWidth = width;
41876 for(var i = 0, len = this.items.length; i < len; i++) {
41877 if(!this.items[i].isHidden()) {
41878 this.items[i].setWidth(width);
41884 * Destroys this TabPanel
41885 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41887 destroy : function(removeEl){
41888 Roo.EventManager.removeResizeListener(this.onResize, this);
41889 for(var i = 0, len = this.items.length; i < len; i++){
41890 this.items[i].purgeListeners();
41892 if(removeEl === true){
41893 this.el.update("");
41898 createStrip : function(container)
41900 var strip = document.createElement("nav");
41901 strip.className = Roo.bootstrap.version == 4 ?
41902 "navbar-light bg-light" :
41903 "navbar navbar-default"; //"x-tabs-wrap";
41904 container.appendChild(strip);
41908 createStripList : function(strip)
41910 // div wrapper for retard IE
41911 // returns the "tr" element.
41912 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41913 //'<div class="x-tabs-strip-wrap">'+
41914 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41915 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41916 return strip.firstChild; //.firstChild.firstChild.firstChild;
41918 createBody : function(container)
41920 var body = document.createElement("div");
41921 Roo.id(body, "tab-body");
41922 //Roo.fly(body).addClass("x-tabs-body");
41923 Roo.fly(body).addClass("tab-content");
41924 container.appendChild(body);
41927 createItemBody :function(bodyEl, id){
41928 var body = Roo.getDom(id);
41930 body = document.createElement("div");
41933 //Roo.fly(body).addClass("x-tabs-item-body");
41934 Roo.fly(body).addClass("tab-pane");
41935 bodyEl.insertBefore(body, bodyEl.firstChild);
41939 createStripElements : function(stripEl, text, closable, tpl)
41941 var td = document.createElement("li"); // was td..
41942 td.className = 'nav-item';
41944 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41947 stripEl.appendChild(td);
41949 td.className = "x-tabs-closable";
41950 if(!this.closeTpl){
41951 this.closeTpl = new Roo.Template(
41952 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41953 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41954 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41957 var el = this.closeTpl.overwrite(td, {"text": text});
41958 var close = el.getElementsByTagName("div")[0];
41959 var inner = el.getElementsByTagName("em")[0];
41960 return {"el": el, "close": close, "inner": inner};
41963 // not sure what this is..
41964 // if(!this.tabTpl){
41965 //this.tabTpl = new Roo.Template(
41966 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41967 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41969 // this.tabTpl = new Roo.Template(
41970 // '<a href="#">' +
41971 // '<span unselectable="on"' +
41972 // (this.disableTooltips ? '' : ' title="{text}"') +
41973 // ' >{text}</span></a>'
41979 var template = tpl || this.tabTpl || false;
41982 template = new Roo.Template(
41983 Roo.bootstrap.version == 4 ?
41985 '<a class="nav-link" href="#" unselectable="on"' +
41986 (this.disableTooltips ? '' : ' title="{text}"') +
41989 '<a class="nav-link" href="#">' +
41990 '<span unselectable="on"' +
41991 (this.disableTooltips ? '' : ' title="{text}"') +
41992 ' >{text}</span></a>'
41997 switch (typeof(template)) {
42001 template = new Roo.Template(template);
42007 var el = template.overwrite(td, {"text": text});
42009 var inner = el.getElementsByTagName("span")[0];
42011 return {"el": el, "inner": inner};
42019 * @class Roo.TabPanelItem
42020 * @extends Roo.util.Observable
42021 * Represents an individual item (tab plus body) in a TabPanel.
42022 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42023 * @param {String} id The id of this TabPanelItem
42024 * @param {String} text The text for the tab of this TabPanelItem
42025 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42027 Roo.bootstrap.panel.TabItem = function(config){
42029 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42030 * @type Roo.TabPanel
42032 this.tabPanel = config.panel;
42034 * The id for this TabPanelItem
42037 this.id = config.id;
42039 this.disabled = false;
42041 this.text = config.text;
42043 this.loaded = false;
42044 this.closable = config.closable;
42047 * The body element for this TabPanelItem.
42048 * @type Roo.Element
42050 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42051 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42052 this.bodyEl.setStyle("display", "block");
42053 this.bodyEl.setStyle("zoom", "1");
42054 //this.hideAction();
42056 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42058 this.el = Roo.get(els.el);
42059 this.inner = Roo.get(els.inner, true);
42060 this.textEl = Roo.bootstrap.version == 4 ?
42061 this.el : Roo.get(this.el.dom.firstChild, true);
42063 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42064 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42067 // this.el.on("mousedown", this.onTabMouseDown, this);
42068 this.el.on("click", this.onTabClick, this);
42070 if(config.closable){
42071 var c = Roo.get(els.close, true);
42072 c.dom.title = this.closeText;
42073 c.addClassOnOver("close-over");
42074 c.on("click", this.closeClick, this);
42080 * Fires when this tab becomes the active tab.
42081 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42082 * @param {Roo.TabPanelItem} this
42086 * @event beforeclose
42087 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42088 * @param {Roo.TabPanelItem} this
42089 * @param {Object} e Set cancel to true on this object to cancel the close.
42091 "beforeclose": true,
42094 * Fires when this tab is closed.
42095 * @param {Roo.TabPanelItem} this
42099 * @event deactivate
42100 * Fires when this tab is no longer the active tab.
42101 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42102 * @param {Roo.TabPanelItem} this
42104 "deactivate" : true
42106 this.hidden = false;
42108 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42111 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42113 purgeListeners : function(){
42114 Roo.util.Observable.prototype.purgeListeners.call(this);
42115 this.el.removeAllListeners();
42118 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42121 this.status_node.addClass("active");
42124 this.tabPanel.stripWrap.repaint();
42126 this.fireEvent("activate", this.tabPanel, this);
42130 * Returns true if this tab is the active tab.
42131 * @return {Boolean}
42133 isActive : function(){
42134 return this.tabPanel.getActiveTab() == this;
42138 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42141 this.status_node.removeClass("active");
42143 this.fireEvent("deactivate", this.tabPanel, this);
42146 hideAction : function(){
42147 this.bodyEl.hide();
42148 this.bodyEl.setStyle("position", "absolute");
42149 this.bodyEl.setLeft("-20000px");
42150 this.bodyEl.setTop("-20000px");
42153 showAction : function(){
42154 this.bodyEl.setStyle("position", "relative");
42155 this.bodyEl.setTop("");
42156 this.bodyEl.setLeft("");
42157 this.bodyEl.show();
42161 * Set the tooltip for the tab.
42162 * @param {String} tooltip The tab's tooltip
42164 setTooltip : function(text){
42165 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42166 this.textEl.dom.qtip = text;
42167 this.textEl.dom.removeAttribute('title');
42169 this.textEl.dom.title = text;
42173 onTabClick : function(e){
42174 e.preventDefault();
42175 this.tabPanel.activate(this.id);
42178 onTabMouseDown : function(e){
42179 e.preventDefault();
42180 this.tabPanel.activate(this.id);
42183 getWidth : function(){
42184 return this.inner.getWidth();
42187 setWidth : function(width){
42188 var iwidth = width - this.linode.getPadding("lr");
42189 this.inner.setWidth(iwidth);
42190 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42191 this.linode.setWidth(width);
42195 * Show or hide the tab
42196 * @param {Boolean} hidden True to hide or false to show.
42198 setHidden : function(hidden){
42199 this.hidden = hidden;
42200 this.linode.setStyle("display", hidden ? "none" : "");
42204 * Returns true if this tab is "hidden"
42205 * @return {Boolean}
42207 isHidden : function(){
42208 return this.hidden;
42212 * Returns the text for this tab
42215 getText : function(){
42219 autoSize : function(){
42220 //this.el.beginMeasure();
42221 this.textEl.setWidth(1);
42223 * #2804 [new] Tabs in Roojs
42224 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42226 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42227 //this.el.endMeasure();
42231 * Sets the text for the tab (Note: this also sets the tooltip text)
42232 * @param {String} text The tab's text and tooltip
42234 setText : function(text){
42236 this.textEl.update(text);
42237 this.setTooltip(text);
42238 //if(!this.tabPanel.resizeTabs){
42239 // this.autoSize();
42243 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42245 activate : function(){
42246 this.tabPanel.activate(this.id);
42250 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42252 disable : function(){
42253 if(this.tabPanel.active != this){
42254 this.disabled = true;
42255 this.status_node.addClass("disabled");
42260 * Enables this TabPanelItem if it was previously disabled.
42262 enable : function(){
42263 this.disabled = false;
42264 this.status_node.removeClass("disabled");
42268 * Sets the content for this TabPanelItem.
42269 * @param {String} content The content
42270 * @param {Boolean} loadScripts true to look for and load scripts
42272 setContent : function(content, loadScripts){
42273 this.bodyEl.update(content, loadScripts);
42277 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42278 * @return {Roo.UpdateManager} The UpdateManager
42280 getUpdateManager : function(){
42281 return this.bodyEl.getUpdateManager();
42285 * Set a URL to be used to load the content for this TabPanelItem.
42286 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42287 * @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)
42288 * @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)
42289 * @return {Roo.UpdateManager} The UpdateManager
42291 setUrl : function(url, params, loadOnce){
42292 if(this.refreshDelegate){
42293 this.un('activate', this.refreshDelegate);
42295 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42296 this.on("activate", this.refreshDelegate);
42297 return this.bodyEl.getUpdateManager();
42301 _handleRefresh : function(url, params, loadOnce){
42302 if(!loadOnce || !this.loaded){
42303 var updater = this.bodyEl.getUpdateManager();
42304 updater.update(url, params, this._setLoaded.createDelegate(this));
42309 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42310 * Will fail silently if the setUrl method has not been called.
42311 * This does not activate the panel, just updates its content.
42313 refresh : function(){
42314 if(this.refreshDelegate){
42315 this.loaded = false;
42316 this.refreshDelegate();
42321 _setLoaded : function(){
42322 this.loaded = true;
42326 closeClick : function(e){
42329 this.fireEvent("beforeclose", this, o);
42330 if(o.cancel !== true){
42331 this.tabPanel.removeTab(this.id);
42335 * The text displayed in the tooltip for the close icon.
42338 closeText : "Close this tab"
42341 * This script refer to:
42342 * Title: International Telephone Input
42343 * Author: Jack O'Connor
42344 * Code version: v12.1.12
42345 * Availability: https://github.com/jackocnr/intl-tel-input.git
42348 Roo.bootstrap.PhoneInputData = function() {
42351 "Afghanistan (افغانستان)",
42356 "Albania (Shqipëri)",
42361 "Algeria (الجزائر)",
42386 "Antigua and Barbuda",
42396 "Armenia (Հայաստան)",
42412 "Austria (Österreich)",
42417 "Azerbaijan (Azərbaycan)",
42427 "Bahrain (البحرين)",
42432 "Bangladesh (বাংলাদেশ)",
42442 "Belarus (Беларусь)",
42447 "Belgium (België)",
42477 "Bosnia and Herzegovina (Босна и Херцеговина)",
42492 "British Indian Ocean Territory",
42497 "British Virgin Islands",
42507 "Bulgaria (България)",
42517 "Burundi (Uburundi)",
42522 "Cambodia (កម្ពុជា)",
42527 "Cameroon (Cameroun)",
42536 ["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"]
42539 "Cape Verde (Kabu Verdi)",
42544 "Caribbean Netherlands",
42555 "Central African Republic (République centrafricaine)",
42575 "Christmas Island",
42581 "Cocos (Keeling) Islands",
42592 "Comoros (جزر القمر)",
42597 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42602 "Congo (Republic) (Congo-Brazzaville)",
42622 "Croatia (Hrvatska)",
42643 "Czech Republic (Česká republika)",
42648 "Denmark (Danmark)",
42663 "Dominican Republic (República Dominicana)",
42667 ["809", "829", "849"]
42685 "Equatorial Guinea (Guinea Ecuatorial)",
42705 "Falkland Islands (Islas Malvinas)",
42710 "Faroe Islands (Føroyar)",
42731 "French Guiana (Guyane française)",
42736 "French Polynesia (Polynésie française)",
42751 "Georgia (საქართველო)",
42756 "Germany (Deutschland)",
42776 "Greenland (Kalaallit Nunaat)",
42813 "Guinea-Bissau (Guiné Bissau)",
42838 "Hungary (Magyarország)",
42843 "Iceland (Ísland)",
42863 "Iraq (العراق)",
42879 "Israel (ישראל)",
42906 "Jordan (الأردن)",
42911 "Kazakhstan (Казахстан)",
42932 "Kuwait (الكويت)",
42937 "Kyrgyzstan (Кыргызстан)",
42947 "Latvia (Latvija)",
42952 "Lebanon (لبنان)",
42967 "Libya (ليبيا)",
42977 "Lithuania (Lietuva)",
42992 "Macedonia (FYROM) (Македонија)",
42997 "Madagascar (Madagasikara)",
43027 "Marshall Islands",
43037 "Mauritania (موريتانيا)",
43042 "Mauritius (Moris)",
43063 "Moldova (Republica Moldova)",
43073 "Mongolia (Монгол)",
43078 "Montenegro (Crna Gora)",
43088 "Morocco (المغرب)",
43094 "Mozambique (Moçambique)",
43099 "Myanmar (Burma) (မြန်မာ)",
43104 "Namibia (Namibië)",
43119 "Netherlands (Nederland)",
43124 "New Caledonia (Nouvelle-Calédonie)",
43159 "North Korea (조선 민주주의 인민 공화국)",
43164 "Northern Mariana Islands",
43180 "Pakistan (پاکستان)",
43190 "Palestine (فلسطين)",
43200 "Papua New Guinea",
43242 "Réunion (La Réunion)",
43248 "Romania (România)",
43264 "Saint Barthélemy",
43275 "Saint Kitts and Nevis",
43285 "Saint Martin (Saint-Martin (partie française))",
43291 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43296 "Saint Vincent and the Grenadines",
43311 "São Tomé and Príncipe (São Tomé e Príncipe)",
43316 "Saudi Arabia (المملكة العربية السعودية)",
43321 "Senegal (Sénégal)",
43351 "Slovakia (Slovensko)",
43356 "Slovenia (Slovenija)",
43366 "Somalia (Soomaaliya)",
43376 "South Korea (대한민국)",
43381 "South Sudan (جنوب السودان)",
43391 "Sri Lanka (ශ්රී ලංකාව)",
43396 "Sudan (السودان)",
43406 "Svalbard and Jan Mayen",
43417 "Sweden (Sverige)",
43422 "Switzerland (Schweiz)",
43427 "Syria (سوريا)",
43472 "Trinidad and Tobago",
43477 "Tunisia (تونس)",
43482 "Turkey (Türkiye)",
43492 "Turks and Caicos Islands",
43502 "U.S. Virgin Islands",
43512 "Ukraine (Україна)",
43517 "United Arab Emirates (الإمارات العربية المتحدة)",
43539 "Uzbekistan (Oʻzbekiston)",
43549 "Vatican City (Città del Vaticano)",
43560 "Vietnam (Việt Nam)",
43565 "Wallis and Futuna (Wallis-et-Futuna)",
43570 "Western Sahara (الصحراء الغربية)",
43576 "Yemen (اليمن)",
43600 * This script refer to:
43601 * Title: International Telephone Input
43602 * Author: Jack O'Connor
43603 * Code version: v12.1.12
43604 * Availability: https://github.com/jackocnr/intl-tel-input.git
43608 * @class Roo.bootstrap.PhoneInput
43609 * @extends Roo.bootstrap.TriggerField
43610 * An input with International dial-code selection
43612 * @cfg {String} defaultDialCode default '+852'
43613 * @cfg {Array} preferedCountries default []
43616 * Create a new PhoneInput.
43617 * @param {Object} config Configuration options
43620 Roo.bootstrap.PhoneInput = function(config) {
43621 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43624 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43626 listWidth: undefined,
43628 selectedClass: 'active',
43630 invalidClass : "has-warning",
43632 validClass: 'has-success',
43634 allowed: '0123456789',
43639 * @cfg {String} defaultDialCode The default dial code when initializing the input
43641 defaultDialCode: '+852',
43644 * @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
43646 preferedCountries: false,
43648 getAutoCreate : function()
43650 var data = Roo.bootstrap.PhoneInputData();
43651 var align = this.labelAlign || this.parentLabelAlign();
43654 this.allCountries = [];
43655 this.dialCodeMapping = [];
43657 for (var i = 0; i < data.length; i++) {
43659 this.allCountries[i] = {
43663 priority: c[3] || 0,
43664 areaCodes: c[4] || null
43666 this.dialCodeMapping[c[2]] = {
43669 priority: c[3] || 0,
43670 areaCodes: c[4] || null
43682 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43683 maxlength: this.max_length,
43684 cls : 'form-control tel-input',
43685 autocomplete: 'new-password'
43688 var hiddenInput = {
43691 cls: 'hidden-tel-input'
43695 hiddenInput.name = this.name;
43698 if (this.disabled) {
43699 input.disabled = true;
43702 var flag_container = {
43719 cls: this.hasFeedback ? 'has-feedback' : '',
43725 cls: 'dial-code-holder',
43732 cls: 'roo-select2-container input-group',
43739 if (this.fieldLabel.length) {
43742 tooltip: 'This field is required'
43748 cls: 'control-label',
43754 html: this.fieldLabel
43757 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43763 if(this.indicatorpos == 'right') {
43764 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43771 if(align == 'left') {
43779 if(this.labelWidth > 12){
43780 label.style = "width: " + this.labelWidth + 'px';
43782 if(this.labelWidth < 13 && this.labelmd == 0){
43783 this.labelmd = this.labelWidth;
43785 if(this.labellg > 0){
43786 label.cls += ' col-lg-' + this.labellg;
43787 input.cls += ' col-lg-' + (12 - this.labellg);
43789 if(this.labelmd > 0){
43790 label.cls += ' col-md-' + this.labelmd;
43791 container.cls += ' col-md-' + (12 - this.labelmd);
43793 if(this.labelsm > 0){
43794 label.cls += ' col-sm-' + this.labelsm;
43795 container.cls += ' col-sm-' + (12 - this.labelsm);
43797 if(this.labelxs > 0){
43798 label.cls += ' col-xs-' + this.labelxs;
43799 container.cls += ' col-xs-' + (12 - this.labelxs);
43809 var settings = this;
43811 ['xs','sm','md','lg'].map(function(size){
43812 if (settings[size]) {
43813 cfg.cls += ' col-' + size + '-' + settings[size];
43817 this.store = new Roo.data.Store({
43818 proxy : new Roo.data.MemoryProxy({}),
43819 reader : new Roo.data.JsonReader({
43830 'name' : 'dialCode',
43834 'name' : 'priority',
43838 'name' : 'areaCodes',
43845 if(!this.preferedCountries) {
43846 this.preferedCountries = [
43853 var p = this.preferedCountries.reverse();
43856 for (var i = 0; i < p.length; i++) {
43857 for (var j = 0; j < this.allCountries.length; j++) {
43858 if(this.allCountries[j].iso2 == p[i]) {
43859 var t = this.allCountries[j];
43860 this.allCountries.splice(j,1);
43861 this.allCountries.unshift(t);
43867 this.store.proxy.data = {
43869 data: this.allCountries
43875 initEvents : function()
43878 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43880 this.indicator = this.indicatorEl();
43881 this.flag = this.flagEl();
43882 this.dialCodeHolder = this.dialCodeHolderEl();
43884 this.trigger = this.el.select('div.flag-box',true).first();
43885 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43890 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43891 _this.list.setWidth(lw);
43894 this.list.on('mouseover', this.onViewOver, this);
43895 this.list.on('mousemove', this.onViewMove, this);
43896 this.inputEl().on("keyup", this.onKeyUp, this);
43897 this.inputEl().on("keypress", this.onKeyPress, this);
43899 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43901 this.view = new Roo.View(this.list, this.tpl, {
43902 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43905 this.view.on('click', this.onViewClick, this);
43906 this.setValue(this.defaultDialCode);
43909 onTriggerClick : function(e)
43911 Roo.log('trigger click');
43916 if(this.isExpanded()){
43918 this.hasFocus = false;
43920 this.store.load({});
43921 this.hasFocus = true;
43926 isExpanded : function()
43928 return this.list.isVisible();
43931 collapse : function()
43933 if(!this.isExpanded()){
43937 Roo.get(document).un('mousedown', this.collapseIf, this);
43938 Roo.get(document).un('mousewheel', this.collapseIf, this);
43939 this.fireEvent('collapse', this);
43943 expand : function()
43947 if(this.isExpanded() || !this.hasFocus){
43951 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43952 this.list.setWidth(lw);
43955 this.restrictHeight();
43957 Roo.get(document).on('mousedown', this.collapseIf, this);
43958 Roo.get(document).on('mousewheel', this.collapseIf, this);
43960 this.fireEvent('expand', this);
43963 restrictHeight : function()
43965 this.list.alignTo(this.inputEl(), this.listAlign);
43966 this.list.alignTo(this.inputEl(), this.listAlign);
43969 onViewOver : function(e, t)
43971 if(this.inKeyMode){
43974 var item = this.view.findItemFromChild(t);
43977 var index = this.view.indexOf(item);
43978 this.select(index, false);
43983 onViewClick : function(view, doFocus, el, e)
43985 var index = this.view.getSelectedIndexes()[0];
43987 var r = this.store.getAt(index);
43990 this.onSelect(r, index);
43992 if(doFocus !== false && !this.blockFocus){
43993 this.inputEl().focus();
43997 onViewMove : function(e, t)
43999 this.inKeyMode = false;
44002 select : function(index, scrollIntoView)
44004 this.selectedIndex = index;
44005 this.view.select(index);
44006 if(scrollIntoView !== false){
44007 var el = this.view.getNode(index);
44009 this.list.scrollChildIntoView(el, false);
44014 createList : function()
44016 this.list = Roo.get(document.body).createChild({
44018 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44019 style: 'display:none'
44022 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44025 collapseIf : function(e)
44027 var in_combo = e.within(this.el);
44028 var in_list = e.within(this.list);
44029 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44031 if (in_combo || in_list || is_list) {
44037 onSelect : function(record, index)
44039 if(this.fireEvent('beforeselect', this, record, index) !== false){
44041 this.setFlagClass(record.data.iso2);
44042 this.setDialCode(record.data.dialCode);
44043 this.hasFocus = false;
44045 this.fireEvent('select', this, record, index);
44049 flagEl : function()
44051 var flag = this.el.select('div.flag',true).first();
44058 dialCodeHolderEl : function()
44060 var d = this.el.select('input.dial-code-holder',true).first();
44067 setDialCode : function(v)
44069 this.dialCodeHolder.dom.value = '+'+v;
44072 setFlagClass : function(n)
44074 this.flag.dom.className = 'flag '+n;
44077 getValue : function()
44079 var v = this.inputEl().getValue();
44080 if(this.dialCodeHolder) {
44081 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44086 setValue : function(v)
44088 var d = this.getDialCode(v);
44090 //invalid dial code
44091 if(v.length == 0 || !d || d.length == 0) {
44093 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44094 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44100 this.setFlagClass(this.dialCodeMapping[d].iso2);
44101 this.setDialCode(d);
44102 this.inputEl().dom.value = v.replace('+'+d,'');
44103 this.hiddenEl().dom.value = this.getValue();
44108 getDialCode : function(v)
44112 if (v.length == 0) {
44113 return this.dialCodeHolder.dom.value;
44117 if (v.charAt(0) != "+") {
44120 var numericChars = "";
44121 for (var i = 1; i < v.length; i++) {
44122 var c = v.charAt(i);
44125 if (this.dialCodeMapping[numericChars]) {
44126 dialCode = v.substr(1, i);
44128 if (numericChars.length == 4) {
44138 this.setValue(this.defaultDialCode);
44142 hiddenEl : function()
44144 return this.el.select('input.hidden-tel-input',true).first();
44147 // after setting val
44148 onKeyUp : function(e){
44149 this.setValue(this.getValue());
44152 onKeyPress : function(e){
44153 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44160 * @class Roo.bootstrap.MoneyField
44161 * @extends Roo.bootstrap.ComboBox
44162 * Bootstrap MoneyField class
44165 * Create a new MoneyField.
44166 * @param {Object} config Configuration options
44169 Roo.bootstrap.MoneyField = function(config) {
44171 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44175 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44178 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44180 allowDecimals : true,
44182 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44184 decimalSeparator : ".",
44186 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44188 decimalPrecision : 0,
44190 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44192 allowNegative : true,
44194 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44198 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44200 minValue : Number.NEGATIVE_INFINITY,
44202 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44204 maxValue : Number.MAX_VALUE,
44206 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44208 minText : "The minimum value for this field is {0}",
44210 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44212 maxText : "The maximum value for this field is {0}",
44214 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44215 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44217 nanText : "{0} is not a valid number",
44219 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44223 * @cfg {String} defaults currency of the MoneyField
44224 * value should be in lkey
44226 defaultCurrency : false,
44228 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44230 thousandsDelimiter : false,
44232 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44243 getAutoCreate : function()
44245 var align = this.labelAlign || this.parentLabelAlign();
44257 cls : 'form-control roo-money-amount-input',
44258 autocomplete: 'new-password'
44261 var hiddenInput = {
44265 cls: 'hidden-number-input'
44268 if(this.max_length) {
44269 input.maxlength = this.max_length;
44273 hiddenInput.name = this.name;
44276 if (this.disabled) {
44277 input.disabled = true;
44280 var clg = 12 - this.inputlg;
44281 var cmd = 12 - this.inputmd;
44282 var csm = 12 - this.inputsm;
44283 var cxs = 12 - this.inputxs;
44287 cls : 'row roo-money-field',
44291 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44295 cls: 'roo-select2-container input-group',
44299 cls : 'form-control roo-money-currency-input',
44300 autocomplete: 'new-password',
44302 name : this.currencyName
44306 cls : 'input-group-addon',
44320 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44324 cls: this.hasFeedback ? 'has-feedback' : '',
44335 if (this.fieldLabel.length) {
44338 tooltip: 'This field is required'
44344 cls: 'control-label',
44350 html: this.fieldLabel
44353 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44359 if(this.indicatorpos == 'right') {
44360 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44367 if(align == 'left') {
44375 if(this.labelWidth > 12){
44376 label.style = "width: " + this.labelWidth + 'px';
44378 if(this.labelWidth < 13 && this.labelmd == 0){
44379 this.labelmd = this.labelWidth;
44381 if(this.labellg > 0){
44382 label.cls += ' col-lg-' + this.labellg;
44383 input.cls += ' col-lg-' + (12 - this.labellg);
44385 if(this.labelmd > 0){
44386 label.cls += ' col-md-' + this.labelmd;
44387 container.cls += ' col-md-' + (12 - this.labelmd);
44389 if(this.labelsm > 0){
44390 label.cls += ' col-sm-' + this.labelsm;
44391 container.cls += ' col-sm-' + (12 - this.labelsm);
44393 if(this.labelxs > 0){
44394 label.cls += ' col-xs-' + this.labelxs;
44395 container.cls += ' col-xs-' + (12 - this.labelxs);
44406 var settings = this;
44408 ['xs','sm','md','lg'].map(function(size){
44409 if (settings[size]) {
44410 cfg.cls += ' col-' + size + '-' + settings[size];
44417 initEvents : function()
44419 this.indicator = this.indicatorEl();
44421 this.initCurrencyEvent();
44423 this.initNumberEvent();
44426 initCurrencyEvent : function()
44429 throw "can not find store for combo";
44432 this.store = Roo.factory(this.store, Roo.data);
44433 this.store.parent = this;
44437 this.triggerEl = this.el.select('.input-group-addon', true).first();
44439 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44444 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44445 _this.list.setWidth(lw);
44448 this.list.on('mouseover', this.onViewOver, this);
44449 this.list.on('mousemove', this.onViewMove, this);
44450 this.list.on('scroll', this.onViewScroll, this);
44453 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44456 this.view = new Roo.View(this.list, this.tpl, {
44457 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44460 this.view.on('click', this.onViewClick, this);
44462 this.store.on('beforeload', this.onBeforeLoad, this);
44463 this.store.on('load', this.onLoad, this);
44464 this.store.on('loadexception', this.onLoadException, this);
44466 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44467 "up" : function(e){
44468 this.inKeyMode = true;
44472 "down" : function(e){
44473 if(!this.isExpanded()){
44474 this.onTriggerClick();
44476 this.inKeyMode = true;
44481 "enter" : function(e){
44484 if(this.fireEvent("specialkey", this, e)){
44485 this.onViewClick(false);
44491 "esc" : function(e){
44495 "tab" : function(e){
44498 if(this.fireEvent("specialkey", this, e)){
44499 this.onViewClick(false);
44507 doRelay : function(foo, bar, hname){
44508 if(hname == 'down' || this.scope.isExpanded()){
44509 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44517 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44521 initNumberEvent : function(e)
44523 this.inputEl().on("keydown" , this.fireKey, this);
44524 this.inputEl().on("focus", this.onFocus, this);
44525 this.inputEl().on("blur", this.onBlur, this);
44527 this.inputEl().relayEvent('keyup', this);
44529 if(this.indicator){
44530 this.indicator.addClass('invisible');
44533 this.originalValue = this.getValue();
44535 if(this.validationEvent == 'keyup'){
44536 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44537 this.inputEl().on('keyup', this.filterValidation, this);
44539 else if(this.validationEvent !== false){
44540 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44543 if(this.selectOnFocus){
44544 this.on("focus", this.preFocus, this);
44547 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44548 this.inputEl().on("keypress", this.filterKeys, this);
44550 this.inputEl().relayEvent('keypress', this);
44553 var allowed = "0123456789";
44555 if(this.allowDecimals){
44556 allowed += this.decimalSeparator;
44559 if(this.allowNegative){
44563 if(this.thousandsDelimiter) {
44567 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44569 var keyPress = function(e){
44571 var k = e.getKey();
44573 var c = e.getCharCode();
44576 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44577 allowed.indexOf(String.fromCharCode(c)) === -1
44583 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44587 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44592 this.inputEl().on("keypress", keyPress, this);
44596 onTriggerClick : function(e)
44603 this.loadNext = false;
44605 if(this.isExpanded()){
44610 this.hasFocus = true;
44612 if(this.triggerAction == 'all') {
44613 this.doQuery(this.allQuery, true);
44617 this.doQuery(this.getRawValue());
44620 getCurrency : function()
44622 var v = this.currencyEl().getValue();
44627 restrictHeight : function()
44629 this.list.alignTo(this.currencyEl(), this.listAlign);
44630 this.list.alignTo(this.currencyEl(), this.listAlign);
44633 onViewClick : function(view, doFocus, el, e)
44635 var index = this.view.getSelectedIndexes()[0];
44637 var r = this.store.getAt(index);
44640 this.onSelect(r, index);
44644 onSelect : function(record, index){
44646 if(this.fireEvent('beforeselect', this, record, index) !== false){
44648 this.setFromCurrencyData(index > -1 ? record.data : false);
44652 this.fireEvent('select', this, record, index);
44656 setFromCurrencyData : function(o)
44660 this.lastCurrency = o;
44662 if (this.currencyField) {
44663 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44665 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44668 this.lastSelectionText = currency;
44670 //setting default currency
44671 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44672 this.setCurrency(this.defaultCurrency);
44676 this.setCurrency(currency);
44679 setFromData : function(o)
44683 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44685 this.setFromCurrencyData(c);
44690 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44692 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44695 this.setValue(value);
44699 setCurrency : function(v)
44701 this.currencyValue = v;
44704 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44709 setValue : function(v)
44711 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44717 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44719 this.inputEl().dom.value = (v == '') ? '' :
44720 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44722 if(!this.allowZero && v === '0') {
44723 this.hiddenEl().dom.value = '';
44724 this.inputEl().dom.value = '';
44731 getRawValue : function()
44733 var v = this.inputEl().getValue();
44738 getValue : function()
44740 return this.fixPrecision(this.parseValue(this.getRawValue()));
44743 parseValue : function(value)
44745 if(this.thousandsDelimiter) {
44747 r = new RegExp(",", "g");
44748 value = value.replace(r, "");
44751 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44752 return isNaN(value) ? '' : value;
44756 fixPrecision : function(value)
44758 if(this.thousandsDelimiter) {
44760 r = new RegExp(",", "g");
44761 value = value.replace(r, "");
44764 var nan = isNaN(value);
44766 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44767 return nan ? '' : value;
44769 return parseFloat(value).toFixed(this.decimalPrecision);
44772 decimalPrecisionFcn : function(v)
44774 return Math.floor(v);
44777 validateValue : function(value)
44779 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44783 var num = this.parseValue(value);
44786 this.markInvalid(String.format(this.nanText, value));
44790 if(num < this.minValue){
44791 this.markInvalid(String.format(this.minText, this.minValue));
44795 if(num > this.maxValue){
44796 this.markInvalid(String.format(this.maxText, this.maxValue));
44803 validate : function()
44805 if(this.disabled || this.allowBlank){
44810 var currency = this.getCurrency();
44812 if(this.validateValue(this.getRawValue()) && currency.length){
44817 this.markInvalid();
44821 getName: function()
44826 beforeBlur : function()
44832 var v = this.parseValue(this.getRawValue());
44839 onBlur : function()
44843 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44844 //this.el.removeClass(this.focusClass);
44847 this.hasFocus = false;
44849 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44853 var v = this.getValue();
44855 if(String(v) !== String(this.startValue)){
44856 this.fireEvent('change', this, v, this.startValue);
44859 this.fireEvent("blur", this);
44862 inputEl : function()
44864 return this.el.select('.roo-money-amount-input', true).first();
44867 currencyEl : function()
44869 return this.el.select('.roo-money-currency-input', true).first();
44872 hiddenEl : function()
44874 return this.el.select('input.hidden-number-input',true).first();
44878 * @class Roo.bootstrap.BezierSignature
44879 * @extends Roo.bootstrap.Component
44880 * Bootstrap BezierSignature class
44881 * This script refer to:
44882 * Title: Signature Pad
44884 * Availability: https://github.com/szimek/signature_pad
44887 * Create a new BezierSignature
44888 * @param {Object} config The config object
44891 Roo.bootstrap.BezierSignature = function(config){
44892 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44898 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44905 mouse_btn_down: true,
44908 * @cfg {int} canvas height
44910 canvas_height: '200px',
44913 * @cfg {float|function} Radius of a single dot.
44918 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44923 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44928 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44933 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44938 * @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.
44940 bg_color: 'rgba(0, 0, 0, 0)',
44943 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44945 dot_color: 'black',
44948 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44950 velocity_filter_weight: 0.7,
44953 * @cfg {function} Callback when stroke begin.
44958 * @cfg {function} Callback when stroke end.
44962 getAutoCreate : function()
44964 var cls = 'roo-signature column';
44967 cls += ' ' + this.cls;
44977 for(var i = 0; i < col_sizes.length; i++) {
44978 if(this[col_sizes[i]]) {
44979 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44989 cls: 'roo-signature-body',
44993 cls: 'roo-signature-body-canvas',
44994 height: this.canvas_height,
44995 width: this.canvas_width
45002 style: 'display: none'
45010 initEvents: function()
45012 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45014 var canvas = this.canvasEl();
45016 // mouse && touch event swapping...
45017 canvas.dom.style.touchAction = 'none';
45018 canvas.dom.style.msTouchAction = 'none';
45020 this.mouse_btn_down = false;
45021 canvas.on('mousedown', this._handleMouseDown, this);
45022 canvas.on('mousemove', this._handleMouseMove, this);
45023 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45025 if (window.PointerEvent) {
45026 canvas.on('pointerdown', this._handleMouseDown, this);
45027 canvas.on('pointermove', this._handleMouseMove, this);
45028 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45031 if ('ontouchstart' in window) {
45032 canvas.on('touchstart', this._handleTouchStart, this);
45033 canvas.on('touchmove', this._handleTouchMove, this);
45034 canvas.on('touchend', this._handleTouchEnd, this);
45037 Roo.EventManager.onWindowResize(this.resize, this, true);
45039 // file input event
45040 this.fileEl().on('change', this.uploadImage, this);
45047 resize: function(){
45049 var canvas = this.canvasEl().dom;
45050 var ctx = this.canvasElCtx();
45051 var img_data = false;
45053 if(canvas.width > 0) {
45054 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45056 // setting canvas width will clean img data
45059 var style = window.getComputedStyle ?
45060 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45062 var padding_left = parseInt(style.paddingLeft) || 0;
45063 var padding_right = parseInt(style.paddingRight) || 0;
45065 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45068 ctx.putImageData(img_data, 0, 0);
45072 _handleMouseDown: function(e)
45074 if (e.browserEvent.which === 1) {
45075 this.mouse_btn_down = true;
45076 this.strokeBegin(e);
45080 _handleMouseMove: function (e)
45082 if (this.mouse_btn_down) {
45083 this.strokeMoveUpdate(e);
45087 _handleMouseUp: function (e)
45089 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45090 this.mouse_btn_down = false;
45095 _handleTouchStart: function (e) {
45097 e.preventDefault();
45098 if (e.browserEvent.targetTouches.length === 1) {
45099 // var touch = e.browserEvent.changedTouches[0];
45100 // this.strokeBegin(touch);
45102 this.strokeBegin(e); // assume e catching the correct xy...
45106 _handleTouchMove: function (e) {
45107 e.preventDefault();
45108 // var touch = event.targetTouches[0];
45109 // _this._strokeMoveUpdate(touch);
45110 this.strokeMoveUpdate(e);
45113 _handleTouchEnd: function (e) {
45114 var wasCanvasTouched = e.target === this.canvasEl().dom;
45115 if (wasCanvasTouched) {
45116 e.preventDefault();
45117 // var touch = event.changedTouches[0];
45118 // _this._strokeEnd(touch);
45123 reset: function () {
45124 this._lastPoints = [];
45125 this._lastVelocity = 0;
45126 this._lastWidth = (this.min_width + this.max_width) / 2;
45127 this.canvasElCtx().fillStyle = this.dot_color;
45130 strokeMoveUpdate: function(e)
45132 this.strokeUpdate(e);
45134 if (this.throttle) {
45135 this.throttleStroke(this.strokeUpdate, this.throttle);
45138 this.strokeUpdate(e);
45142 strokeBegin: function(e)
45144 var newPointGroup = {
45145 color: this.dot_color,
45149 if (typeof this.onBegin === 'function') {
45153 this.curve_data.push(newPointGroup);
45155 this.strokeUpdate(e);
45158 strokeUpdate: function(e)
45160 var rect = this.canvasEl().dom.getBoundingClientRect();
45161 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45162 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45163 var lastPoints = lastPointGroup.points;
45164 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45165 var isLastPointTooClose = lastPoint
45166 ? point.distanceTo(lastPoint) <= this.min_distance
45168 var color = lastPointGroup.color;
45169 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45170 var curve = this.addPoint(point);
45172 this.drawDot({color: color, point: point});
45175 this.drawCurve({color: color, curve: curve});
45185 strokeEnd: function(e)
45187 this.strokeUpdate(e);
45188 if (typeof this.onEnd === 'function') {
45193 addPoint: function (point) {
45194 var _lastPoints = this._lastPoints;
45195 _lastPoints.push(point);
45196 if (_lastPoints.length > 2) {
45197 if (_lastPoints.length === 3) {
45198 _lastPoints.unshift(_lastPoints[0]);
45200 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45201 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45202 _lastPoints.shift();
45208 calculateCurveWidths: function (startPoint, endPoint) {
45209 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45210 (1 - this.velocity_filter_weight) * this._lastVelocity;
45212 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45215 start: this._lastWidth
45218 this._lastVelocity = velocity;
45219 this._lastWidth = newWidth;
45223 drawDot: function (_a) {
45224 var color = _a.color, point = _a.point;
45225 var ctx = this.canvasElCtx();
45226 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45228 this.drawCurveSegment(point.x, point.y, width);
45230 ctx.fillStyle = color;
45234 drawCurve: function (_a) {
45235 var color = _a.color, curve = _a.curve;
45236 var ctx = this.canvasElCtx();
45237 var widthDelta = curve.endWidth - curve.startWidth;
45238 var drawSteps = Math.floor(curve.length()) * 2;
45240 ctx.fillStyle = color;
45241 for (var i = 0; i < drawSteps; i += 1) {
45242 var t = i / drawSteps;
45248 var x = uuu * curve.startPoint.x;
45249 x += 3 * uu * t * curve.control1.x;
45250 x += 3 * u * tt * curve.control2.x;
45251 x += ttt * curve.endPoint.x;
45252 var y = uuu * curve.startPoint.y;
45253 y += 3 * uu * t * curve.control1.y;
45254 y += 3 * u * tt * curve.control2.y;
45255 y += ttt * curve.endPoint.y;
45256 var width = curve.startWidth + ttt * widthDelta;
45257 this.drawCurveSegment(x, y, width);
45263 drawCurveSegment: function (x, y, width) {
45264 var ctx = this.canvasElCtx();
45266 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45267 this.is_empty = false;
45272 var ctx = this.canvasElCtx();
45273 var canvas = this.canvasEl().dom;
45274 ctx.fillStyle = this.bg_color;
45275 ctx.clearRect(0, 0, canvas.width, canvas.height);
45276 ctx.fillRect(0, 0, canvas.width, canvas.height);
45277 this.curve_data = [];
45279 this.is_empty = true;
45284 return this.el.select('input',true).first();
45287 canvasEl: function()
45289 return this.el.select('canvas',true).first();
45292 canvasElCtx: function()
45294 return this.el.select('canvas',true).first().dom.getContext('2d');
45297 getImage: function(type)
45299 if(this.is_empty) {
45304 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45307 drawFromImage: function(img_src)
45309 var img = new Image();
45311 img.onload = function(){
45312 this.canvasElCtx().drawImage(img, 0, 0);
45317 this.is_empty = false;
45320 selectImage: function()
45322 this.fileEl().dom.click();
45325 uploadImage: function(e)
45327 var reader = new FileReader();
45329 reader.onload = function(e){
45330 var img = new Image();
45331 img.onload = function(){
45333 this.canvasElCtx().drawImage(img, 0, 0);
45335 img.src = e.target.result;
45338 reader.readAsDataURL(e.target.files[0]);
45341 // Bezier Point Constructor
45342 Point: (function () {
45343 function Point(x, y, time) {
45346 this.time = time || Date.now();
45348 Point.prototype.distanceTo = function (start) {
45349 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45351 Point.prototype.equals = function (other) {
45352 return this.x === other.x && this.y === other.y && this.time === other.time;
45354 Point.prototype.velocityFrom = function (start) {
45355 return this.time !== start.time
45356 ? this.distanceTo(start) / (this.time - start.time)
45363 // Bezier Constructor
45364 Bezier: (function () {
45365 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45366 this.startPoint = startPoint;
45367 this.control2 = control2;
45368 this.control1 = control1;
45369 this.endPoint = endPoint;
45370 this.startWidth = startWidth;
45371 this.endWidth = endWidth;
45373 Bezier.fromPoints = function (points, widths, scope) {
45374 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45375 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45376 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45378 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45379 var dx1 = s1.x - s2.x;
45380 var dy1 = s1.y - s2.y;
45381 var dx2 = s2.x - s3.x;
45382 var dy2 = s2.y - s3.y;
45383 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45384 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45385 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45386 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45387 var dxm = m1.x - m2.x;
45388 var dym = m1.y - m2.y;
45389 var k = l2 / (l1 + l2);
45390 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45391 var tx = s2.x - cm.x;
45392 var ty = s2.y - cm.y;
45394 c1: new scope.Point(m1.x + tx, m1.y + ty),
45395 c2: new scope.Point(m2.x + tx, m2.y + ty)
45398 Bezier.prototype.length = function () {
45403 for (var i = 0; i <= steps; i += 1) {
45405 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45406 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45408 var xdiff = cx - px;
45409 var ydiff = cy - py;
45410 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45417 Bezier.prototype.point = function (t, start, c1, c2, end) {
45418 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45419 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45420 + (3.0 * c2 * (1.0 - t) * t * t)
45421 + (end * t * t * t);
45426 throttleStroke: function(fn, wait) {
45427 if (wait === void 0) { wait = 250; }
45429 var timeout = null;
45433 var later = function () {
45434 previous = Date.now();
45436 result = fn.apply(storedContext, storedArgs);
45438 storedContext = null;
45442 return function wrapper() {
45444 for (var _i = 0; _i < arguments.length; _i++) {
45445 args[_i] = arguments[_i];
45447 var now = Date.now();
45448 var remaining = wait - (now - previous);
45449 storedContext = this;
45451 if (remaining <= 0 || remaining > wait) {
45453 clearTimeout(timeout);
45457 result = fn.apply(storedContext, storedArgs);
45459 storedContext = null;
45463 else if (!timeout) {
45464 timeout = window.setTimeout(later, remaining);