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 {Roo.data.Store} store
20204 * The data source for the calendar
20208 * @cfg {Number} startDay
20209 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20217 getAutoCreate : function(){
20220 var fc_button = function(name, corner, style, content ) {
20221 return Roo.apply({},{
20223 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20225 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20228 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20239 style : 'width:100%',
20246 cls : 'fc-header-left',
20248 fc_button('prev', 'left', 'arrow', '‹' ),
20249 fc_button('next', 'right', 'arrow', '›' ),
20250 { tag: 'span', cls: 'fc-header-space' },
20251 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20259 cls : 'fc-header-center',
20263 cls: 'fc-header-title',
20266 html : 'month / year'
20274 cls : 'fc-header-right',
20276 /* fc_button('month', 'left', '', 'month' ),
20277 fc_button('week', '', '', 'week' ),
20278 fc_button('day', 'right', '', 'day' )
20290 header = this.header;
20293 var cal_heads = function() {
20295 // fixme - handle this.
20297 for (var i =0; i < Date.dayNames.length; i++) {
20298 var d = Date.dayNames[i];
20301 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20302 html : d.substring(0,3)
20306 ret[0].cls += ' fc-first';
20307 ret[6].cls += ' fc-last';
20310 var cal_cell = function(n) {
20313 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20318 cls: 'fc-day-number',
20322 cls: 'fc-day-content',
20326 style: 'position: relative;' // height: 17px;
20338 var cal_rows = function() {
20341 for (var r = 0; r < 6; r++) {
20348 for (var i =0; i < Date.dayNames.length; i++) {
20349 var d = Date.dayNames[i];
20350 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20353 row.cn[0].cls+=' fc-first';
20354 row.cn[0].cn[0].style = 'min-height:90px';
20355 row.cn[6].cls+=' fc-last';
20359 ret[0].cls += ' fc-first';
20360 ret[4].cls += ' fc-prev-last';
20361 ret[5].cls += ' fc-last';
20368 cls: 'fc-border-separate',
20369 style : 'width:100%',
20377 cls : 'fc-first fc-last',
20395 cls : 'fc-content',
20396 style : "position: relative;",
20399 cls : 'fc-view fc-view-month fc-grid',
20400 style : 'position: relative',
20401 unselectable : 'on',
20404 cls : 'fc-event-container',
20405 style : 'position:absolute;z-index:8;top:0;left:0;'
20423 initEvents : function()
20426 throw "can not find store for calendar";
20432 style: "text-align:center",
20436 style: "background-color:white;width:50%;margin:250 auto",
20440 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20451 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20453 var size = this.el.select('.fc-content', true).first().getSize();
20454 this.maskEl.setSize(size.width, size.height);
20455 this.maskEl.enableDisplayMode("block");
20456 if(!this.loadMask){
20457 this.maskEl.hide();
20460 this.store = Roo.factory(this.store, Roo.data);
20461 this.store.on('load', this.onLoad, this);
20462 this.store.on('beforeload', this.onBeforeLoad, this);
20466 this.cells = this.el.select('.fc-day',true);
20467 //Roo.log(this.cells);
20468 this.textNodes = this.el.query('.fc-day-number');
20469 this.cells.addClassOnOver('fc-state-hover');
20471 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20472 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20473 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20474 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20476 this.on('monthchange', this.onMonthChange, this);
20478 this.update(new Date().clearTime());
20481 resize : function() {
20482 var sz = this.el.getSize();
20484 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20485 this.el.select('.fc-day-content div',true).setHeight(34);
20490 showPrevMonth : function(e){
20491 this.update(this.activeDate.add("mo", -1));
20493 showToday : function(e){
20494 this.update(new Date().clearTime());
20497 showNextMonth : function(e){
20498 this.update(this.activeDate.add("mo", 1));
20502 showPrevYear : function(){
20503 this.update(this.activeDate.add("y", -1));
20507 showNextYear : function(){
20508 this.update(this.activeDate.add("y", 1));
20513 update : function(date)
20515 var vd = this.activeDate;
20516 this.activeDate = date;
20517 // if(vd && this.el){
20518 // var t = date.getTime();
20519 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20520 // Roo.log('using add remove');
20522 // this.fireEvent('monthchange', this, date);
20524 // this.cells.removeClass("fc-state-highlight");
20525 // this.cells.each(function(c){
20526 // if(c.dateValue == t){
20527 // c.addClass("fc-state-highlight");
20528 // setTimeout(function(){
20529 // try{c.dom.firstChild.focus();}catch(e){}
20539 var days = date.getDaysInMonth();
20541 var firstOfMonth = date.getFirstDateOfMonth();
20542 var startingPos = firstOfMonth.getDay()-this.startDay;
20544 if(startingPos < this.startDay){
20548 var pm = date.add(Date.MONTH, -1);
20549 var prevStart = pm.getDaysInMonth()-startingPos;
20551 this.cells = this.el.select('.fc-day',true);
20552 this.textNodes = this.el.query('.fc-day-number');
20553 this.cells.addClassOnOver('fc-state-hover');
20555 var cells = this.cells.elements;
20556 var textEls = this.textNodes;
20558 Roo.each(cells, function(cell){
20559 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20562 days += startingPos;
20564 // convert everything to numbers so it's fast
20565 var day = 86400000;
20566 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20569 //Roo.log(prevStart);
20571 var today = new Date().clearTime().getTime();
20572 var sel = date.clearTime().getTime();
20573 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20574 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20575 var ddMatch = this.disabledDatesRE;
20576 var ddText = this.disabledDatesText;
20577 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20578 var ddaysText = this.disabledDaysText;
20579 var format = this.format;
20581 var setCellClass = function(cal, cell){
20585 //Roo.log('set Cell Class');
20587 var t = d.getTime();
20591 cell.dateValue = t;
20593 cell.className += " fc-today";
20594 cell.className += " fc-state-highlight";
20595 cell.title = cal.todayText;
20598 // disable highlight in other month..
20599 //cell.className += " fc-state-highlight";
20604 cell.className = " fc-state-disabled";
20605 cell.title = cal.minText;
20609 cell.className = " fc-state-disabled";
20610 cell.title = cal.maxText;
20614 if(ddays.indexOf(d.getDay()) != -1){
20615 cell.title = ddaysText;
20616 cell.className = " fc-state-disabled";
20619 if(ddMatch && format){
20620 var fvalue = d.dateFormat(format);
20621 if(ddMatch.test(fvalue)){
20622 cell.title = ddText.replace("%0", fvalue);
20623 cell.className = " fc-state-disabled";
20627 if (!cell.initialClassName) {
20628 cell.initialClassName = cell.dom.className;
20631 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20636 for(; i < startingPos; i++) {
20637 textEls[i].innerHTML = (++prevStart);
20638 d.setDate(d.getDate()+1);
20640 cells[i].className = "fc-past fc-other-month";
20641 setCellClass(this, cells[i]);
20646 for(; i < days; i++){
20647 intDay = i - startingPos + 1;
20648 textEls[i].innerHTML = (intDay);
20649 d.setDate(d.getDate()+1);
20651 cells[i].className = ''; // "x-date-active";
20652 setCellClass(this, cells[i]);
20656 for(; i < 42; i++) {
20657 textEls[i].innerHTML = (++extraDays);
20658 d.setDate(d.getDate()+1);
20660 cells[i].className = "fc-future fc-other-month";
20661 setCellClass(this, cells[i]);
20664 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20666 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20668 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20669 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20671 if(totalRows != 6){
20672 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20673 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20676 this.fireEvent('monthchange', this, date);
20680 if(!this.internalRender){
20681 var main = this.el.dom.firstChild;
20682 var w = main.offsetWidth;
20683 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20684 Roo.fly(main).setWidth(w);
20685 this.internalRender = true;
20686 // opera does not respect the auto grow header center column
20687 // then, after it gets a width opera refuses to recalculate
20688 // without a second pass
20689 if(Roo.isOpera && !this.secondPass){
20690 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20691 this.secondPass = true;
20692 this.update.defer(10, this, [date]);
20699 findCell : function(dt) {
20700 dt = dt.clearTime().getTime();
20702 this.cells.each(function(c){
20703 //Roo.log("check " +c.dateValue + '?=' + dt);
20704 if(c.dateValue == dt){
20714 findCells : function(ev) {
20715 var s = ev.start.clone().clearTime().getTime();
20717 var e= ev.end.clone().clearTime().getTime();
20720 this.cells.each(function(c){
20721 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20723 if(c.dateValue > e){
20726 if(c.dateValue < s){
20735 // findBestRow: function(cells)
20739 // for (var i =0 ; i < cells.length;i++) {
20740 // ret = Math.max(cells[i].rows || 0,ret);
20747 addItem : function(ev)
20749 // look for vertical location slot in
20750 var cells = this.findCells(ev);
20752 // ev.row = this.findBestRow(cells);
20754 // work out the location.
20758 for(var i =0; i < cells.length; i++) {
20760 cells[i].row = cells[0].row;
20763 cells[i].row = cells[i].row + 1;
20773 if (crow.start.getY() == cells[i].getY()) {
20775 crow.end = cells[i];
20792 cells[0].events.push(ev);
20794 this.calevents.push(ev);
20797 clearEvents: function() {
20799 if(!this.calevents){
20803 Roo.each(this.cells.elements, function(c){
20809 Roo.each(this.calevents, function(e) {
20810 Roo.each(e.els, function(el) {
20811 el.un('mouseenter' ,this.onEventEnter, this);
20812 el.un('mouseleave' ,this.onEventLeave, this);
20817 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20823 renderEvents: function()
20827 this.cells.each(function(c) {
20836 if(c.row != c.events.length){
20837 r = 4 - (4 - (c.row - c.events.length));
20840 c.events = ev.slice(0, r);
20841 c.more = ev.slice(r);
20843 if(c.more.length && c.more.length == 1){
20844 c.events.push(c.more.pop());
20847 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20851 this.cells.each(function(c) {
20853 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20856 for (var e = 0; e < c.events.length; e++){
20857 var ev = c.events[e];
20858 var rows = ev.rows;
20860 for(var i = 0; i < rows.length; i++) {
20862 // how many rows should it span..
20865 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20866 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20868 unselectable : "on",
20871 cls: 'fc-event-inner',
20875 // cls: 'fc-event-time',
20876 // html : cells.length > 1 ? '' : ev.time
20880 cls: 'fc-event-title',
20881 html : String.format('{0}', ev.title)
20888 cls: 'ui-resizable-handle ui-resizable-e',
20889 html : '  '
20896 cfg.cls += ' fc-event-start';
20898 if ((i+1) == rows.length) {
20899 cfg.cls += ' fc-event-end';
20902 var ctr = _this.el.select('.fc-event-container',true).first();
20903 var cg = ctr.createChild(cfg);
20905 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20906 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20908 var r = (c.more.length) ? 1 : 0;
20909 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20910 cg.setWidth(ebox.right - sbox.x -2);
20912 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20913 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20914 cg.on('click', _this.onEventClick, _this, ev);
20925 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20926 style : 'position: absolute',
20927 unselectable : "on",
20930 cls: 'fc-event-inner',
20934 cls: 'fc-event-title',
20942 cls: 'ui-resizable-handle ui-resizable-e',
20943 html : '  '
20949 var ctr = _this.el.select('.fc-event-container',true).first();
20950 var cg = ctr.createChild(cfg);
20952 var sbox = c.select('.fc-day-content',true).first().getBox();
20953 var ebox = c.select('.fc-day-content',true).first().getBox();
20955 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20956 cg.setWidth(ebox.right - sbox.x -2);
20958 cg.on('click', _this.onMoreEventClick, _this, c.more);
20968 onEventEnter: function (e, el,event,d) {
20969 this.fireEvent('evententer', this, el, event);
20972 onEventLeave: function (e, el,event,d) {
20973 this.fireEvent('eventleave', this, el, event);
20976 onEventClick: function (e, el,event,d) {
20977 this.fireEvent('eventclick', this, el, event);
20980 onMonthChange: function () {
20984 onMoreEventClick: function(e, el, more)
20988 this.calpopover.placement = 'right';
20989 this.calpopover.setTitle('More');
20991 this.calpopover.setContent('');
20993 var ctr = this.calpopover.el.select('.popover-content', true).first();
20995 Roo.each(more, function(m){
20997 cls : 'fc-event-hori fc-event-draggable',
21000 var cg = ctr.createChild(cfg);
21002 cg.on('click', _this.onEventClick, _this, m);
21005 this.calpopover.show(el);
21010 onLoad: function ()
21012 this.calevents = [];
21015 if(this.store.getCount() > 0){
21016 this.store.data.each(function(d){
21019 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21020 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21021 time : d.data.start_time,
21022 title : d.data.title,
21023 description : d.data.description,
21024 venue : d.data.venue
21029 this.renderEvents();
21031 if(this.calevents.length && this.loadMask){
21032 this.maskEl.hide();
21036 onBeforeLoad: function()
21038 this.clearEvents();
21040 this.maskEl.show();
21054 * @class Roo.bootstrap.Popover
21055 * @extends Roo.bootstrap.Component
21056 * Bootstrap Popover class
21057 * @cfg {String} html contents of the popover (or false to use children..)
21058 * @cfg {String} title of popover (or false to hide)
21059 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21060 * @cfg {String} trigger click || hover (or false to trigger manually)
21061 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21062 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21063 * - if false and it has a 'parent' then it will be automatically added to that element
21064 * - if string - Roo.get will be called
21065 * @cfg {Number} delay - delay before showing
21068 * Create a new Popover
21069 * @param {Object} config The config object
21072 Roo.bootstrap.Popover = function(config){
21073 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21079 * After the popover show
21081 * @param {Roo.bootstrap.Popover} this
21086 * After the popover hide
21088 * @param {Roo.bootstrap.Popover} this
21094 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21099 placement : 'right',
21100 trigger : 'hover', // hover
21106 can_build_overlaid : false,
21108 maskEl : false, // the mask element
21111 alignEl : false, // when show is called with an element - this get's stored.
21113 getChildContainer : function()
21115 return this.contentEl;
21118 getPopoverHeader : function()
21120 this.title = true; // flag not to hide it..
21121 this.headerEl.addClass('p-0');
21122 return this.headerEl
21126 getAutoCreate : function(){
21129 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21130 style: 'display:block',
21136 cls : 'popover-inner ',
21140 cls: 'popover-title popover-header',
21141 html : this.title === false ? '' : this.title
21144 cls : 'popover-content popover-body ' + (this.cls || ''),
21145 html : this.html || ''
21156 * @param {string} the title
21158 setTitle: function(str)
21162 this.headerEl.dom.innerHTML = str;
21167 * @param {string} the body content
21169 setContent: function(str)
21172 if (this.contentEl) {
21173 this.contentEl.dom.innerHTML = str;
21177 // as it get's added to the bottom of the page.
21178 onRender : function(ct, position)
21180 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21185 var cfg = Roo.apply({}, this.getAutoCreate());
21189 cfg.cls += ' ' + this.cls;
21192 cfg.style = this.style;
21194 //Roo.log("adding to ");
21195 this.el = Roo.get(document.body).createChild(cfg, position);
21196 // Roo.log(this.el);
21199 this.contentEl = this.el.select('.popover-content',true).first();
21200 this.headerEl = this.el.select('.popover-title',true).first();
21203 if(typeof(this.items) != 'undefined'){
21204 var items = this.items;
21207 for(var i =0;i < items.length;i++) {
21208 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21212 this.items = nitems;
21214 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21215 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21222 resizeMask : function()
21224 this.maskEl.setSize(
21225 Roo.lib.Dom.getViewWidth(true),
21226 Roo.lib.Dom.getViewHeight(true)
21230 initEvents : function()
21234 Roo.bootstrap.Popover.register(this);
21237 this.arrowEl = this.el.select('.arrow',true).first();
21238 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21239 this.el.enableDisplayMode('block');
21243 if (this.over === false && !this.parent()) {
21246 if (this.triggers === false) {
21251 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21252 var triggers = this.trigger ? this.trigger.split(' ') : [];
21253 Roo.each(triggers, function(trigger) {
21255 if (trigger == 'click') {
21256 on_el.on('click', this.toggle, this);
21257 } else if (trigger != 'manual') {
21258 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21259 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21261 on_el.on(eventIn ,this.enter, this);
21262 on_el.on(eventOut, this.leave, this);
21272 toggle : function () {
21273 this.hoverState == 'in' ? this.leave() : this.enter();
21276 enter : function () {
21278 clearTimeout(this.timeout);
21280 this.hoverState = 'in';
21282 if (!this.delay || !this.delay.show) {
21287 this.timeout = setTimeout(function () {
21288 if (_t.hoverState == 'in') {
21291 }, this.delay.show)
21294 leave : function() {
21295 clearTimeout(this.timeout);
21297 this.hoverState = 'out';
21299 if (!this.delay || !this.delay.hide) {
21304 this.timeout = setTimeout(function () {
21305 if (_t.hoverState == 'out') {
21308 }, this.delay.hide)
21312 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21313 * @param {string} (left|right|top|bottom) position
21315 show : function (on_el, placement)
21317 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21318 on_el = on_el || false; // default to false
21321 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21322 on_el = this.parent().el;
21323 } else if (this.over) {
21324 on_el = Roo.get(this.over);
21329 this.alignEl = Roo.get( on_el );
21332 this.render(document.body);
21338 if (this.title === false) {
21339 this.headerEl.hide();
21344 this.el.dom.style.display = 'block';
21347 if (this.alignEl) {
21348 this.updatePosition(this.placement, true);
21351 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21352 var es = this.el.getSize();
21353 var x = Roo.lib.Dom.getViewWidth()/2;
21354 var y = Roo.lib.Dom.getViewHeight()/2;
21355 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21360 //var arrow = this.el.select('.arrow',true).first();
21361 //arrow.set(align[2],
21363 this.el.addClass('in');
21367 this.hoverState = 'in';
21370 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21371 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21372 this.maskEl.dom.style.display = 'block';
21373 this.maskEl.addClass('show');
21375 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21377 this.fireEvent('show', this);
21381 * fire this manually after loading a grid in the table for example
21382 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21383 * @param {Boolean} try and move it if we cant get right position.
21385 updatePosition : function(placement, try_move)
21387 // allow for calling with no parameters
21388 placement = placement ? placement : this.placement;
21389 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21391 this.el.removeClass([
21392 'fade','top','bottom', 'left', 'right','in',
21393 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21395 this.el.addClass(placement + ' bs-popover-' + placement);
21397 if (!this.alignEl ) {
21401 switch (placement) {
21403 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21404 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21405 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21406 //normal display... or moved up/down.
21407 this.el.setXY(offset);
21408 var xy = this.alignEl.getAnchorXY('tr', false);
21410 this.arrowEl.setXY(xy);
21413 // continue through...
21414 return this.updatePosition('left', false);
21418 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21419 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21420 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21421 //normal display... or moved up/down.
21422 this.el.setXY(offset);
21423 var xy = this.alignEl.getAnchorXY('tl', false);
21424 xy[0]-=10;xy[1]+=5; // << fix me
21425 this.arrowEl.setXY(xy);
21429 return this.updatePosition('right', false);
21432 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21433 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21434 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21435 //normal display... or moved up/down.
21436 this.el.setXY(offset);
21437 var xy = this.alignEl.getAnchorXY('t', false);
21438 xy[1]-=10; // << fix me
21439 this.arrowEl.setXY(xy);
21443 return this.updatePosition('bottom', false);
21446 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21447 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21448 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21449 //normal display... or moved up/down.
21450 this.el.setXY(offset);
21451 var xy = this.alignEl.getAnchorXY('b', false);
21452 xy[1]+=2; // << fix me
21453 this.arrowEl.setXY(xy);
21457 return this.updatePosition('top', false);
21468 this.el.setXY([0,0]);
21469 this.el.removeClass('in');
21471 this.hoverState = null;
21472 this.maskEl.hide(); // always..
21473 this.fireEvent('hide', this);
21479 Roo.apply(Roo.bootstrap.Popover, {
21482 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21483 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21484 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21485 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21490 clickHander : false,
21494 onMouseDown : function(e)
21496 if (this.popups.length && !e.getTarget(".roo-popover")) {
21497 /// what is nothing is showing..
21506 register : function(popup)
21508 if (!Roo.bootstrap.Popover.clickHandler) {
21509 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21511 // hide other popups.
21512 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21513 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21514 this.hideAll(); //<< why?
21515 //this.popups.push(popup);
21517 hideAll : function()
21519 this.popups.forEach(function(p) {
21523 onShow : function() {
21524 Roo.bootstrap.Popover.popups.push(this);
21526 onHide : function() {
21527 Roo.bootstrap.Popover.popups.remove(this);
21533 * Card header - holder for the card header elements.
21538 * @class Roo.bootstrap.PopoverNav
21539 * @extends Roo.bootstrap.NavGroup
21540 * Bootstrap Popover header navigation class
21542 * Create a new Popover Header Navigation
21543 * @param {Object} config The config object
21546 Roo.bootstrap.PopoverNav = function(config){
21547 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21550 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21553 container_method : 'getPopoverHeader'
21571 * @class Roo.bootstrap.Progress
21572 * @extends Roo.bootstrap.Component
21573 * Bootstrap Progress class
21574 * @cfg {Boolean} striped striped of the progress bar
21575 * @cfg {Boolean} active animated of the progress bar
21579 * Create a new Progress
21580 * @param {Object} config The config object
21583 Roo.bootstrap.Progress = function(config){
21584 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21587 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21592 getAutoCreate : function(){
21600 cfg.cls += ' progress-striped';
21604 cfg.cls += ' active';
21623 * @class Roo.bootstrap.ProgressBar
21624 * @extends Roo.bootstrap.Component
21625 * Bootstrap ProgressBar class
21626 * @cfg {Number} aria_valuenow aria-value now
21627 * @cfg {Number} aria_valuemin aria-value min
21628 * @cfg {Number} aria_valuemax aria-value max
21629 * @cfg {String} label label for the progress bar
21630 * @cfg {String} panel (success | info | warning | danger )
21631 * @cfg {String} role role of the progress bar
21632 * @cfg {String} sr_only text
21636 * Create a new ProgressBar
21637 * @param {Object} config The config object
21640 Roo.bootstrap.ProgressBar = function(config){
21641 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21644 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21648 aria_valuemax : 100,
21654 getAutoCreate : function()
21659 cls: 'progress-bar',
21660 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21672 cfg.role = this.role;
21675 if(this.aria_valuenow){
21676 cfg['aria-valuenow'] = this.aria_valuenow;
21679 if(this.aria_valuemin){
21680 cfg['aria-valuemin'] = this.aria_valuemin;
21683 if(this.aria_valuemax){
21684 cfg['aria-valuemax'] = this.aria_valuemax;
21687 if(this.label && !this.sr_only){
21688 cfg.html = this.label;
21692 cfg.cls += ' progress-bar-' + this.panel;
21698 update : function(aria_valuenow)
21700 this.aria_valuenow = aria_valuenow;
21702 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21717 * @class Roo.bootstrap.TabGroup
21718 * @extends Roo.bootstrap.Column
21719 * Bootstrap Column class
21720 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21721 * @cfg {Boolean} carousel true to make the group behave like a carousel
21722 * @cfg {Boolean} bullets show bullets for the panels
21723 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21724 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21725 * @cfg {Boolean} showarrow (true|false) show arrow default true
21728 * Create a new TabGroup
21729 * @param {Object} config The config object
21732 Roo.bootstrap.TabGroup = function(config){
21733 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21735 this.navId = Roo.id();
21738 Roo.bootstrap.TabGroup.register(this);
21742 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21745 transition : false,
21750 slideOnTouch : false,
21753 getAutoCreate : function()
21755 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21757 cfg.cls += ' tab-content';
21759 if (this.carousel) {
21760 cfg.cls += ' carousel slide';
21763 cls : 'carousel-inner',
21767 if(this.bullets && !Roo.isTouch){
21770 cls : 'carousel-bullets',
21774 if(this.bullets_cls){
21775 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21782 cfg.cn[0].cn.push(bullets);
21785 if(this.showarrow){
21786 cfg.cn[0].cn.push({
21788 class : 'carousel-arrow',
21792 class : 'carousel-prev',
21796 class : 'fa fa-chevron-left'
21802 class : 'carousel-next',
21806 class : 'fa fa-chevron-right'
21819 initEvents: function()
21821 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21822 // this.el.on("touchstart", this.onTouchStart, this);
21825 if(this.autoslide){
21828 this.slideFn = window.setInterval(function() {
21829 _this.showPanelNext();
21833 if(this.showarrow){
21834 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21835 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21841 // onTouchStart : function(e, el, o)
21843 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21847 // this.showPanelNext();
21851 getChildContainer : function()
21853 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21857 * register a Navigation item
21858 * @param {Roo.bootstrap.NavItem} the navitem to add
21860 register : function(item)
21862 this.tabs.push( item);
21863 item.navId = this.navId; // not really needed..
21868 getActivePanel : function()
21871 Roo.each(this.tabs, function(t) {
21881 getPanelByName : function(n)
21884 Roo.each(this.tabs, function(t) {
21885 if (t.tabId == n) {
21893 indexOfPanel : function(p)
21896 Roo.each(this.tabs, function(t,i) {
21897 if (t.tabId == p.tabId) {
21906 * show a specific panel
21907 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21908 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21910 showPanel : function (pan)
21912 if(this.transition || typeof(pan) == 'undefined'){
21913 Roo.log("waiting for the transitionend");
21917 if (typeof(pan) == 'number') {
21918 pan = this.tabs[pan];
21921 if (typeof(pan) == 'string') {
21922 pan = this.getPanelByName(pan);
21925 var cur = this.getActivePanel();
21928 Roo.log('pan or acitve pan is undefined');
21932 if (pan.tabId == this.getActivePanel().tabId) {
21936 if (false === cur.fireEvent('beforedeactivate')) {
21940 if(this.bullets > 0 && !Roo.isTouch){
21941 this.setActiveBullet(this.indexOfPanel(pan));
21944 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21946 //class="carousel-item carousel-item-next carousel-item-left"
21948 this.transition = true;
21949 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21950 var lr = dir == 'next' ? 'left' : 'right';
21951 pan.el.addClass(dir); // or prev
21952 pan.el.addClass('carousel-item-' + dir); // or prev
21953 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21954 cur.el.addClass(lr); // or right
21955 pan.el.addClass(lr);
21956 cur.el.addClass('carousel-item-' +lr); // or right
21957 pan.el.addClass('carousel-item-' +lr);
21961 cur.el.on('transitionend', function() {
21962 Roo.log("trans end?");
21964 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21965 pan.setActive(true);
21967 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21968 cur.setActive(false);
21970 _this.transition = false;
21972 }, this, { single: true } );
21977 cur.setActive(false);
21978 pan.setActive(true);
21983 showPanelNext : function()
21985 var i = this.indexOfPanel(this.getActivePanel());
21987 if (i >= this.tabs.length - 1 && !this.autoslide) {
21991 if (i >= this.tabs.length - 1 && this.autoslide) {
21995 this.showPanel(this.tabs[i+1]);
21998 showPanelPrev : function()
22000 var i = this.indexOfPanel(this.getActivePanel());
22002 if (i < 1 && !this.autoslide) {
22006 if (i < 1 && this.autoslide) {
22007 i = this.tabs.length;
22010 this.showPanel(this.tabs[i-1]);
22014 addBullet: function()
22016 if(!this.bullets || Roo.isTouch){
22019 var ctr = this.el.select('.carousel-bullets',true).first();
22020 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22021 var bullet = ctr.createChild({
22022 cls : 'bullet bullet-' + i
22023 },ctr.dom.lastChild);
22028 bullet.on('click', (function(e, el, o, ii, t){
22030 e.preventDefault();
22032 this.showPanel(ii);
22034 if(this.autoslide && this.slideFn){
22035 clearInterval(this.slideFn);
22036 this.slideFn = window.setInterval(function() {
22037 _this.showPanelNext();
22041 }).createDelegate(this, [i, bullet], true));
22046 setActiveBullet : function(i)
22052 Roo.each(this.el.select('.bullet', true).elements, function(el){
22053 el.removeClass('selected');
22056 var bullet = this.el.select('.bullet-' + i, true).first();
22062 bullet.addClass('selected');
22073 Roo.apply(Roo.bootstrap.TabGroup, {
22077 * register a Navigation Group
22078 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22080 register : function(navgrp)
22082 this.groups[navgrp.navId] = navgrp;
22086 * fetch a Navigation Group based on the navigation ID
22087 * if one does not exist , it will get created.
22088 * @param {string} the navgroup to add
22089 * @returns {Roo.bootstrap.NavGroup} the navgroup
22091 get: function(navId) {
22092 if (typeof(this.groups[navId]) == 'undefined') {
22093 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22095 return this.groups[navId] ;
22110 * @class Roo.bootstrap.TabPanel
22111 * @extends Roo.bootstrap.Component
22112 * Bootstrap TabPanel class
22113 * @cfg {Boolean} active panel active
22114 * @cfg {String} html panel content
22115 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22116 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22117 * @cfg {String} href click to link..
22118 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22122 * Create a new TabPanel
22123 * @param {Object} config The config object
22126 Roo.bootstrap.TabPanel = function(config){
22127 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22131 * Fires when the active status changes
22132 * @param {Roo.bootstrap.TabPanel} this
22133 * @param {Boolean} state the new state
22138 * @event beforedeactivate
22139 * Fires before a tab is de-activated - can be used to do validation on a form.
22140 * @param {Roo.bootstrap.TabPanel} this
22141 * @return {Boolean} false if there is an error
22144 'beforedeactivate': true
22147 this.tabId = this.tabId || Roo.id();
22151 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22158 touchSlide : false,
22159 getAutoCreate : function(){
22164 // item is needed for carousel - not sure if it has any effect otherwise
22165 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22166 html: this.html || ''
22170 cfg.cls += ' active';
22174 cfg.tabId = this.tabId;
22182 initEvents: function()
22184 var p = this.parent();
22186 this.navId = this.navId || p.navId;
22188 if (typeof(this.navId) != 'undefined') {
22189 // not really needed.. but just in case.. parent should be a NavGroup.
22190 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22194 var i = tg.tabs.length - 1;
22196 if(this.active && tg.bullets > 0 && i < tg.bullets){
22197 tg.setActiveBullet(i);
22201 this.el.on('click', this.onClick, this);
22203 if(Roo.isTouch && this.touchSlide){
22204 this.el.on("touchstart", this.onTouchStart, this);
22205 this.el.on("touchmove", this.onTouchMove, this);
22206 this.el.on("touchend", this.onTouchEnd, this);
22211 onRender : function(ct, position)
22213 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22216 setActive : function(state)
22218 Roo.log("panel - set active " + this.tabId + "=" + state);
22220 this.active = state;
22222 this.el.removeClass('active');
22224 } else if (!this.el.hasClass('active')) {
22225 this.el.addClass('active');
22228 this.fireEvent('changed', this, state);
22231 onClick : function(e)
22233 e.preventDefault();
22235 if(!this.href.length){
22239 window.location.href = this.href;
22248 onTouchStart : function(e)
22250 this.swiping = false;
22252 this.startX = e.browserEvent.touches[0].clientX;
22253 this.startY = e.browserEvent.touches[0].clientY;
22256 onTouchMove : function(e)
22258 this.swiping = true;
22260 this.endX = e.browserEvent.touches[0].clientX;
22261 this.endY = e.browserEvent.touches[0].clientY;
22264 onTouchEnd : function(e)
22271 var tabGroup = this.parent();
22273 if(this.endX > this.startX){ // swiping right
22274 tabGroup.showPanelPrev();
22278 if(this.startX > this.endX){ // swiping left
22279 tabGroup.showPanelNext();
22298 * @class Roo.bootstrap.DateField
22299 * @extends Roo.bootstrap.Input
22300 * Bootstrap DateField class
22301 * @cfg {Number} weekStart default 0
22302 * @cfg {String} viewMode default empty, (months|years)
22303 * @cfg {String} minViewMode default empty, (months|years)
22304 * @cfg {Number} startDate default -Infinity
22305 * @cfg {Number} endDate default Infinity
22306 * @cfg {Boolean} todayHighlight default false
22307 * @cfg {Boolean} todayBtn default false
22308 * @cfg {Boolean} calendarWeeks default false
22309 * @cfg {Object} daysOfWeekDisabled default empty
22310 * @cfg {Boolean} singleMode default false (true | false)
22312 * @cfg {Boolean} keyboardNavigation default true
22313 * @cfg {String} language default en
22316 * Create a new DateField
22317 * @param {Object} config The config object
22320 Roo.bootstrap.DateField = function(config){
22321 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22325 * Fires when this field show.
22326 * @param {Roo.bootstrap.DateField} this
22327 * @param {Mixed} date The date value
22332 * Fires when this field hide.
22333 * @param {Roo.bootstrap.DateField} this
22334 * @param {Mixed} date The date value
22339 * Fires when select a date.
22340 * @param {Roo.bootstrap.DateField} this
22341 * @param {Mixed} date The date value
22345 * @event beforeselect
22346 * Fires when before select a date.
22347 * @param {Roo.bootstrap.DateField} this
22348 * @param {Mixed} date The date value
22350 beforeselect : true
22354 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22357 * @cfg {String} format
22358 * The default date format string which can be overriden for localization support. The format must be
22359 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22363 * @cfg {String} altFormats
22364 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22365 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22367 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22375 todayHighlight : false,
22381 keyboardNavigation: true,
22383 calendarWeeks: false,
22385 startDate: -Infinity,
22389 daysOfWeekDisabled: [],
22393 singleMode : false,
22395 UTCDate: function()
22397 return new Date(Date.UTC.apply(Date, arguments));
22400 UTCToday: function()
22402 var today = new Date();
22403 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22406 getDate: function() {
22407 var d = this.getUTCDate();
22408 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22411 getUTCDate: function() {
22415 setDate: function(d) {
22416 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22419 setUTCDate: function(d) {
22421 this.setValue(this.formatDate(this.date));
22424 onRender: function(ct, position)
22427 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22429 this.language = this.language || 'en';
22430 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22431 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22433 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22434 this.format = this.format || 'm/d/y';
22435 this.isInline = false;
22436 this.isInput = true;
22437 this.component = this.el.select('.add-on', true).first() || false;
22438 this.component = (this.component && this.component.length === 0) ? false : this.component;
22439 this.hasInput = this.component && this.inputEl().length;
22441 if (typeof(this.minViewMode === 'string')) {
22442 switch (this.minViewMode) {
22444 this.minViewMode = 1;
22447 this.minViewMode = 2;
22450 this.minViewMode = 0;
22455 if (typeof(this.viewMode === 'string')) {
22456 switch (this.viewMode) {
22469 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22471 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22473 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22475 this.picker().on('mousedown', this.onMousedown, this);
22476 this.picker().on('click', this.onClick, this);
22478 this.picker().addClass('datepicker-dropdown');
22480 this.startViewMode = this.viewMode;
22482 if(this.singleMode){
22483 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22484 v.setVisibilityMode(Roo.Element.DISPLAY);
22488 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22489 v.setStyle('width', '189px');
22493 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22494 if(!this.calendarWeeks){
22499 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22500 v.attr('colspan', function(i, val){
22501 return parseInt(val) + 1;
22506 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22508 this.setStartDate(this.startDate);
22509 this.setEndDate(this.endDate);
22511 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22518 if(this.isInline) {
22523 picker : function()
22525 return this.pickerEl;
22526 // return this.el.select('.datepicker', true).first();
22529 fillDow: function()
22531 var dowCnt = this.weekStart;
22540 if(this.calendarWeeks){
22548 while (dowCnt < this.weekStart + 7) {
22552 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22556 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22559 fillMonths: function()
22562 var months = this.picker().select('>.datepicker-months td', true).first();
22564 months.dom.innerHTML = '';
22570 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22573 months.createChild(month);
22580 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;
22582 if (this.date < this.startDate) {
22583 this.viewDate = new Date(this.startDate);
22584 } else if (this.date > this.endDate) {
22585 this.viewDate = new Date(this.endDate);
22587 this.viewDate = new Date(this.date);
22595 var d = new Date(this.viewDate),
22596 year = d.getUTCFullYear(),
22597 month = d.getUTCMonth(),
22598 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22599 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22600 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22601 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22602 currentDate = this.date && this.date.valueOf(),
22603 today = this.UTCToday();
22605 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22607 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22609 // this.picker.select('>tfoot th.today').
22610 // .text(dates[this.language].today)
22611 // .toggle(this.todayBtn !== false);
22613 this.updateNavArrows();
22616 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22618 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22620 prevMonth.setUTCDate(day);
22622 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22624 var nextMonth = new Date(prevMonth);
22626 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22628 nextMonth = nextMonth.valueOf();
22630 var fillMonths = false;
22632 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22634 while(prevMonth.valueOf() <= nextMonth) {
22637 if (prevMonth.getUTCDay() === this.weekStart) {
22639 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22647 if(this.calendarWeeks){
22648 // ISO 8601: First week contains first thursday.
22649 // ISO also states week starts on Monday, but we can be more abstract here.
22651 // Start of current week: based on weekstart/current date
22652 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22653 // Thursday of this week
22654 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22655 // First Thursday of year, year from thursday
22656 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22657 // Calendar week: ms between thursdays, div ms per day, div 7 days
22658 calWeek = (th - yth) / 864e5 / 7 + 1;
22660 fillMonths.cn.push({
22668 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22670 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22673 if (this.todayHighlight &&
22674 prevMonth.getUTCFullYear() == today.getFullYear() &&
22675 prevMonth.getUTCMonth() == today.getMonth() &&
22676 prevMonth.getUTCDate() == today.getDate()) {
22677 clsName += ' today';
22680 if (currentDate && prevMonth.valueOf() === currentDate) {
22681 clsName += ' active';
22684 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22685 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22686 clsName += ' disabled';
22689 fillMonths.cn.push({
22691 cls: 'day ' + clsName,
22692 html: prevMonth.getDate()
22695 prevMonth.setDate(prevMonth.getDate()+1);
22698 var currentYear = this.date && this.date.getUTCFullYear();
22699 var currentMonth = this.date && this.date.getUTCMonth();
22701 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22703 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22704 v.removeClass('active');
22706 if(currentYear === year && k === currentMonth){
22707 v.addClass('active');
22710 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22711 v.addClass('disabled');
22717 year = parseInt(year/10, 10) * 10;
22719 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22721 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22724 for (var i = -1; i < 11; i++) {
22725 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22727 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22735 showMode: function(dir)
22738 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22741 Roo.each(this.picker().select('>div',true).elements, function(v){
22742 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22745 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22750 if(this.isInline) {
22754 this.picker().removeClass(['bottom', 'top']);
22756 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22758 * place to the top of element!
22762 this.picker().addClass('top');
22763 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22768 this.picker().addClass('bottom');
22770 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22773 parseDate : function(value)
22775 if(!value || value instanceof Date){
22778 var v = Date.parseDate(value, this.format);
22779 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22780 v = Date.parseDate(value, 'Y-m-d');
22782 if(!v && this.altFormats){
22783 if(!this.altFormatsArray){
22784 this.altFormatsArray = this.altFormats.split("|");
22786 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22787 v = Date.parseDate(value, this.altFormatsArray[i]);
22793 formatDate : function(date, fmt)
22795 return (!date || !(date instanceof Date)) ?
22796 date : date.dateFormat(fmt || this.format);
22799 onFocus : function()
22801 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22805 onBlur : function()
22807 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22809 var d = this.inputEl().getValue();
22816 showPopup : function()
22818 this.picker().show();
22822 this.fireEvent('showpopup', this, this.date);
22825 hidePopup : function()
22827 if(this.isInline) {
22830 this.picker().hide();
22831 this.viewMode = this.startViewMode;
22834 this.fireEvent('hidepopup', this, this.date);
22838 onMousedown: function(e)
22840 e.stopPropagation();
22841 e.preventDefault();
22846 Roo.bootstrap.DateField.superclass.keyup.call(this);
22850 setValue: function(v)
22852 if(this.fireEvent('beforeselect', this, v) !== false){
22853 var d = new Date(this.parseDate(v) ).clearTime();
22855 if(isNaN(d.getTime())){
22856 this.date = this.viewDate = '';
22857 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22861 v = this.formatDate(d);
22863 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22865 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22869 this.fireEvent('select', this, this.date);
22873 getValue: function()
22875 return this.formatDate(this.date);
22878 fireKey: function(e)
22880 if (!this.picker().isVisible()){
22881 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22887 var dateChanged = false,
22889 newDate, newViewDate;
22894 e.preventDefault();
22898 if (!this.keyboardNavigation) {
22901 dir = e.keyCode == 37 ? -1 : 1;
22904 newDate = this.moveYear(this.date, dir);
22905 newViewDate = this.moveYear(this.viewDate, dir);
22906 } else if (e.shiftKey){
22907 newDate = this.moveMonth(this.date, dir);
22908 newViewDate = this.moveMonth(this.viewDate, dir);
22910 newDate = new Date(this.date);
22911 newDate.setUTCDate(this.date.getUTCDate() + dir);
22912 newViewDate = new Date(this.viewDate);
22913 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22915 if (this.dateWithinRange(newDate)){
22916 this.date = newDate;
22917 this.viewDate = newViewDate;
22918 this.setValue(this.formatDate(this.date));
22920 e.preventDefault();
22921 dateChanged = true;
22926 if (!this.keyboardNavigation) {
22929 dir = e.keyCode == 38 ? -1 : 1;
22931 newDate = this.moveYear(this.date, dir);
22932 newViewDate = this.moveYear(this.viewDate, dir);
22933 } else if (e.shiftKey){
22934 newDate = this.moveMonth(this.date, dir);
22935 newViewDate = this.moveMonth(this.viewDate, dir);
22937 newDate = new Date(this.date);
22938 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22939 newViewDate = new Date(this.viewDate);
22940 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22942 if (this.dateWithinRange(newDate)){
22943 this.date = newDate;
22944 this.viewDate = newViewDate;
22945 this.setValue(this.formatDate(this.date));
22947 e.preventDefault();
22948 dateChanged = true;
22952 this.setValue(this.formatDate(this.date));
22954 e.preventDefault();
22957 this.setValue(this.formatDate(this.date));
22971 onClick: function(e)
22973 e.stopPropagation();
22974 e.preventDefault();
22976 var target = e.getTarget();
22978 if(target.nodeName.toLowerCase() === 'i'){
22979 target = Roo.get(target).dom.parentNode;
22982 var nodeName = target.nodeName;
22983 var className = target.className;
22984 var html = target.innerHTML;
22985 //Roo.log(nodeName);
22987 switch(nodeName.toLowerCase()) {
22989 switch(className) {
22995 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22996 switch(this.viewMode){
22998 this.viewDate = this.moveMonth(this.viewDate, dir);
23002 this.viewDate = this.moveYear(this.viewDate, dir);
23008 var date = new Date();
23009 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23011 this.setValue(this.formatDate(this.date));
23018 if (className.indexOf('disabled') < 0) {
23019 if (!this.viewDate) {
23020 this.viewDate = new Date();
23022 this.viewDate.setUTCDate(1);
23023 if (className.indexOf('month') > -1) {
23024 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23026 var year = parseInt(html, 10) || 0;
23027 this.viewDate.setUTCFullYear(year);
23031 if(this.singleMode){
23032 this.setValue(this.formatDate(this.viewDate));
23043 //Roo.log(className);
23044 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23045 var day = parseInt(html, 10) || 1;
23046 var year = (this.viewDate || new Date()).getUTCFullYear(),
23047 month = (this.viewDate || new Date()).getUTCMonth();
23049 if (className.indexOf('old') > -1) {
23056 } else if (className.indexOf('new') > -1) {
23064 //Roo.log([year,month,day]);
23065 this.date = this.UTCDate(year, month, day,0,0,0,0);
23066 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23068 //Roo.log(this.formatDate(this.date));
23069 this.setValue(this.formatDate(this.date));
23076 setStartDate: function(startDate)
23078 this.startDate = startDate || -Infinity;
23079 if (this.startDate !== -Infinity) {
23080 this.startDate = this.parseDate(this.startDate);
23083 this.updateNavArrows();
23086 setEndDate: function(endDate)
23088 this.endDate = endDate || Infinity;
23089 if (this.endDate !== Infinity) {
23090 this.endDate = this.parseDate(this.endDate);
23093 this.updateNavArrows();
23096 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23098 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23099 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23100 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23102 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23103 return parseInt(d, 10);
23106 this.updateNavArrows();
23109 updateNavArrows: function()
23111 if(this.singleMode){
23115 var d = new Date(this.viewDate),
23116 year = d.getUTCFullYear(),
23117 month = d.getUTCMonth();
23119 Roo.each(this.picker().select('.prev', true).elements, function(v){
23121 switch (this.viewMode) {
23124 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23130 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23137 Roo.each(this.picker().select('.next', true).elements, function(v){
23139 switch (this.viewMode) {
23142 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23148 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23156 moveMonth: function(date, dir)
23161 var new_date = new Date(date.valueOf()),
23162 day = new_date.getUTCDate(),
23163 month = new_date.getUTCMonth(),
23164 mag = Math.abs(dir),
23166 dir = dir > 0 ? 1 : -1;
23169 // If going back one month, make sure month is not current month
23170 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23172 return new_date.getUTCMonth() == month;
23174 // If going forward one month, make sure month is as expected
23175 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23177 return new_date.getUTCMonth() != new_month;
23179 new_month = month + dir;
23180 new_date.setUTCMonth(new_month);
23181 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23182 if (new_month < 0 || new_month > 11) {
23183 new_month = (new_month + 12) % 12;
23186 // For magnitudes >1, move one month at a time...
23187 for (var i=0; i<mag; i++) {
23188 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23189 new_date = this.moveMonth(new_date, dir);
23191 // ...then reset the day, keeping it in the new month
23192 new_month = new_date.getUTCMonth();
23193 new_date.setUTCDate(day);
23195 return new_month != new_date.getUTCMonth();
23198 // Common date-resetting loop -- if date is beyond end of month, make it
23201 new_date.setUTCDate(--day);
23202 new_date.setUTCMonth(new_month);
23207 moveYear: function(date, dir)
23209 return this.moveMonth(date, dir*12);
23212 dateWithinRange: function(date)
23214 return date >= this.startDate && date <= this.endDate;
23220 this.picker().remove();
23223 validateValue : function(value)
23225 if(this.getVisibilityEl().hasClass('hidden')){
23229 if(value.length < 1) {
23230 if(this.allowBlank){
23236 if(value.length < this.minLength){
23239 if(value.length > this.maxLength){
23243 var vt = Roo.form.VTypes;
23244 if(!vt[this.vtype](value, this)){
23248 if(typeof this.validator == "function"){
23249 var msg = this.validator(value);
23255 if(this.regex && !this.regex.test(value)){
23259 if(typeof(this.parseDate(value)) == 'undefined'){
23263 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23267 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23277 this.date = this.viewDate = '';
23279 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23284 Roo.apply(Roo.bootstrap.DateField, {
23295 html: '<i class="fa fa-arrow-left"/>'
23305 html: '<i class="fa fa-arrow-right"/>'
23347 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23348 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23349 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23350 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23351 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23364 navFnc: 'FullYear',
23369 navFnc: 'FullYear',
23374 Roo.apply(Roo.bootstrap.DateField, {
23378 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23382 cls: 'datepicker-days',
23386 cls: 'table-condensed',
23388 Roo.bootstrap.DateField.head,
23392 Roo.bootstrap.DateField.footer
23399 cls: 'datepicker-months',
23403 cls: 'table-condensed',
23405 Roo.bootstrap.DateField.head,
23406 Roo.bootstrap.DateField.content,
23407 Roo.bootstrap.DateField.footer
23414 cls: 'datepicker-years',
23418 cls: 'table-condensed',
23420 Roo.bootstrap.DateField.head,
23421 Roo.bootstrap.DateField.content,
23422 Roo.bootstrap.DateField.footer
23441 * @class Roo.bootstrap.TimeField
23442 * @extends Roo.bootstrap.Input
23443 * Bootstrap DateField class
23447 * Create a new TimeField
23448 * @param {Object} config The config object
23451 Roo.bootstrap.TimeField = function(config){
23452 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23456 * Fires when this field show.
23457 * @param {Roo.bootstrap.DateField} thisthis
23458 * @param {Mixed} date The date value
23463 * Fires when this field hide.
23464 * @param {Roo.bootstrap.DateField} this
23465 * @param {Mixed} date The date value
23470 * Fires when select a date.
23471 * @param {Roo.bootstrap.DateField} this
23472 * @param {Mixed} date The date value
23478 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23481 * @cfg {String} format
23482 * The default time format string which can be overriden for localization support. The format must be
23483 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23487 getAutoCreate : function()
23489 this.after = '<i class="fa far fa-clock"></i>';
23490 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23494 onRender: function(ct, position)
23497 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23499 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23501 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23503 this.pop = this.picker().select('>.datepicker-time',true).first();
23504 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23506 this.picker().on('mousedown', this.onMousedown, this);
23507 this.picker().on('click', this.onClick, this);
23509 this.picker().addClass('datepicker-dropdown');
23514 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23515 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23516 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23517 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23518 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23519 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23523 fireKey: function(e){
23524 if (!this.picker().isVisible()){
23525 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23531 e.preventDefault();
23539 this.onTogglePeriod();
23542 this.onIncrementMinutes();
23545 this.onDecrementMinutes();
23554 onClick: function(e) {
23555 e.stopPropagation();
23556 e.preventDefault();
23559 picker : function()
23561 return this.pickerEl;
23564 fillTime: function()
23566 var time = this.pop.select('tbody', true).first();
23568 time.dom.innerHTML = '';
23583 cls: 'hours-up fa fas fa-chevron-up'
23603 cls: 'minutes-up fa fas fa-chevron-up'
23624 cls: 'timepicker-hour',
23639 cls: 'timepicker-minute',
23654 cls: 'btn btn-primary period',
23676 cls: 'hours-down fa fas fa-chevron-down'
23696 cls: 'minutes-down fa fas fa-chevron-down'
23714 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23721 var hours = this.time.getHours();
23722 var minutes = this.time.getMinutes();
23735 hours = hours - 12;
23739 hours = '0' + hours;
23743 minutes = '0' + minutes;
23746 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23747 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23748 this.pop.select('button', true).first().dom.innerHTML = period;
23754 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23756 var cls = ['bottom'];
23758 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23765 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23769 //this.picker().setXY(20000,20000);
23770 this.picker().addClass(cls.join('-'));
23774 Roo.each(cls, function(c){
23779 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23780 //_this.picker().setTop(_this.inputEl().getHeight());
23784 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23786 //_this.picker().setTop(0 - _this.picker().getHeight());
23791 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23795 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23803 onFocus : function()
23805 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23809 onBlur : function()
23811 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23817 this.picker().show();
23822 this.fireEvent('show', this, this.date);
23827 this.picker().hide();
23830 this.fireEvent('hide', this, this.date);
23833 setTime : function()
23836 this.setValue(this.time.format(this.format));
23838 this.fireEvent('select', this, this.date);
23843 onMousedown: function(e){
23844 e.stopPropagation();
23845 e.preventDefault();
23848 onIncrementHours: function()
23850 Roo.log('onIncrementHours');
23851 this.time = this.time.add(Date.HOUR, 1);
23856 onDecrementHours: function()
23858 Roo.log('onDecrementHours');
23859 this.time = this.time.add(Date.HOUR, -1);
23863 onIncrementMinutes: function()
23865 Roo.log('onIncrementMinutes');
23866 this.time = this.time.add(Date.MINUTE, 1);
23870 onDecrementMinutes: function()
23872 Roo.log('onDecrementMinutes');
23873 this.time = this.time.add(Date.MINUTE, -1);
23877 onTogglePeriod: function()
23879 Roo.log('onTogglePeriod');
23880 this.time = this.time.add(Date.HOUR, 12);
23888 Roo.apply(Roo.bootstrap.TimeField, {
23892 cls: 'datepicker dropdown-menu',
23896 cls: 'datepicker-time',
23900 cls: 'table-condensed',
23929 cls: 'btn btn-info ok',
23957 * @class Roo.bootstrap.MonthField
23958 * @extends Roo.bootstrap.Input
23959 * Bootstrap MonthField class
23961 * @cfg {String} language default en
23964 * Create a new MonthField
23965 * @param {Object} config The config object
23968 Roo.bootstrap.MonthField = function(config){
23969 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23974 * Fires when this field show.
23975 * @param {Roo.bootstrap.MonthField} this
23976 * @param {Mixed} date The date value
23981 * Fires when this field hide.
23982 * @param {Roo.bootstrap.MonthField} this
23983 * @param {Mixed} date The date value
23988 * Fires when select a date.
23989 * @param {Roo.bootstrap.MonthField} this
23990 * @param {String} oldvalue The old value
23991 * @param {String} newvalue The new value
23997 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23999 onRender: function(ct, position)
24002 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24004 this.language = this.language || 'en';
24005 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24006 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24008 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24009 this.isInline = false;
24010 this.isInput = true;
24011 this.component = this.el.select('.add-on', true).first() || false;
24012 this.component = (this.component && this.component.length === 0) ? false : this.component;
24013 this.hasInput = this.component && this.inputEL().length;
24015 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24017 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24019 this.picker().on('mousedown', this.onMousedown, this);
24020 this.picker().on('click', this.onClick, this);
24022 this.picker().addClass('datepicker-dropdown');
24024 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24025 v.setStyle('width', '189px');
24032 if(this.isInline) {
24038 setValue: function(v, suppressEvent)
24040 var o = this.getValue();
24042 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24046 if(suppressEvent !== true){
24047 this.fireEvent('select', this, o, v);
24052 getValue: function()
24057 onClick: function(e)
24059 e.stopPropagation();
24060 e.preventDefault();
24062 var target = e.getTarget();
24064 if(target.nodeName.toLowerCase() === 'i'){
24065 target = Roo.get(target).dom.parentNode;
24068 var nodeName = target.nodeName;
24069 var className = target.className;
24070 var html = target.innerHTML;
24072 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24076 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24078 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24084 picker : function()
24086 return this.pickerEl;
24089 fillMonths: function()
24092 var months = this.picker().select('>.datepicker-months td', true).first();
24094 months.dom.innerHTML = '';
24100 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24103 months.createChild(month);
24112 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24113 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24116 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24117 e.removeClass('active');
24119 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24120 e.addClass('active');
24127 if(this.isInline) {
24131 this.picker().removeClass(['bottom', 'top']);
24133 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24135 * place to the top of element!
24139 this.picker().addClass('top');
24140 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24145 this.picker().addClass('bottom');
24147 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24150 onFocus : function()
24152 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24156 onBlur : function()
24158 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24160 var d = this.inputEl().getValue();
24169 this.picker().show();
24170 this.picker().select('>.datepicker-months', true).first().show();
24174 this.fireEvent('show', this, this.date);
24179 if(this.isInline) {
24182 this.picker().hide();
24183 this.fireEvent('hide', this, this.date);
24187 onMousedown: function(e)
24189 e.stopPropagation();
24190 e.preventDefault();
24195 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24199 fireKey: function(e)
24201 if (!this.picker().isVisible()){
24202 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24213 e.preventDefault();
24217 dir = e.keyCode == 37 ? -1 : 1;
24219 this.vIndex = this.vIndex + dir;
24221 if(this.vIndex < 0){
24225 if(this.vIndex > 11){
24229 if(isNaN(this.vIndex)){
24233 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24239 dir = e.keyCode == 38 ? -1 : 1;
24241 this.vIndex = this.vIndex + dir * 4;
24243 if(this.vIndex < 0){
24247 if(this.vIndex > 11){
24251 if(isNaN(this.vIndex)){
24255 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24260 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24261 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24265 e.preventDefault();
24268 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24269 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24285 this.picker().remove();
24290 Roo.apply(Roo.bootstrap.MonthField, {
24309 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24310 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24315 Roo.apply(Roo.bootstrap.MonthField, {
24319 cls: 'datepicker dropdown-menu roo-dynamic',
24323 cls: 'datepicker-months',
24327 cls: 'table-condensed',
24329 Roo.bootstrap.DateField.content
24349 * @class Roo.bootstrap.CheckBox
24350 * @extends Roo.bootstrap.Input
24351 * Bootstrap CheckBox class
24353 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24354 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24355 * @cfg {String} boxLabel The text that appears beside the checkbox
24356 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24357 * @cfg {Boolean} checked initnal the element
24358 * @cfg {Boolean} inline inline the element (default false)
24359 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24360 * @cfg {String} tooltip label tooltip
24363 * Create a new CheckBox
24364 * @param {Object} config The config object
24367 Roo.bootstrap.CheckBox = function(config){
24368 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24373 * Fires when the element is checked or unchecked.
24374 * @param {Roo.bootstrap.CheckBox} this This input
24375 * @param {Boolean} checked The new checked value
24380 * Fires when the element is click.
24381 * @param {Roo.bootstrap.CheckBox} this This input
24388 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24390 inputType: 'checkbox',
24399 // checkbox success does not make any sense really..
24404 getAutoCreate : function()
24406 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24412 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24415 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24421 type : this.inputType,
24422 value : this.inputValue,
24423 cls : 'roo-' + this.inputType, //'form-box',
24424 placeholder : this.placeholder || ''
24428 if(this.inputType != 'radio'){
24432 cls : 'roo-hidden-value',
24433 value : this.checked ? this.inputValue : this.valueOff
24438 if (this.weight) { // Validity check?
24439 cfg.cls += " " + this.inputType + "-" + this.weight;
24442 if (this.disabled) {
24443 input.disabled=true;
24447 input.checked = this.checked;
24452 input.name = this.name;
24454 if(this.inputType != 'radio'){
24455 hidden.name = this.name;
24456 input.name = '_hidden_' + this.name;
24461 input.cls += ' input-' + this.size;
24466 ['xs','sm','md','lg'].map(function(size){
24467 if (settings[size]) {
24468 cfg.cls += ' col-' + size + '-' + settings[size];
24472 var inputblock = input;
24474 if (this.before || this.after) {
24477 cls : 'input-group',
24482 inputblock.cn.push({
24484 cls : 'input-group-addon',
24489 inputblock.cn.push(input);
24491 if(this.inputType != 'radio'){
24492 inputblock.cn.push(hidden);
24496 inputblock.cn.push({
24498 cls : 'input-group-addon',
24504 var boxLabelCfg = false;
24510 //'for': id, // box label is handled by onclick - so no for...
24512 html: this.boxLabel
24515 boxLabelCfg.tooltip = this.tooltip;
24521 if (align ==='left' && this.fieldLabel.length) {
24522 // Roo.log("left and has label");
24527 cls : 'control-label',
24528 html : this.fieldLabel
24539 cfg.cn[1].cn.push(boxLabelCfg);
24542 if(this.labelWidth > 12){
24543 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24546 if(this.labelWidth < 13 && this.labelmd == 0){
24547 this.labelmd = this.labelWidth;
24550 if(this.labellg > 0){
24551 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24552 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24555 if(this.labelmd > 0){
24556 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24557 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24560 if(this.labelsm > 0){
24561 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24562 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24565 if(this.labelxs > 0){
24566 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24567 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24570 } else if ( this.fieldLabel.length) {
24571 // Roo.log(" label");
24575 tag: this.boxLabel ? 'span' : 'label',
24577 cls: 'control-label box-input-label',
24578 //cls : 'input-group-addon',
24579 html : this.fieldLabel
24586 cfg.cn.push(boxLabelCfg);
24591 // Roo.log(" no label && no align");
24592 cfg.cn = [ inputblock ] ;
24594 cfg.cn.push(boxLabelCfg);
24602 if(this.inputType != 'radio'){
24603 cfg.cn.push(hidden);
24611 * return the real input element.
24613 inputEl: function ()
24615 return this.el.select('input.roo-' + this.inputType,true).first();
24617 hiddenEl: function ()
24619 return this.el.select('input.roo-hidden-value',true).first();
24622 labelEl: function()
24624 return this.el.select('label.control-label',true).first();
24626 /* depricated... */
24630 return this.labelEl();
24633 boxLabelEl: function()
24635 return this.el.select('label.box-label',true).first();
24638 initEvents : function()
24640 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24642 this.inputEl().on('click', this.onClick, this);
24644 if (this.boxLabel) {
24645 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24648 this.startValue = this.getValue();
24651 Roo.bootstrap.CheckBox.register(this);
24655 onClick : function(e)
24657 if(this.fireEvent('click', this, e) !== false){
24658 this.setChecked(!this.checked);
24663 setChecked : function(state,suppressEvent)
24665 this.startValue = this.getValue();
24667 if(this.inputType == 'radio'){
24669 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24670 e.dom.checked = false;
24673 this.inputEl().dom.checked = true;
24675 this.inputEl().dom.value = this.inputValue;
24677 if(suppressEvent !== true){
24678 this.fireEvent('check', this, true);
24686 this.checked = state;
24688 this.inputEl().dom.checked = state;
24691 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24693 if(suppressEvent !== true){
24694 this.fireEvent('check', this, state);
24700 getValue : function()
24702 if(this.inputType == 'radio'){
24703 return this.getGroupValue();
24706 return this.hiddenEl().dom.value;
24710 getGroupValue : function()
24712 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24716 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24719 setValue : function(v,suppressEvent)
24721 if(this.inputType == 'radio'){
24722 this.setGroupValue(v, suppressEvent);
24726 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24731 setGroupValue : function(v, suppressEvent)
24733 this.startValue = this.getValue();
24735 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24736 e.dom.checked = false;
24738 if(e.dom.value == v){
24739 e.dom.checked = true;
24743 if(suppressEvent !== true){
24744 this.fireEvent('check', this, true);
24752 validate : function()
24754 if(this.getVisibilityEl().hasClass('hidden')){
24760 (this.inputType == 'radio' && this.validateRadio()) ||
24761 (this.inputType == 'checkbox' && this.validateCheckbox())
24767 this.markInvalid();
24771 validateRadio : function()
24773 if(this.getVisibilityEl().hasClass('hidden')){
24777 if(this.allowBlank){
24783 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24784 if(!e.dom.checked){
24796 validateCheckbox : function()
24799 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24800 //return (this.getValue() == this.inputValue) ? true : false;
24803 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24811 for(var i in group){
24812 if(group[i].el.isVisible(true)){
24820 for(var i in group){
24825 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24832 * Mark this field as valid
24834 markValid : function()
24838 this.fireEvent('valid', this);
24840 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24843 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24850 if(this.inputType == 'radio'){
24851 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24852 var fg = e.findParent('.form-group', false, true);
24853 if (Roo.bootstrap.version == 3) {
24854 fg.removeClass([_this.invalidClass, _this.validClass]);
24855 fg.addClass(_this.validClass);
24857 fg.removeClass(['is-valid', 'is-invalid']);
24858 fg.addClass('is-valid');
24866 var fg = this.el.findParent('.form-group', false, true);
24867 if (Roo.bootstrap.version == 3) {
24868 fg.removeClass([this.invalidClass, this.validClass]);
24869 fg.addClass(this.validClass);
24871 fg.removeClass(['is-valid', 'is-invalid']);
24872 fg.addClass('is-valid');
24877 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24883 for(var i in group){
24884 var fg = group[i].el.findParent('.form-group', false, true);
24885 if (Roo.bootstrap.version == 3) {
24886 fg.removeClass([this.invalidClass, this.validClass]);
24887 fg.addClass(this.validClass);
24889 fg.removeClass(['is-valid', 'is-invalid']);
24890 fg.addClass('is-valid');
24896 * Mark this field as invalid
24897 * @param {String} msg The validation message
24899 markInvalid : function(msg)
24901 if(this.allowBlank){
24907 this.fireEvent('invalid', this, msg);
24909 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24912 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24916 label.markInvalid();
24919 if(this.inputType == 'radio'){
24921 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24922 var fg = e.findParent('.form-group', false, true);
24923 if (Roo.bootstrap.version == 3) {
24924 fg.removeClass([_this.invalidClass, _this.validClass]);
24925 fg.addClass(_this.invalidClass);
24927 fg.removeClass(['is-invalid', 'is-valid']);
24928 fg.addClass('is-invalid');
24936 var fg = this.el.findParent('.form-group', false, true);
24937 if (Roo.bootstrap.version == 3) {
24938 fg.removeClass([_this.invalidClass, _this.validClass]);
24939 fg.addClass(_this.invalidClass);
24941 fg.removeClass(['is-invalid', 'is-valid']);
24942 fg.addClass('is-invalid');
24947 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24953 for(var i in group){
24954 var fg = group[i].el.findParent('.form-group', false, true);
24955 if (Roo.bootstrap.version == 3) {
24956 fg.removeClass([_this.invalidClass, _this.validClass]);
24957 fg.addClass(_this.invalidClass);
24959 fg.removeClass(['is-invalid', 'is-valid']);
24960 fg.addClass('is-invalid');
24966 clearInvalid : function()
24968 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24970 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24972 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24974 if (label && label.iconEl) {
24975 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24976 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24980 disable : function()
24982 if(this.inputType != 'radio'){
24983 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24990 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24991 _this.getActionEl().addClass(this.disabledClass);
24992 e.dom.disabled = true;
24996 this.disabled = true;
24997 this.fireEvent("disable", this);
25001 enable : function()
25003 if(this.inputType != 'radio'){
25004 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25011 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25012 _this.getActionEl().removeClass(this.disabledClass);
25013 e.dom.disabled = false;
25017 this.disabled = false;
25018 this.fireEvent("enable", this);
25022 setBoxLabel : function(v)
25027 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25033 Roo.apply(Roo.bootstrap.CheckBox, {
25038 * register a CheckBox Group
25039 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25041 register : function(checkbox)
25043 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25044 this.groups[checkbox.groupId] = {};
25047 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25051 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25055 * fetch a CheckBox Group based on the group ID
25056 * @param {string} the group ID
25057 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25059 get: function(groupId) {
25060 if (typeof(this.groups[groupId]) == 'undefined') {
25064 return this.groups[groupId] ;
25077 * @class Roo.bootstrap.Radio
25078 * @extends Roo.bootstrap.Component
25079 * Bootstrap Radio class
25080 * @cfg {String} boxLabel - the label associated
25081 * @cfg {String} value - the value of radio
25084 * Create a new Radio
25085 * @param {Object} config The config object
25087 Roo.bootstrap.Radio = function(config){
25088 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25092 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25098 getAutoCreate : function()
25102 cls : 'form-group radio',
25107 html : this.boxLabel
25115 initEvents : function()
25117 this.parent().register(this);
25119 this.el.on('click', this.onClick, this);
25123 onClick : function(e)
25125 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25126 this.setChecked(true);
25130 setChecked : function(state, suppressEvent)
25132 this.parent().setValue(this.value, suppressEvent);
25136 setBoxLabel : function(v)
25141 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25156 * @class Roo.bootstrap.SecurePass
25157 * @extends Roo.bootstrap.Input
25158 * Bootstrap SecurePass class
25162 * Create a new SecurePass
25163 * @param {Object} config The config object
25166 Roo.bootstrap.SecurePass = function (config) {
25167 // these go here, so the translation tool can replace them..
25169 PwdEmpty: "Please type a password, and then retype it to confirm.",
25170 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25171 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25172 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25173 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25174 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25175 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25176 TooWeak: "Your password is Too Weak."
25178 this.meterLabel = "Password strength:";
25179 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25180 this.meterClass = [
25181 "roo-password-meter-tooweak",
25182 "roo-password-meter-weak",
25183 "roo-password-meter-medium",
25184 "roo-password-meter-strong",
25185 "roo-password-meter-grey"
25190 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25193 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25195 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25197 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25198 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25199 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25200 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25201 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25202 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25203 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25213 * @cfg {String/Object} Label for the strength meter (defaults to
25214 * 'Password strength:')
25219 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25220 * ['Weak', 'Medium', 'Strong'])
25223 pwdStrengths: false,
25236 initEvents: function ()
25238 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25240 if (this.el.is('input[type=password]') && Roo.isSafari) {
25241 this.el.on('keydown', this.SafariOnKeyDown, this);
25244 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25247 onRender: function (ct, position)
25249 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25250 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25251 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25253 this.trigger.createChild({
25258 cls: 'roo-password-meter-grey col-xs-12',
25261 //width: this.meterWidth + 'px'
25265 cls: 'roo-password-meter-text'
25271 if (this.hideTrigger) {
25272 this.trigger.setDisplayed(false);
25274 this.setSize(this.width || '', this.height || '');
25277 onDestroy: function ()
25279 if (this.trigger) {
25280 this.trigger.removeAllListeners();
25281 this.trigger.remove();
25284 this.wrap.remove();
25286 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25289 checkStrength: function ()
25291 var pwd = this.inputEl().getValue();
25292 if (pwd == this._lastPwd) {
25297 if (this.ClientSideStrongPassword(pwd)) {
25299 } else if (this.ClientSideMediumPassword(pwd)) {
25301 } else if (this.ClientSideWeakPassword(pwd)) {
25307 Roo.log('strength1: ' + strength);
25309 //var pm = this.trigger.child('div/div/div').dom;
25310 var pm = this.trigger.child('div/div');
25311 pm.removeClass(this.meterClass);
25312 pm.addClass(this.meterClass[strength]);
25315 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25317 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25319 this._lastPwd = pwd;
25323 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25325 this._lastPwd = '';
25327 var pm = this.trigger.child('div/div');
25328 pm.removeClass(this.meterClass);
25329 pm.addClass('roo-password-meter-grey');
25332 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25335 this.inputEl().dom.type='password';
25338 validateValue: function (value)
25340 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25343 if (value.length == 0) {
25344 if (this.allowBlank) {
25345 this.clearInvalid();
25349 this.markInvalid(this.errors.PwdEmpty);
25350 this.errorMsg = this.errors.PwdEmpty;
25358 if (!value.match(/[\x21-\x7e]+/)) {
25359 this.markInvalid(this.errors.PwdBadChar);
25360 this.errorMsg = this.errors.PwdBadChar;
25363 if (value.length < 6) {
25364 this.markInvalid(this.errors.PwdShort);
25365 this.errorMsg = this.errors.PwdShort;
25368 if (value.length > 16) {
25369 this.markInvalid(this.errors.PwdLong);
25370 this.errorMsg = this.errors.PwdLong;
25374 if (this.ClientSideStrongPassword(value)) {
25376 } else if (this.ClientSideMediumPassword(value)) {
25378 } else if (this.ClientSideWeakPassword(value)) {
25385 if (strength < 2) {
25386 //this.markInvalid(this.errors.TooWeak);
25387 this.errorMsg = this.errors.TooWeak;
25392 console.log('strength2: ' + strength);
25394 //var pm = this.trigger.child('div/div/div').dom;
25396 var pm = this.trigger.child('div/div');
25397 pm.removeClass(this.meterClass);
25398 pm.addClass(this.meterClass[strength]);
25400 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25402 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25404 this.errorMsg = '';
25408 CharacterSetChecks: function (type)
25411 this.fResult = false;
25414 isctype: function (character, type)
25417 case this.kCapitalLetter:
25418 if (character >= 'A' && character <= 'Z') {
25423 case this.kSmallLetter:
25424 if (character >= 'a' && character <= 'z') {
25430 if (character >= '0' && character <= '9') {
25435 case this.kPunctuation:
25436 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25447 IsLongEnough: function (pwd, size)
25449 return !(pwd == null || isNaN(size) || pwd.length < size);
25452 SpansEnoughCharacterSets: function (word, nb)
25454 if (!this.IsLongEnough(word, nb))
25459 var characterSetChecks = new Array(
25460 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25461 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25464 for (var index = 0; index < word.length; ++index) {
25465 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25466 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25467 characterSetChecks[nCharSet].fResult = true;
25474 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25475 if (characterSetChecks[nCharSet].fResult) {
25480 if (nCharSets < nb) {
25486 ClientSideStrongPassword: function (pwd)
25488 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25491 ClientSideMediumPassword: function (pwd)
25493 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25496 ClientSideWeakPassword: function (pwd)
25498 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25501 })//<script type="text/javascript">
25504 * Based Ext JS Library 1.1.1
25505 * Copyright(c) 2006-2007, Ext JS, LLC.
25511 * @class Roo.HtmlEditorCore
25512 * @extends Roo.Component
25513 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25515 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25518 Roo.HtmlEditorCore = function(config){
25521 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25526 * @event initialize
25527 * Fires when the editor is fully initialized (including the iframe)
25528 * @param {Roo.HtmlEditorCore} this
25533 * Fires when the editor is first receives the focus. Any insertion must wait
25534 * until after this event.
25535 * @param {Roo.HtmlEditorCore} this
25539 * @event beforesync
25540 * Fires before the textarea is updated with content from the editor iframe. Return false
25541 * to cancel the sync.
25542 * @param {Roo.HtmlEditorCore} this
25543 * @param {String} html
25547 * @event beforepush
25548 * Fires before the iframe editor is updated with content from the textarea. Return false
25549 * to cancel the push.
25550 * @param {Roo.HtmlEditorCore} this
25551 * @param {String} html
25556 * Fires when the textarea is updated with content from the editor iframe.
25557 * @param {Roo.HtmlEditorCore} this
25558 * @param {String} html
25563 * Fires when the iframe editor is updated with content from the textarea.
25564 * @param {Roo.HtmlEditorCore} this
25565 * @param {String} html
25570 * @event editorevent
25571 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25572 * @param {Roo.HtmlEditorCore} this
25578 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25580 // defaults : white / black...
25581 this.applyBlacklists();
25588 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25592 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25598 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25603 * @cfg {Number} height (in pixels)
25607 * @cfg {Number} width (in pixels)
25612 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25615 stylesheets: false,
25618 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25620 allowComments: false,
25624 // private properties
25625 validationEvent : false,
25627 initialized : false,
25629 sourceEditMode : false,
25630 onFocus : Roo.emptyFn,
25632 hideMode:'offsets',
25636 // blacklist + whitelisted elements..
25643 * Protected method that will not generally be called directly. It
25644 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25645 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25647 getDocMarkup : function(){
25651 // inherit styels from page...??
25652 if (this.stylesheets === false) {
25654 Roo.get(document.head).select('style').each(function(node) {
25655 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25658 Roo.get(document.head).select('link').each(function(node) {
25659 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25662 } else if (!this.stylesheets.length) {
25664 st = '<style type="text/css">' +
25665 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25668 for (var i in this.stylesheets) {
25669 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25674 st += '<style type="text/css">' +
25675 'IMG { cursor: pointer } ' +
25678 var cls = 'roo-htmleditor-body';
25680 if(this.bodyCls.length){
25681 cls += ' ' + this.bodyCls;
25684 return '<html><head>' + st +
25685 //<style type="text/css">' +
25686 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25688 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25692 onRender : function(ct, position)
25695 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25696 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25699 this.el.dom.style.border = '0 none';
25700 this.el.dom.setAttribute('tabIndex', -1);
25701 this.el.addClass('x-hidden hide');
25705 if(Roo.isIE){ // fix IE 1px bogus margin
25706 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25710 this.frameId = Roo.id();
25714 var iframe = this.owner.wrap.createChild({
25716 cls: 'form-control', // bootstrap..
25718 name: this.frameId,
25719 frameBorder : 'no',
25720 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25725 this.iframe = iframe.dom;
25727 this.assignDocWin();
25729 this.doc.designMode = 'on';
25732 this.doc.write(this.getDocMarkup());
25736 var task = { // must defer to wait for browser to be ready
25738 //console.log("run task?" + this.doc.readyState);
25739 this.assignDocWin();
25740 if(this.doc.body || this.doc.readyState == 'complete'){
25742 this.doc.designMode="on";
25746 Roo.TaskMgr.stop(task);
25747 this.initEditor.defer(10, this);
25754 Roo.TaskMgr.start(task);
25759 onResize : function(w, h)
25761 Roo.log('resize: ' +w + ',' + h );
25762 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25766 if(typeof w == 'number'){
25768 this.iframe.style.width = w + 'px';
25770 if(typeof h == 'number'){
25772 this.iframe.style.height = h + 'px';
25774 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25781 * Toggles the editor between standard and source edit mode.
25782 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25784 toggleSourceEdit : function(sourceEditMode){
25786 this.sourceEditMode = sourceEditMode === true;
25788 if(this.sourceEditMode){
25790 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25793 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25794 //this.iframe.className = '';
25797 //this.setSize(this.owner.wrap.getSize());
25798 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25805 * Protected method that will not generally be called directly. If you need/want
25806 * custom HTML cleanup, this is the method you should override.
25807 * @param {String} html The HTML to be cleaned
25808 * return {String} The cleaned HTML
25810 cleanHtml : function(html){
25811 html = String(html);
25812 if(html.length > 5){
25813 if(Roo.isSafari){ // strip safari nonsense
25814 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25817 if(html == ' '){
25824 * HTML Editor -> Textarea
25825 * Protected method that will not generally be called directly. Syncs the contents
25826 * of the editor iframe with the textarea.
25828 syncValue : function(){
25829 if(this.initialized){
25830 var bd = (this.doc.body || this.doc.documentElement);
25831 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25832 var html = bd.innerHTML;
25834 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25835 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25837 html = '<div style="'+m[0]+'">' + html + '</div>';
25840 html = this.cleanHtml(html);
25841 // fix up the special chars.. normaly like back quotes in word...
25842 // however we do not want to do this with chinese..
25843 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25845 var cc = match.charCodeAt();
25847 // Get the character value, handling surrogate pairs
25848 if (match.length == 2) {
25849 // It's a surrogate pair, calculate the Unicode code point
25850 var high = match.charCodeAt(0) - 0xD800;
25851 var low = match.charCodeAt(1) - 0xDC00;
25852 cc = (high * 0x400) + low + 0x10000;
25854 (cc >= 0x4E00 && cc < 0xA000 ) ||
25855 (cc >= 0x3400 && cc < 0x4E00 ) ||
25856 (cc >= 0xf900 && cc < 0xfb00 )
25861 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25862 return "&#" + cc + ";";
25869 if(this.owner.fireEvent('beforesync', this, html) !== false){
25870 this.el.dom.value = html;
25871 this.owner.fireEvent('sync', this, html);
25877 * Protected method that will not generally be called directly. Pushes the value of the textarea
25878 * into the iframe editor.
25880 pushValue : function(){
25881 if(this.initialized){
25882 var v = this.el.dom.value.trim();
25884 // if(v.length < 1){
25888 if(this.owner.fireEvent('beforepush', this, v) !== false){
25889 var d = (this.doc.body || this.doc.documentElement);
25891 this.cleanUpPaste();
25892 this.el.dom.value = d.innerHTML;
25893 this.owner.fireEvent('push', this, v);
25899 deferFocus : function(){
25900 this.focus.defer(10, this);
25904 focus : function(){
25905 if(this.win && !this.sourceEditMode){
25912 assignDocWin: function()
25914 var iframe = this.iframe;
25917 this.doc = iframe.contentWindow.document;
25918 this.win = iframe.contentWindow;
25920 // if (!Roo.get(this.frameId)) {
25923 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25924 // this.win = Roo.get(this.frameId).dom.contentWindow;
25926 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25930 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25931 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25936 initEditor : function(){
25937 //console.log("INIT EDITOR");
25938 this.assignDocWin();
25942 this.doc.designMode="on";
25944 this.doc.write(this.getDocMarkup());
25947 var dbody = (this.doc.body || this.doc.documentElement);
25948 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25949 // this copies styles from the containing element into thsi one..
25950 // not sure why we need all of this..
25951 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25953 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25954 //ss['background-attachment'] = 'fixed'; // w3c
25955 dbody.bgProperties = 'fixed'; // ie
25956 //Roo.DomHelper.applyStyles(dbody, ss);
25957 Roo.EventManager.on(this.doc, {
25958 //'mousedown': this.onEditorEvent,
25959 'mouseup': this.onEditorEvent,
25960 'dblclick': this.onEditorEvent,
25961 'click': this.onEditorEvent,
25962 'keyup': this.onEditorEvent,
25967 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25969 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25970 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25972 this.initialized = true;
25974 this.owner.fireEvent('initialize', this);
25979 onDestroy : function(){
25985 //for (var i =0; i < this.toolbars.length;i++) {
25986 // // fixme - ask toolbars for heights?
25987 // this.toolbars[i].onDestroy();
25990 //this.wrap.dom.innerHTML = '';
25991 //this.wrap.remove();
25996 onFirstFocus : function(){
25998 this.assignDocWin();
26001 this.activated = true;
26004 if(Roo.isGecko){ // prevent silly gecko errors
26006 var s = this.win.getSelection();
26007 if(!s.focusNode || s.focusNode.nodeType != 3){
26008 var r = s.getRangeAt(0);
26009 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26014 this.execCmd('useCSS', true);
26015 this.execCmd('styleWithCSS', false);
26018 this.owner.fireEvent('activate', this);
26022 adjustFont: function(btn){
26023 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26024 //if(Roo.isSafari){ // safari
26027 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26028 if(Roo.isSafari){ // safari
26029 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26030 v = (v < 10) ? 10 : v;
26031 v = (v > 48) ? 48 : v;
26032 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26037 v = Math.max(1, v+adjust);
26039 this.execCmd('FontSize', v );
26042 onEditorEvent : function(e)
26044 this.owner.fireEvent('editorevent', this, e);
26045 // this.updateToolbar();
26046 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26049 insertTag : function(tg)
26051 // could be a bit smarter... -> wrap the current selected tRoo..
26052 if (tg.toLowerCase() == 'span' ||
26053 tg.toLowerCase() == 'code' ||
26054 tg.toLowerCase() == 'sup' ||
26055 tg.toLowerCase() == 'sub'
26058 range = this.createRange(this.getSelection());
26059 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26060 wrappingNode.appendChild(range.extractContents());
26061 range.insertNode(wrappingNode);
26068 this.execCmd("formatblock", tg);
26072 insertText : function(txt)
26076 var range = this.createRange();
26077 range.deleteContents();
26078 //alert(Sender.getAttribute('label'));
26080 range.insertNode(this.doc.createTextNode(txt));
26086 * Executes a Midas editor command on the editor document and performs necessary focus and
26087 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26088 * @param {String} cmd The Midas command
26089 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26091 relayCmd : function(cmd, value){
26093 this.execCmd(cmd, value);
26094 this.owner.fireEvent('editorevent', this);
26095 //this.updateToolbar();
26096 this.owner.deferFocus();
26100 * Executes a Midas editor command directly on the editor document.
26101 * For visual commands, you should use {@link #relayCmd} instead.
26102 * <b>This should only be called after the editor is initialized.</b>
26103 * @param {String} cmd The Midas command
26104 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26106 execCmd : function(cmd, value){
26107 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26114 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26116 * @param {String} text | dom node..
26118 insertAtCursor : function(text)
26121 if(!this.activated){
26127 var r = this.doc.selection.createRange();
26138 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26142 // from jquery ui (MIT licenced)
26144 var win = this.win;
26146 if (win.getSelection && win.getSelection().getRangeAt) {
26147 range = win.getSelection().getRangeAt(0);
26148 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26149 range.insertNode(node);
26150 } else if (win.document.selection && win.document.selection.createRange) {
26151 // no firefox support
26152 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26153 win.document.selection.createRange().pasteHTML(txt);
26155 // no firefox support
26156 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26157 this.execCmd('InsertHTML', txt);
26166 mozKeyPress : function(e){
26168 var c = e.getCharCode(), cmd;
26171 c = String.fromCharCode(c).toLowerCase();
26185 this.cleanUpPaste.defer(100, this);
26193 e.preventDefault();
26201 fixKeys : function(){ // load time branching for fastest keydown performance
26203 return function(e){
26204 var k = e.getKey(), r;
26207 r = this.doc.selection.createRange();
26210 r.pasteHTML('    ');
26217 r = this.doc.selection.createRange();
26219 var target = r.parentElement();
26220 if(!target || target.tagName.toLowerCase() != 'li'){
26222 r.pasteHTML('<br />');
26228 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26229 this.cleanUpPaste.defer(100, this);
26235 }else if(Roo.isOpera){
26236 return function(e){
26237 var k = e.getKey();
26241 this.execCmd('InsertHTML','    ');
26244 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26245 this.cleanUpPaste.defer(100, this);
26250 }else if(Roo.isSafari){
26251 return function(e){
26252 var k = e.getKey();
26256 this.execCmd('InsertText','\t');
26260 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26261 this.cleanUpPaste.defer(100, this);
26269 getAllAncestors: function()
26271 var p = this.getSelectedNode();
26274 a.push(p); // push blank onto stack..
26275 p = this.getParentElement();
26279 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26283 a.push(this.doc.body);
26287 lastSelNode : false,
26290 getSelection : function()
26292 this.assignDocWin();
26293 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26296 getSelectedNode: function()
26298 // this may only work on Gecko!!!
26300 // should we cache this!!!!
26305 var range = this.createRange(this.getSelection()).cloneRange();
26308 var parent = range.parentElement();
26310 var testRange = range.duplicate();
26311 testRange.moveToElementText(parent);
26312 if (testRange.inRange(range)) {
26315 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26318 parent = parent.parentElement;
26323 // is ancestor a text element.
26324 var ac = range.commonAncestorContainer;
26325 if (ac.nodeType == 3) {
26326 ac = ac.parentNode;
26329 var ar = ac.childNodes;
26332 var other_nodes = [];
26333 var has_other_nodes = false;
26334 for (var i=0;i<ar.length;i++) {
26335 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26338 // fullly contained node.
26340 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26345 // probably selected..
26346 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26347 other_nodes.push(ar[i]);
26351 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26356 has_other_nodes = true;
26358 if (!nodes.length && other_nodes.length) {
26359 nodes= other_nodes;
26361 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26367 createRange: function(sel)
26369 // this has strange effects when using with
26370 // top toolbar - not sure if it's a great idea.
26371 //this.editor.contentWindow.focus();
26372 if (typeof sel != "undefined") {
26374 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26376 return this.doc.createRange();
26379 return this.doc.createRange();
26382 getParentElement: function()
26385 this.assignDocWin();
26386 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26388 var range = this.createRange(sel);
26391 var p = range.commonAncestorContainer;
26392 while (p.nodeType == 3) { // text node
26403 * Range intersection.. the hard stuff...
26407 * [ -- selected range --- ]
26411 * if end is before start or hits it. fail.
26412 * if start is after end or hits it fail.
26414 * if either hits (but other is outside. - then it's not
26420 // @see http://www.thismuchiknow.co.uk/?p=64.
26421 rangeIntersectsNode : function(range, node)
26423 var nodeRange = node.ownerDocument.createRange();
26425 nodeRange.selectNode(node);
26427 nodeRange.selectNodeContents(node);
26430 var rangeStartRange = range.cloneRange();
26431 rangeStartRange.collapse(true);
26433 var rangeEndRange = range.cloneRange();
26434 rangeEndRange.collapse(false);
26436 var nodeStartRange = nodeRange.cloneRange();
26437 nodeStartRange.collapse(true);
26439 var nodeEndRange = nodeRange.cloneRange();
26440 nodeEndRange.collapse(false);
26442 return rangeStartRange.compareBoundaryPoints(
26443 Range.START_TO_START, nodeEndRange) == -1 &&
26444 rangeEndRange.compareBoundaryPoints(
26445 Range.START_TO_START, nodeStartRange) == 1;
26449 rangeCompareNode : function(range, node)
26451 var nodeRange = node.ownerDocument.createRange();
26453 nodeRange.selectNode(node);
26455 nodeRange.selectNodeContents(node);
26459 range.collapse(true);
26461 nodeRange.collapse(true);
26463 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26464 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26466 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26468 var nodeIsBefore = ss == 1;
26469 var nodeIsAfter = ee == -1;
26471 if (nodeIsBefore && nodeIsAfter) {
26474 if (!nodeIsBefore && nodeIsAfter) {
26475 return 1; //right trailed.
26478 if (nodeIsBefore && !nodeIsAfter) {
26479 return 2; // left trailed.
26485 // private? - in a new class?
26486 cleanUpPaste : function()
26488 // cleans up the whole document..
26489 Roo.log('cleanuppaste');
26491 this.cleanUpChildren(this.doc.body);
26492 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26493 if (clean != this.doc.body.innerHTML) {
26494 this.doc.body.innerHTML = clean;
26499 cleanWordChars : function(input) {// change the chars to hex code
26500 var he = Roo.HtmlEditorCore;
26502 var output = input;
26503 Roo.each(he.swapCodes, function(sw) {
26504 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26506 output = output.replace(swapper, sw[1]);
26513 cleanUpChildren : function (n)
26515 if (!n.childNodes.length) {
26518 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26519 this.cleanUpChild(n.childNodes[i]);
26526 cleanUpChild : function (node)
26529 //console.log(node);
26530 if (node.nodeName == "#text") {
26531 // clean up silly Windows -- stuff?
26534 if (node.nodeName == "#comment") {
26535 if (!this.allowComments) {
26536 node.parentNode.removeChild(node);
26538 // clean up silly Windows -- stuff?
26541 var lcname = node.tagName.toLowerCase();
26542 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26543 // whitelist of tags..
26545 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26547 node.parentNode.removeChild(node);
26552 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26554 // spans with no attributes - just remove them..
26555 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26556 remove_keep_children = true;
26559 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26560 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26562 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26563 // remove_keep_children = true;
26566 if (remove_keep_children) {
26567 this.cleanUpChildren(node);
26568 // inserts everything just before this node...
26569 while (node.childNodes.length) {
26570 var cn = node.childNodes[0];
26571 node.removeChild(cn);
26572 node.parentNode.insertBefore(cn, node);
26574 node.parentNode.removeChild(node);
26578 if (!node.attributes || !node.attributes.length) {
26583 this.cleanUpChildren(node);
26587 function cleanAttr(n,v)
26590 if (v.match(/^\./) || v.match(/^\//)) {
26593 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26596 if (v.match(/^#/)) {
26599 if (v.match(/^\{/)) { // allow template editing.
26602 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26603 node.removeAttribute(n);
26607 var cwhite = this.cwhite;
26608 var cblack = this.cblack;
26610 function cleanStyle(n,v)
26612 if (v.match(/expression/)) { //XSS?? should we even bother..
26613 node.removeAttribute(n);
26617 var parts = v.split(/;/);
26620 Roo.each(parts, function(p) {
26621 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26625 var l = p.split(':').shift().replace(/\s+/g,'');
26626 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26628 if ( cwhite.length && cblack.indexOf(l) > -1) {
26629 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26630 //node.removeAttribute(n);
26634 // only allow 'c whitelisted system attributes'
26635 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26636 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26637 //node.removeAttribute(n);
26647 if (clean.length) {
26648 node.setAttribute(n, clean.join(';'));
26650 node.removeAttribute(n);
26656 for (var i = node.attributes.length-1; i > -1 ; i--) {
26657 var a = node.attributes[i];
26660 if (a.name.toLowerCase().substr(0,2)=='on') {
26661 node.removeAttribute(a.name);
26664 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26665 node.removeAttribute(a.name);
26668 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26669 cleanAttr(a.name,a.value); // fixme..
26672 if (a.name == 'style') {
26673 cleanStyle(a.name,a.value);
26676 /// clean up MS crap..
26677 // tecnically this should be a list of valid class'es..
26680 if (a.name == 'class') {
26681 if (a.value.match(/^Mso/)) {
26682 node.removeAttribute('class');
26685 if (a.value.match(/^body$/)) {
26686 node.removeAttribute('class');
26697 this.cleanUpChildren(node);
26703 * Clean up MS wordisms...
26705 cleanWord : function(node)
26708 this.cleanWord(this.doc.body);
26713 node.nodeName == 'SPAN' &&
26714 !node.hasAttributes() &&
26715 node.childNodes.length == 1 &&
26716 node.firstChild.nodeName == "#text"
26718 var textNode = node.firstChild;
26719 node.removeChild(textNode);
26720 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26721 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26723 node.parentNode.insertBefore(textNode, node);
26724 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26725 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26727 node.parentNode.removeChild(node);
26730 if (node.nodeName == "#text") {
26731 // clean up silly Windows -- stuff?
26734 if (node.nodeName == "#comment") {
26735 node.parentNode.removeChild(node);
26736 // clean up silly Windows -- stuff?
26740 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26741 node.parentNode.removeChild(node);
26744 //Roo.log(node.tagName);
26745 // remove - but keep children..
26746 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26747 //Roo.log('-- removed');
26748 while (node.childNodes.length) {
26749 var cn = node.childNodes[0];
26750 node.removeChild(cn);
26751 node.parentNode.insertBefore(cn, node);
26752 // move node to parent - and clean it..
26753 this.cleanWord(cn);
26755 node.parentNode.removeChild(node);
26756 /// no need to iterate chidlren = it's got none..
26757 //this.iterateChildren(node, this.cleanWord);
26761 if (node.className.length) {
26763 var cn = node.className.split(/\W+/);
26765 Roo.each(cn, function(cls) {
26766 if (cls.match(/Mso[a-zA-Z]+/)) {
26771 node.className = cna.length ? cna.join(' ') : '';
26773 node.removeAttribute("class");
26777 if (node.hasAttribute("lang")) {
26778 node.removeAttribute("lang");
26781 if (node.hasAttribute("style")) {
26783 var styles = node.getAttribute("style").split(";");
26785 Roo.each(styles, function(s) {
26786 if (!s.match(/:/)) {
26789 var kv = s.split(":");
26790 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26793 // what ever is left... we allow.
26796 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26797 if (!nstyle.length) {
26798 node.removeAttribute('style');
26801 this.iterateChildren(node, this.cleanWord);
26807 * iterateChildren of a Node, calling fn each time, using this as the scole..
26808 * @param {DomNode} node node to iterate children of.
26809 * @param {Function} fn method of this class to call on each item.
26811 iterateChildren : function(node, fn)
26813 if (!node.childNodes.length) {
26816 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26817 fn.call(this, node.childNodes[i])
26823 * cleanTableWidths.
26825 * Quite often pasting from word etc.. results in tables with column and widths.
26826 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26829 cleanTableWidths : function(node)
26834 this.cleanTableWidths(this.doc.body);
26839 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26842 Roo.log(node.tagName);
26843 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26844 this.iterateChildren(node, this.cleanTableWidths);
26847 if (node.hasAttribute('width')) {
26848 node.removeAttribute('width');
26852 if (node.hasAttribute("style")) {
26855 var styles = node.getAttribute("style").split(";");
26857 Roo.each(styles, function(s) {
26858 if (!s.match(/:/)) {
26861 var kv = s.split(":");
26862 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26865 // what ever is left... we allow.
26868 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26869 if (!nstyle.length) {
26870 node.removeAttribute('style');
26874 this.iterateChildren(node, this.cleanTableWidths);
26882 domToHTML : function(currentElement, depth, nopadtext) {
26884 depth = depth || 0;
26885 nopadtext = nopadtext || false;
26887 if (!currentElement) {
26888 return this.domToHTML(this.doc.body);
26891 //Roo.log(currentElement);
26893 var allText = false;
26894 var nodeName = currentElement.nodeName;
26895 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26897 if (nodeName == '#text') {
26899 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26904 if (nodeName != 'BODY') {
26907 // Prints the node tagName, such as <A>, <IMG>, etc
26910 for(i = 0; i < currentElement.attributes.length;i++) {
26912 var aname = currentElement.attributes.item(i).name;
26913 if (!currentElement.attributes.item(i).value.length) {
26916 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26919 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26928 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26931 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26936 // Traverse the tree
26938 var currentElementChild = currentElement.childNodes.item(i);
26939 var allText = true;
26940 var innerHTML = '';
26942 while (currentElementChild) {
26943 // Formatting code (indent the tree so it looks nice on the screen)
26944 var nopad = nopadtext;
26945 if (lastnode == 'SPAN') {
26949 if (currentElementChild.nodeName == '#text') {
26950 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26951 toadd = nopadtext ? toadd : toadd.trim();
26952 if (!nopad && toadd.length > 80) {
26953 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26955 innerHTML += toadd;
26958 currentElementChild = currentElement.childNodes.item(i);
26964 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26966 // Recursively traverse the tree structure of the child node
26967 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26968 lastnode = currentElementChild.nodeName;
26970 currentElementChild=currentElement.childNodes.item(i);
26976 // The remaining code is mostly for formatting the tree
26977 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26982 ret+= "</"+tagName+">";
26988 applyBlacklists : function()
26990 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26991 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26995 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26996 if (b.indexOf(tag) > -1) {
26999 this.white.push(tag);
27003 Roo.each(w, function(tag) {
27004 if (b.indexOf(tag) > -1) {
27007 if (this.white.indexOf(tag) > -1) {
27010 this.white.push(tag);
27015 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27016 if (w.indexOf(tag) > -1) {
27019 this.black.push(tag);
27023 Roo.each(b, function(tag) {
27024 if (w.indexOf(tag) > -1) {
27027 if (this.black.indexOf(tag) > -1) {
27030 this.black.push(tag);
27035 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27036 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27040 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27041 if (b.indexOf(tag) > -1) {
27044 this.cwhite.push(tag);
27048 Roo.each(w, function(tag) {
27049 if (b.indexOf(tag) > -1) {
27052 if (this.cwhite.indexOf(tag) > -1) {
27055 this.cwhite.push(tag);
27060 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27061 if (w.indexOf(tag) > -1) {
27064 this.cblack.push(tag);
27068 Roo.each(b, function(tag) {
27069 if (w.indexOf(tag) > -1) {
27072 if (this.cblack.indexOf(tag) > -1) {
27075 this.cblack.push(tag);
27080 setStylesheets : function(stylesheets)
27082 if(typeof(stylesheets) == 'string'){
27083 Roo.get(this.iframe.contentDocument.head).createChild({
27085 rel : 'stylesheet',
27094 Roo.each(stylesheets, function(s) {
27099 Roo.get(_this.iframe.contentDocument.head).createChild({
27101 rel : 'stylesheet',
27110 removeStylesheets : function()
27114 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27119 setStyle : function(style)
27121 Roo.get(this.iframe.contentDocument.head).createChild({
27130 // hide stuff that is not compatible
27144 * @event specialkey
27148 * @cfg {String} fieldClass @hide
27151 * @cfg {String} focusClass @hide
27154 * @cfg {String} autoCreate @hide
27157 * @cfg {String} inputType @hide
27160 * @cfg {String} invalidClass @hide
27163 * @cfg {String} invalidText @hide
27166 * @cfg {String} msgFx @hide
27169 * @cfg {String} validateOnBlur @hide
27173 Roo.HtmlEditorCore.white = [
27174 'area', 'br', 'img', 'input', 'hr', 'wbr',
27176 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27177 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27178 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27179 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27180 'table', 'ul', 'xmp',
27182 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27185 'dir', 'menu', 'ol', 'ul', 'dl',
27191 Roo.HtmlEditorCore.black = [
27192 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27194 'base', 'basefont', 'bgsound', 'blink', 'body',
27195 'frame', 'frameset', 'head', 'html', 'ilayer',
27196 'iframe', 'layer', 'link', 'meta', 'object',
27197 'script', 'style' ,'title', 'xml' // clean later..
27199 Roo.HtmlEditorCore.clean = [
27200 'script', 'style', 'title', 'xml'
27202 Roo.HtmlEditorCore.remove = [
27207 Roo.HtmlEditorCore.ablack = [
27211 Roo.HtmlEditorCore.aclean = [
27212 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27216 Roo.HtmlEditorCore.pwhite= [
27217 'http', 'https', 'mailto'
27220 // white listed style attributes.
27221 Roo.HtmlEditorCore.cwhite= [
27222 // 'text-align', /// default is to allow most things..
27228 // black listed style attributes.
27229 Roo.HtmlEditorCore.cblack= [
27230 // 'font-size' -- this can be set by the project
27234 Roo.HtmlEditorCore.swapCodes =[
27235 [ 8211, "–" ],
27236 [ 8212, "—" ],
27253 * @class Roo.bootstrap.HtmlEditor
27254 * @extends Roo.bootstrap.TextArea
27255 * Bootstrap HtmlEditor class
27258 * Create a new HtmlEditor
27259 * @param {Object} config The config object
27262 Roo.bootstrap.HtmlEditor = function(config){
27263 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27264 if (!this.toolbars) {
27265 this.toolbars = [];
27268 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27271 * @event initialize
27272 * Fires when the editor is fully initialized (including the iframe)
27273 * @param {HtmlEditor} this
27278 * Fires when the editor is first receives the focus. Any insertion must wait
27279 * until after this event.
27280 * @param {HtmlEditor} this
27284 * @event beforesync
27285 * Fires before the textarea is updated with content from the editor iframe. Return false
27286 * to cancel the sync.
27287 * @param {HtmlEditor} this
27288 * @param {String} html
27292 * @event beforepush
27293 * Fires before the iframe editor is updated with content from the textarea. Return false
27294 * to cancel the push.
27295 * @param {HtmlEditor} this
27296 * @param {String} html
27301 * Fires when the textarea is updated with content from the editor iframe.
27302 * @param {HtmlEditor} this
27303 * @param {String} html
27308 * Fires when the iframe editor is updated with content from the textarea.
27309 * @param {HtmlEditor} this
27310 * @param {String} html
27314 * @event editmodechange
27315 * Fires when the editor switches edit modes
27316 * @param {HtmlEditor} this
27317 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27319 editmodechange: true,
27321 * @event editorevent
27322 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27323 * @param {HtmlEditor} this
27327 * @event firstfocus
27328 * Fires when on first focus - needed by toolbars..
27329 * @param {HtmlEditor} this
27334 * Auto save the htmlEditor value as a file into Events
27335 * @param {HtmlEditor} this
27339 * @event savedpreview
27340 * preview the saved version of htmlEditor
27341 * @param {HtmlEditor} this
27348 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27352 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27357 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27362 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27367 * @cfg {Number} height (in pixels)
27371 * @cfg {Number} width (in pixels)
27376 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27379 stylesheets: false,
27384 // private properties
27385 validationEvent : false,
27387 initialized : false,
27390 onFocus : Roo.emptyFn,
27392 hideMode:'offsets',
27394 tbContainer : false,
27398 toolbarContainer :function() {
27399 return this.wrap.select('.x-html-editor-tb',true).first();
27403 * Protected method that will not generally be called directly. It
27404 * is called when the editor creates its toolbar. Override this method if you need to
27405 * add custom toolbar buttons.
27406 * @param {HtmlEditor} editor
27408 createToolbar : function(){
27409 Roo.log('renewing');
27410 Roo.log("create toolbars");
27412 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27413 this.toolbars[0].render(this.toolbarContainer());
27417 // if (!editor.toolbars || !editor.toolbars.length) {
27418 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27421 // for (var i =0 ; i < editor.toolbars.length;i++) {
27422 // editor.toolbars[i] = Roo.factory(
27423 // typeof(editor.toolbars[i]) == 'string' ?
27424 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27425 // Roo.bootstrap.HtmlEditor);
27426 // editor.toolbars[i].init(editor);
27432 onRender : function(ct, position)
27434 // Roo.log("Call onRender: " + this.xtype);
27436 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27438 this.wrap = this.inputEl().wrap({
27439 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27442 this.editorcore.onRender(ct, position);
27444 if (this.resizable) {
27445 this.resizeEl = new Roo.Resizable(this.wrap, {
27449 minHeight : this.height,
27450 height: this.height,
27451 handles : this.resizable,
27454 resize : function(r, w, h) {
27455 _t.onResize(w,h); // -something
27461 this.createToolbar(this);
27464 if(!this.width && this.resizable){
27465 this.setSize(this.wrap.getSize());
27467 if (this.resizeEl) {
27468 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27469 // should trigger onReize..
27475 onResize : function(w, h)
27477 Roo.log('resize: ' +w + ',' + h );
27478 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27482 if(this.inputEl() ){
27483 if(typeof w == 'number'){
27484 var aw = w - this.wrap.getFrameWidth('lr');
27485 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27488 if(typeof h == 'number'){
27489 var tbh = -11; // fixme it needs to tool bar size!
27490 for (var i =0; i < this.toolbars.length;i++) {
27491 // fixme - ask toolbars for heights?
27492 tbh += this.toolbars[i].el.getHeight();
27493 //if (this.toolbars[i].footer) {
27494 // tbh += this.toolbars[i].footer.el.getHeight();
27502 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27503 ah -= 5; // knock a few pixes off for look..
27504 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27508 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27509 this.editorcore.onResize(ew,eh);
27514 * Toggles the editor between standard and source edit mode.
27515 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27517 toggleSourceEdit : function(sourceEditMode)
27519 this.editorcore.toggleSourceEdit(sourceEditMode);
27521 if(this.editorcore.sourceEditMode){
27522 Roo.log('editor - showing textarea');
27525 // Roo.log(this.syncValue());
27527 this.inputEl().removeClass(['hide', 'x-hidden']);
27528 this.inputEl().dom.removeAttribute('tabIndex');
27529 this.inputEl().focus();
27531 Roo.log('editor - hiding textarea');
27533 // Roo.log(this.pushValue());
27536 this.inputEl().addClass(['hide', 'x-hidden']);
27537 this.inputEl().dom.setAttribute('tabIndex', -1);
27538 //this.deferFocus();
27541 if(this.resizable){
27542 this.setSize(this.wrap.getSize());
27545 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27548 // private (for BoxComponent)
27549 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27551 // private (for BoxComponent)
27552 getResizeEl : function(){
27556 // private (for BoxComponent)
27557 getPositionEl : function(){
27562 initEvents : function(){
27563 this.originalValue = this.getValue();
27567 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27570 // markInvalid : Roo.emptyFn,
27572 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27575 // clearInvalid : Roo.emptyFn,
27577 setValue : function(v){
27578 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27579 this.editorcore.pushValue();
27584 deferFocus : function(){
27585 this.focus.defer(10, this);
27589 focus : function(){
27590 this.editorcore.focus();
27596 onDestroy : function(){
27602 for (var i =0; i < this.toolbars.length;i++) {
27603 // fixme - ask toolbars for heights?
27604 this.toolbars[i].onDestroy();
27607 this.wrap.dom.innerHTML = '';
27608 this.wrap.remove();
27613 onFirstFocus : function(){
27614 //Roo.log("onFirstFocus");
27615 this.editorcore.onFirstFocus();
27616 for (var i =0; i < this.toolbars.length;i++) {
27617 this.toolbars[i].onFirstFocus();
27623 syncValue : function()
27625 this.editorcore.syncValue();
27628 pushValue : function()
27630 this.editorcore.pushValue();
27634 // hide stuff that is not compatible
27648 * @event specialkey
27652 * @cfg {String} fieldClass @hide
27655 * @cfg {String} focusClass @hide
27658 * @cfg {String} autoCreate @hide
27661 * @cfg {String} inputType @hide
27665 * @cfg {String} invalidText @hide
27668 * @cfg {String} msgFx @hide
27671 * @cfg {String} validateOnBlur @hide
27680 Roo.namespace('Roo.bootstrap.htmleditor');
27682 * @class Roo.bootstrap.HtmlEditorToolbar1
27688 new Roo.bootstrap.HtmlEditor({
27691 new Roo.bootstrap.HtmlEditorToolbar1({
27692 disable : { fonts: 1 , format: 1, ..., ... , ...],
27698 * @cfg {Object} disable List of elements to disable..
27699 * @cfg {Array} btns List of additional buttons.
27703 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27706 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27709 Roo.apply(this, config);
27711 // default disabled, based on 'good practice'..
27712 this.disable = this.disable || {};
27713 Roo.applyIf(this.disable, {
27716 specialElements : true
27718 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27720 this.editor = config.editor;
27721 this.editorcore = config.editor.editorcore;
27723 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27725 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27726 // dont call parent... till later.
27728 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27733 editorcore : false,
27738 "h1","h2","h3","h4","h5","h6",
27740 "abbr", "acronym", "address", "cite", "samp", "var",
27744 onRender : function(ct, position)
27746 // Roo.log("Call onRender: " + this.xtype);
27748 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27750 this.el.dom.style.marginBottom = '0';
27752 var editorcore = this.editorcore;
27753 var editor= this.editor;
27756 var btn = function(id,cmd , toggle, handler, html){
27758 var event = toggle ? 'toggle' : 'click';
27763 xns: Roo.bootstrap,
27767 enableToggle:toggle !== false,
27769 pressed : toggle ? false : null,
27772 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27773 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27779 // var cb_box = function...
27784 xns: Roo.bootstrap,
27789 xns: Roo.bootstrap,
27793 Roo.each(this.formats, function(f) {
27794 style.menu.items.push({
27796 xns: Roo.bootstrap,
27797 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27802 editorcore.insertTag(this.tagname);
27809 children.push(style);
27811 btn('bold',false,true);
27812 btn('italic',false,true);
27813 btn('align-left', 'justifyleft',true);
27814 btn('align-center', 'justifycenter',true);
27815 btn('align-right' , 'justifyright',true);
27816 btn('link', false, false, function(btn) {
27817 //Roo.log("create link?");
27818 var url = prompt(this.createLinkText, this.defaultLinkValue);
27819 if(url && url != 'http:/'+'/'){
27820 this.editorcore.relayCmd('createlink', url);
27823 btn('list','insertunorderedlist',true);
27824 btn('pencil', false,true, function(btn){
27826 this.toggleSourceEdit(btn.pressed);
27829 if (this.editor.btns.length > 0) {
27830 for (var i = 0; i<this.editor.btns.length; i++) {
27831 children.push(this.editor.btns[i]);
27839 xns: Roo.bootstrap,
27844 xns: Roo.bootstrap,
27849 cog.menu.items.push({
27851 xns: Roo.bootstrap,
27852 html : Clean styles,
27857 editorcore.insertTag(this.tagname);
27866 this.xtype = 'NavSimplebar';
27868 for(var i=0;i< children.length;i++) {
27870 this.buttons.add(this.addxtypeChild(children[i]));
27874 editor.on('editorevent', this.updateToolbar, this);
27876 onBtnClick : function(id)
27878 this.editorcore.relayCmd(id);
27879 this.editorcore.focus();
27883 * Protected method that will not generally be called directly. It triggers
27884 * a toolbar update by reading the markup state of the current selection in the editor.
27886 updateToolbar: function(){
27888 if(!this.editorcore.activated){
27889 this.editor.onFirstFocus(); // is this neeed?
27893 var btns = this.buttons;
27894 var doc = this.editorcore.doc;
27895 btns.get('bold').setActive(doc.queryCommandState('bold'));
27896 btns.get('italic').setActive(doc.queryCommandState('italic'));
27897 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27899 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27900 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27901 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27903 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27904 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27907 var ans = this.editorcore.getAllAncestors();
27908 if (this.formatCombo) {
27911 var store = this.formatCombo.store;
27912 this.formatCombo.setValue("");
27913 for (var i =0; i < ans.length;i++) {
27914 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27916 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27924 // hides menus... - so this cant be on a menu...
27925 Roo.bootstrap.MenuMgr.hideAll();
27927 Roo.bootstrap.MenuMgr.hideAll();
27928 //this.editorsyncValue();
27930 onFirstFocus: function() {
27931 this.buttons.each(function(item){
27935 toggleSourceEdit : function(sourceEditMode){
27938 if(sourceEditMode){
27939 Roo.log("disabling buttons");
27940 this.buttons.each( function(item){
27941 if(item.cmd != 'pencil'){
27947 Roo.log("enabling buttons");
27948 if(this.editorcore.initialized){
27949 this.buttons.each( function(item){
27955 Roo.log("calling toggole on editor");
27956 // tell the editor that it's been pressed..
27957 this.editor.toggleSourceEdit(sourceEditMode);
27971 * @class Roo.bootstrap.Markdown
27972 * @extends Roo.bootstrap.TextArea
27973 * Bootstrap Showdown editable area
27974 * @cfg {string} content
27977 * Create a new Showdown
27980 Roo.bootstrap.Markdown = function(config){
27981 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27985 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27989 initEvents : function()
27992 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27993 this.markdownEl = this.el.createChild({
27994 cls : 'roo-markdown-area'
27996 this.inputEl().addClass('d-none');
27997 if (this.getValue() == '') {
27998 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28001 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28003 this.markdownEl.on('click', this.toggleTextEdit, this);
28004 this.on('blur', this.toggleTextEdit, this);
28005 this.on('specialkey', this.resizeTextArea, this);
28008 toggleTextEdit : function()
28010 var sh = this.markdownEl.getHeight();
28011 this.inputEl().addClass('d-none');
28012 this.markdownEl.addClass('d-none');
28013 if (!this.editing) {
28015 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28016 this.inputEl().removeClass('d-none');
28017 this.inputEl().focus();
28018 this.editing = true;
28021 // show showdown...
28022 this.updateMarkdown();
28023 this.markdownEl.removeClass('d-none');
28024 this.editing = false;
28027 updateMarkdown : function()
28029 if (this.getValue() == '') {
28030 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28034 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28037 resizeTextArea: function () {
28040 Roo.log([sh, this.getValue().split("\n").length * 30]);
28041 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28043 setValue : function(val)
28045 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28046 if (!this.editing) {
28047 this.updateMarkdown();
28053 if (!this.editing) {
28054 this.toggleTextEdit();
28062 * Ext JS Library 1.1.1
28063 * Copyright(c) 2006-2007, Ext JS, LLC.
28065 * Originally Released Under LGPL - original licence link has changed is not relivant.
28068 * <script type="text/javascript">
28072 * @class Roo.bootstrap.PagingToolbar
28073 * @extends Roo.bootstrap.NavSimplebar
28074 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28076 * Create a new PagingToolbar
28077 * @param {Object} config The config object
28078 * @param {Roo.data.Store} store
28080 Roo.bootstrap.PagingToolbar = function(config)
28082 // old args format still supported... - xtype is prefered..
28083 // created from xtype...
28085 this.ds = config.dataSource;
28087 if (config.store && !this.ds) {
28088 this.store= Roo.factory(config.store, Roo.data);
28089 this.ds = this.store;
28090 this.ds.xmodule = this.xmodule || false;
28093 this.toolbarItems = [];
28094 if (config.items) {
28095 this.toolbarItems = config.items;
28098 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28103 this.bind(this.ds);
28106 if (Roo.bootstrap.version == 4) {
28107 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28109 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28114 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28116 * @cfg {Roo.data.Store} dataSource
28117 * The underlying data store providing the paged data
28120 * @cfg {String/HTMLElement/Element} container
28121 * container The id or element that will contain the toolbar
28124 * @cfg {Boolean} displayInfo
28125 * True to display the displayMsg (defaults to false)
28128 * @cfg {Number} pageSize
28129 * The number of records to display per page (defaults to 20)
28133 * @cfg {String} displayMsg
28134 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28136 displayMsg : 'Displaying {0} - {1} of {2}',
28138 * @cfg {String} emptyMsg
28139 * The message to display when no records are found (defaults to "No data to display")
28141 emptyMsg : 'No data to display',
28143 * Customizable piece of the default paging text (defaults to "Page")
28146 beforePageText : "Page",
28148 * Customizable piece of the default paging text (defaults to "of %0")
28151 afterPageText : "of {0}",
28153 * Customizable piece of the default paging text (defaults to "First Page")
28156 firstText : "First Page",
28158 * Customizable piece of the default paging text (defaults to "Previous Page")
28161 prevText : "Previous Page",
28163 * Customizable piece of the default paging text (defaults to "Next Page")
28166 nextText : "Next Page",
28168 * Customizable piece of the default paging text (defaults to "Last Page")
28171 lastText : "Last Page",
28173 * Customizable piece of the default paging text (defaults to "Refresh")
28176 refreshText : "Refresh",
28180 onRender : function(ct, position)
28182 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28183 this.navgroup.parentId = this.id;
28184 this.navgroup.onRender(this.el, null);
28185 // add the buttons to the navgroup
28187 if(this.displayInfo){
28188 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28189 this.displayEl = this.el.select('.x-paging-info', true).first();
28190 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28191 // this.displayEl = navel.el.select('span',true).first();
28197 Roo.each(_this.buttons, function(e){ // this might need to use render????
28198 Roo.factory(e).render(_this.el);
28202 Roo.each(_this.toolbarItems, function(e) {
28203 _this.navgroup.addItem(e);
28207 this.first = this.navgroup.addItem({
28208 tooltip: this.firstText,
28209 cls: "prev btn-outline-secondary",
28210 html : ' <i class="fa fa-step-backward"></i>',
28212 preventDefault: true,
28213 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28216 this.prev = this.navgroup.addItem({
28217 tooltip: this.prevText,
28218 cls: "prev btn-outline-secondary",
28219 html : ' <i class="fa fa-backward"></i>',
28221 preventDefault: true,
28222 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28224 //this.addSeparator();
28227 var field = this.navgroup.addItem( {
28229 cls : 'x-paging-position btn-outline-secondary',
28231 html : this.beforePageText +
28232 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28233 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28236 this.field = field.el.select('input', true).first();
28237 this.field.on("keydown", this.onPagingKeydown, this);
28238 this.field.on("focus", function(){this.dom.select();});
28241 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28242 //this.field.setHeight(18);
28243 //this.addSeparator();
28244 this.next = this.navgroup.addItem({
28245 tooltip: this.nextText,
28246 cls: "next btn-outline-secondary",
28247 html : ' <i class="fa fa-forward"></i>',
28249 preventDefault: true,
28250 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28252 this.last = this.navgroup.addItem({
28253 tooltip: this.lastText,
28254 html : ' <i class="fa fa-step-forward"></i>',
28255 cls: "next btn-outline-secondary",
28257 preventDefault: true,
28258 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28260 //this.addSeparator();
28261 this.loading = this.navgroup.addItem({
28262 tooltip: this.refreshText,
28263 cls: "btn-outline-secondary",
28264 html : ' <i class="fa fa-refresh"></i>',
28265 preventDefault: true,
28266 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28272 updateInfo : function(){
28273 if(this.displayEl){
28274 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28275 var msg = count == 0 ?
28279 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28281 this.displayEl.update(msg);
28286 onLoad : function(ds, r, o)
28288 this.cursor = o.params && o.params.start ? o.params.start : 0;
28290 var d = this.getPageData(),
28295 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28296 this.field.dom.value = ap;
28297 this.first.setDisabled(ap == 1);
28298 this.prev.setDisabled(ap == 1);
28299 this.next.setDisabled(ap == ps);
28300 this.last.setDisabled(ap == ps);
28301 this.loading.enable();
28306 getPageData : function(){
28307 var total = this.ds.getTotalCount();
28310 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28311 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28316 onLoadError : function(){
28317 this.loading.enable();
28321 onPagingKeydown : function(e){
28322 var k = e.getKey();
28323 var d = this.getPageData();
28325 var v = this.field.dom.value, pageNum;
28326 if(!v || isNaN(pageNum = parseInt(v, 10))){
28327 this.field.dom.value = d.activePage;
28330 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28331 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28334 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))
28336 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28337 this.field.dom.value = pageNum;
28338 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28341 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28343 var v = this.field.dom.value, pageNum;
28344 var increment = (e.shiftKey) ? 10 : 1;
28345 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28348 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28349 this.field.dom.value = d.activePage;
28352 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28354 this.field.dom.value = parseInt(v, 10) + increment;
28355 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28356 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28363 beforeLoad : function(){
28365 this.loading.disable();
28370 onClick : function(which){
28379 ds.load({params:{start: 0, limit: this.pageSize}});
28382 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28385 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28388 var total = ds.getTotalCount();
28389 var extra = total % this.pageSize;
28390 var lastStart = extra ? (total - extra) : total-this.pageSize;
28391 ds.load({params:{start: lastStart, limit: this.pageSize}});
28394 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28400 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28401 * @param {Roo.data.Store} store The data store to unbind
28403 unbind : function(ds){
28404 ds.un("beforeload", this.beforeLoad, this);
28405 ds.un("load", this.onLoad, this);
28406 ds.un("loadexception", this.onLoadError, this);
28407 ds.un("remove", this.updateInfo, this);
28408 ds.un("add", this.updateInfo, this);
28409 this.ds = undefined;
28413 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28414 * @param {Roo.data.Store} store The data store to bind
28416 bind : function(ds){
28417 ds.on("beforeload", this.beforeLoad, this);
28418 ds.on("load", this.onLoad, this);
28419 ds.on("loadexception", this.onLoadError, this);
28420 ds.on("remove", this.updateInfo, this);
28421 ds.on("add", this.updateInfo, this);
28432 * @class Roo.bootstrap.MessageBar
28433 * @extends Roo.bootstrap.Component
28434 * Bootstrap MessageBar class
28435 * @cfg {String} html contents of the MessageBar
28436 * @cfg {String} weight (info | success | warning | danger) default info
28437 * @cfg {String} beforeClass insert the bar before the given class
28438 * @cfg {Boolean} closable (true | false) default false
28439 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28442 * Create a new Element
28443 * @param {Object} config The config object
28446 Roo.bootstrap.MessageBar = function(config){
28447 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28450 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28456 beforeClass: 'bootstrap-sticky-wrap',
28458 getAutoCreate : function(){
28462 cls: 'alert alert-dismissable alert-' + this.weight,
28467 html: this.html || ''
28473 cfg.cls += ' alert-messages-fixed';
28487 onRender : function(ct, position)
28489 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28492 var cfg = Roo.apply({}, this.getAutoCreate());
28496 cfg.cls += ' ' + this.cls;
28499 cfg.style = this.style;
28501 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28503 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28506 this.el.select('>button.close').on('click', this.hide, this);
28512 if (!this.rendered) {
28518 this.fireEvent('show', this);
28524 if (!this.rendered) {
28530 this.fireEvent('hide', this);
28533 update : function()
28535 // var e = this.el.dom.firstChild;
28537 // if(this.closable){
28538 // e = e.nextSibling;
28541 // e.data = this.html || '';
28543 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28559 * @class Roo.bootstrap.Graph
28560 * @extends Roo.bootstrap.Component
28561 * Bootstrap Graph class
28565 @cfg {String} graphtype bar | vbar | pie
28566 @cfg {number} g_x coodinator | centre x (pie)
28567 @cfg {number} g_y coodinator | centre y (pie)
28568 @cfg {number} g_r radius (pie)
28569 @cfg {number} g_height height of the chart (respected by all elements in the set)
28570 @cfg {number} g_width width of the chart (respected by all elements in the set)
28571 @cfg {Object} title The title of the chart
28574 -opts (object) options for the chart
28576 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28577 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28579 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.
28580 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28582 o stretch (boolean)
28584 -opts (object) options for the pie
28587 o startAngle (number)
28588 o endAngle (number)
28592 * Create a new Input
28593 * @param {Object} config The config object
28596 Roo.bootstrap.Graph = function(config){
28597 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28603 * The img click event for the img.
28604 * @param {Roo.EventObject} e
28610 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28621 //g_colors: this.colors,
28628 getAutoCreate : function(){
28639 onRender : function(ct,position){
28642 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28644 if (typeof(Raphael) == 'undefined') {
28645 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28649 this.raphael = Raphael(this.el.dom);
28651 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28652 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28653 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28654 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28656 r.text(160, 10, "Single Series Chart").attr(txtattr);
28657 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28658 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28659 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28661 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28662 r.barchart(330, 10, 300, 220, data1);
28663 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28664 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28667 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28668 // r.barchart(30, 30, 560, 250, xdata, {
28669 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28670 // axis : "0 0 1 1",
28671 // axisxlabels : xdata
28672 // //yvalues : cols,
28675 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28677 // this.load(null,xdata,{
28678 // axis : "0 0 1 1",
28679 // axisxlabels : xdata
28684 load : function(graphtype,xdata,opts)
28686 this.raphael.clear();
28688 graphtype = this.graphtype;
28693 var r = this.raphael,
28694 fin = function () {
28695 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28697 fout = function () {
28698 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28700 pfin = function() {
28701 this.sector.stop();
28702 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28705 this.label[0].stop();
28706 this.label[0].attr({ r: 7.5 });
28707 this.label[1].attr({ "font-weight": 800 });
28710 pfout = function() {
28711 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28714 this.label[0].animate({ r: 5 }, 500, "bounce");
28715 this.label[1].attr({ "font-weight": 400 });
28721 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28724 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28727 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28728 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28730 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28737 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28742 setTitle: function(o)
28747 initEvents: function() {
28750 this.el.on('click', this.onClick, this);
28754 onClick : function(e)
28756 Roo.log('img onclick');
28757 this.fireEvent('click', this, e);
28769 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28772 * @class Roo.bootstrap.dash.NumberBox
28773 * @extends Roo.bootstrap.Component
28774 * Bootstrap NumberBox class
28775 * @cfg {String} headline Box headline
28776 * @cfg {String} content Box content
28777 * @cfg {String} icon Box icon
28778 * @cfg {String} footer Footer text
28779 * @cfg {String} fhref Footer href
28782 * Create a new NumberBox
28783 * @param {Object} config The config object
28787 Roo.bootstrap.dash.NumberBox = function(config){
28788 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28792 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28801 getAutoCreate : function(){
28805 cls : 'small-box ',
28813 cls : 'roo-headline',
28814 html : this.headline
28818 cls : 'roo-content',
28819 html : this.content
28833 cls : 'ion ' + this.icon
28842 cls : 'small-box-footer',
28843 href : this.fhref || '#',
28847 cfg.cn.push(footer);
28854 onRender : function(ct,position){
28855 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28862 setHeadline: function (value)
28864 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28867 setFooter: function (value, href)
28869 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28872 this.el.select('a.small-box-footer',true).first().attr('href', href);
28877 setContent: function (value)
28879 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28882 initEvents: function()
28896 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28899 * @class Roo.bootstrap.dash.TabBox
28900 * @extends Roo.bootstrap.Component
28901 * Bootstrap TabBox class
28902 * @cfg {String} title Title of the TabBox
28903 * @cfg {String} icon Icon of the TabBox
28904 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28905 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28908 * Create a new TabBox
28909 * @param {Object} config The config object
28913 Roo.bootstrap.dash.TabBox = function(config){
28914 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28919 * When a pane is added
28920 * @param {Roo.bootstrap.dash.TabPane} pane
28924 * @event activatepane
28925 * When a pane is activated
28926 * @param {Roo.bootstrap.dash.TabPane} pane
28928 "activatepane" : true
28936 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28941 tabScrollable : false,
28943 getChildContainer : function()
28945 return this.el.select('.tab-content', true).first();
28948 getAutoCreate : function(){
28952 cls: 'pull-left header',
28960 cls: 'fa ' + this.icon
28966 cls: 'nav nav-tabs pull-right',
28972 if(this.tabScrollable){
28979 cls: 'nav nav-tabs pull-right',
28990 cls: 'nav-tabs-custom',
28995 cls: 'tab-content no-padding',
29003 initEvents : function()
29005 //Roo.log('add add pane handler');
29006 this.on('addpane', this.onAddPane, this);
29009 * Updates the box title
29010 * @param {String} html to set the title to.
29012 setTitle : function(value)
29014 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29016 onAddPane : function(pane)
29018 this.panes.push(pane);
29019 //Roo.log('addpane');
29021 // tabs are rendere left to right..
29022 if(!this.showtabs){
29026 var ctr = this.el.select('.nav-tabs', true).first();
29029 var existing = ctr.select('.nav-tab',true);
29030 var qty = existing.getCount();;
29033 var tab = ctr.createChild({
29035 cls : 'nav-tab' + (qty ? '' : ' active'),
29043 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29046 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29048 pane.el.addClass('active');
29053 onTabClick : function(ev,un,ob,pane)
29055 //Roo.log('tab - prev default');
29056 ev.preventDefault();
29059 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29060 pane.tab.addClass('active');
29061 //Roo.log(pane.title);
29062 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29063 // technically we should have a deactivate event.. but maybe add later.
29064 // and it should not de-activate the selected tab...
29065 this.fireEvent('activatepane', pane);
29066 pane.el.addClass('active');
29067 pane.fireEvent('activate');
29072 getActivePane : function()
29075 Roo.each(this.panes, function(p) {
29076 if(p.el.hasClass('active')){
29097 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29099 * @class Roo.bootstrap.TabPane
29100 * @extends Roo.bootstrap.Component
29101 * Bootstrap TabPane class
29102 * @cfg {Boolean} active (false | true) Default false
29103 * @cfg {String} title title of panel
29107 * Create a new TabPane
29108 * @param {Object} config The config object
29111 Roo.bootstrap.dash.TabPane = function(config){
29112 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29118 * When a pane is activated
29119 * @param {Roo.bootstrap.dash.TabPane} pane
29126 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29131 // the tabBox that this is attached to.
29134 getAutoCreate : function()
29142 cfg.cls += ' active';
29147 initEvents : function()
29149 //Roo.log('trigger add pane handler');
29150 this.parent().fireEvent('addpane', this)
29154 * Updates the tab title
29155 * @param {String} html to set the title to.
29157 setTitle: function(str)
29163 this.tab.select('a', true).first().dom.innerHTML = str;
29180 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29183 * @class Roo.bootstrap.menu.Menu
29184 * @extends Roo.bootstrap.Component
29185 * Bootstrap Menu class - container for Menu
29186 * @cfg {String} html Text of the menu
29187 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29188 * @cfg {String} icon Font awesome icon
29189 * @cfg {String} pos Menu align to (top | bottom) default bottom
29193 * Create a new Menu
29194 * @param {Object} config The config object
29198 Roo.bootstrap.menu.Menu = function(config){
29199 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29203 * @event beforeshow
29204 * Fires before this menu is displayed
29205 * @param {Roo.bootstrap.menu.Menu} this
29209 * @event beforehide
29210 * Fires before this menu is hidden
29211 * @param {Roo.bootstrap.menu.Menu} this
29216 * Fires after this menu is displayed
29217 * @param {Roo.bootstrap.menu.Menu} this
29222 * Fires after this menu is hidden
29223 * @param {Roo.bootstrap.menu.Menu} this
29228 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29229 * @param {Roo.bootstrap.menu.Menu} this
29230 * @param {Roo.EventObject} e
29237 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29241 weight : 'default',
29246 getChildContainer : function() {
29247 if(this.isSubMenu){
29251 return this.el.select('ul.dropdown-menu', true).first();
29254 getAutoCreate : function()
29259 cls : 'roo-menu-text',
29267 cls : 'fa ' + this.icon
29278 cls : 'dropdown-button btn btn-' + this.weight,
29283 cls : 'dropdown-toggle btn btn-' + this.weight,
29293 cls : 'dropdown-menu'
29299 if(this.pos == 'top'){
29300 cfg.cls += ' dropup';
29303 if(this.isSubMenu){
29306 cls : 'dropdown-menu'
29313 onRender : function(ct, position)
29315 this.isSubMenu = ct.hasClass('dropdown-submenu');
29317 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29320 initEvents : function()
29322 if(this.isSubMenu){
29326 this.hidden = true;
29328 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29329 this.triggerEl.on('click', this.onTriggerPress, this);
29331 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29332 this.buttonEl.on('click', this.onClick, this);
29338 if(this.isSubMenu){
29342 return this.el.select('ul.dropdown-menu', true).first();
29345 onClick : function(e)
29347 this.fireEvent("click", this, e);
29350 onTriggerPress : function(e)
29352 if (this.isVisible()) {
29359 isVisible : function(){
29360 return !this.hidden;
29365 this.fireEvent("beforeshow", this);
29367 this.hidden = false;
29368 this.el.addClass('open');
29370 Roo.get(document).on("mouseup", this.onMouseUp, this);
29372 this.fireEvent("show", this);
29379 this.fireEvent("beforehide", this);
29381 this.hidden = true;
29382 this.el.removeClass('open');
29384 Roo.get(document).un("mouseup", this.onMouseUp);
29386 this.fireEvent("hide", this);
29389 onMouseUp : function()
29403 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29406 * @class Roo.bootstrap.menu.Item
29407 * @extends Roo.bootstrap.Component
29408 * Bootstrap MenuItem class
29409 * @cfg {Boolean} submenu (true | false) default false
29410 * @cfg {String} html text of the item
29411 * @cfg {String} href the link
29412 * @cfg {Boolean} disable (true | false) default false
29413 * @cfg {Boolean} preventDefault (true | false) default true
29414 * @cfg {String} icon Font awesome icon
29415 * @cfg {String} pos Submenu align to (left | right) default right
29419 * Create a new Item
29420 * @param {Object} config The config object
29424 Roo.bootstrap.menu.Item = function(config){
29425 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29429 * Fires when the mouse is hovering over this menu
29430 * @param {Roo.bootstrap.menu.Item} this
29431 * @param {Roo.EventObject} e
29436 * Fires when the mouse exits this menu
29437 * @param {Roo.bootstrap.menu.Item} this
29438 * @param {Roo.EventObject} e
29444 * The raw click event for the entire grid.
29445 * @param {Roo.EventObject} e
29451 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29456 preventDefault: true,
29461 getAutoCreate : function()
29466 cls : 'roo-menu-item-text',
29474 cls : 'fa ' + this.icon
29483 href : this.href || '#',
29490 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29494 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29496 if(this.pos == 'left'){
29497 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29504 initEvents : function()
29506 this.el.on('mouseover', this.onMouseOver, this);
29507 this.el.on('mouseout', this.onMouseOut, this);
29509 this.el.select('a', true).first().on('click', this.onClick, this);
29513 onClick : function(e)
29515 if(this.preventDefault){
29516 e.preventDefault();
29519 this.fireEvent("click", this, e);
29522 onMouseOver : function(e)
29524 if(this.submenu && this.pos == 'left'){
29525 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29528 this.fireEvent("mouseover", this, e);
29531 onMouseOut : function(e)
29533 this.fireEvent("mouseout", this, e);
29545 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29548 * @class Roo.bootstrap.menu.Separator
29549 * @extends Roo.bootstrap.Component
29550 * Bootstrap Separator class
29553 * Create a new Separator
29554 * @param {Object} config The config object
29558 Roo.bootstrap.menu.Separator = function(config){
29559 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29562 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29564 getAutoCreate : function(){
29567 cls: 'dropdown-divider divider'
29585 * @class Roo.bootstrap.Tooltip
29586 * Bootstrap Tooltip class
29587 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29588 * to determine which dom element triggers the tooltip.
29590 * It needs to add support for additional attributes like tooltip-position
29593 * Create a new Toolti
29594 * @param {Object} config The config object
29597 Roo.bootstrap.Tooltip = function(config){
29598 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29600 this.alignment = Roo.bootstrap.Tooltip.alignment;
29602 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29603 this.alignment = config.alignment;
29608 Roo.apply(Roo.bootstrap.Tooltip, {
29610 * @function init initialize tooltip monitoring.
29614 currentTip : false,
29615 currentRegion : false,
29621 Roo.get(document).on('mouseover', this.enter ,this);
29622 Roo.get(document).on('mouseout', this.leave, this);
29625 this.currentTip = new Roo.bootstrap.Tooltip();
29628 enter : function(ev)
29630 var dom = ev.getTarget();
29632 //Roo.log(['enter',dom]);
29633 var el = Roo.fly(dom);
29634 if (this.currentEl) {
29636 //Roo.log(this.currentEl);
29637 //Roo.log(this.currentEl.contains(dom));
29638 if (this.currentEl == el) {
29641 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29647 if (this.currentTip.el) {
29648 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29652 if(!el || el.dom == document){
29658 if (!el.attr('tooltip')) {
29659 pel = el.findParent("[tooltip]");
29661 bindEl = Roo.get(pel);
29667 // you can not look for children, as if el is the body.. then everythign is the child..
29668 if (!pel && !el.attr('tooltip')) { //
29669 if (!el.select("[tooltip]").elements.length) {
29672 // is the mouse over this child...?
29673 bindEl = el.select("[tooltip]").first();
29674 var xy = ev.getXY();
29675 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29676 //Roo.log("not in region.");
29679 //Roo.log("child element over..");
29682 this.currentEl = el;
29683 this.currentTip.bind(bindEl);
29684 this.currentRegion = Roo.lib.Region.getRegion(dom);
29685 this.currentTip.enter();
29688 leave : function(ev)
29690 var dom = ev.getTarget();
29691 //Roo.log(['leave',dom]);
29692 if (!this.currentEl) {
29697 if (dom != this.currentEl.dom) {
29700 var xy = ev.getXY();
29701 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29704 // only activate leave if mouse cursor is outside... bounding box..
29709 if (this.currentTip) {
29710 this.currentTip.leave();
29712 //Roo.log('clear currentEl');
29713 this.currentEl = false;
29718 'left' : ['r-l', [-2,0], 'right'],
29719 'right' : ['l-r', [2,0], 'left'],
29720 'bottom' : ['t-b', [0,2], 'top'],
29721 'top' : [ 'b-t', [0,-2], 'bottom']
29727 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29732 delay : null, // can be { show : 300 , hide: 500}
29736 hoverState : null, //???
29738 placement : 'bottom',
29742 getAutoCreate : function(){
29749 cls : 'tooltip-arrow arrow'
29752 cls : 'tooltip-inner'
29759 bind : function(el)
29764 initEvents : function()
29766 this.arrowEl = this.el.select('.arrow', true).first();
29767 this.innerEl = this.el.select('.tooltip-inner', true).first();
29770 enter : function () {
29772 if (this.timeout != null) {
29773 clearTimeout(this.timeout);
29776 this.hoverState = 'in';
29777 //Roo.log("enter - show");
29778 if (!this.delay || !this.delay.show) {
29783 this.timeout = setTimeout(function () {
29784 if (_t.hoverState == 'in') {
29787 }, this.delay.show);
29791 clearTimeout(this.timeout);
29793 this.hoverState = 'out';
29794 if (!this.delay || !this.delay.hide) {
29800 this.timeout = setTimeout(function () {
29801 //Roo.log("leave - timeout");
29803 if (_t.hoverState == 'out') {
29805 Roo.bootstrap.Tooltip.currentEl = false;
29810 show : function (msg)
29813 this.render(document.body);
29816 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29818 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29820 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29822 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29823 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29825 var placement = typeof this.placement == 'function' ?
29826 this.placement.call(this, this.el, on_el) :
29829 var autoToken = /\s?auto?\s?/i;
29830 var autoPlace = autoToken.test(placement);
29832 placement = placement.replace(autoToken, '') || 'top';
29836 //this.el.setXY([0,0]);
29838 //this.el.dom.style.display='block';
29840 //this.el.appendTo(on_el);
29842 var p = this.getPosition();
29843 var box = this.el.getBox();
29849 var align = this.alignment[placement];
29851 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29853 if(placement == 'top' || placement == 'bottom'){
29855 placement = 'right';
29858 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29859 placement = 'left';
29862 var scroll = Roo.select('body', true).first().getScroll();
29864 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29868 align = this.alignment[placement];
29870 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29874 var elems = document.getElementsByTagName('div');
29875 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29876 for (var i = 0; i < elems.length; i++) {
29877 var zindex = Number.parseInt(
29878 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29881 if (zindex > highest) {
29888 this.el.dom.style.zIndex = highest;
29890 this.el.alignTo(this.bindEl, align[0],align[1]);
29891 //var arrow = this.el.select('.arrow',true).first();
29892 //arrow.set(align[2],
29894 this.el.addClass(placement);
29895 this.el.addClass("bs-tooltip-"+ placement);
29897 this.el.addClass('in fade show');
29899 this.hoverState = null;
29901 if (this.el.hasClass('fade')) {
29916 //this.el.setXY([0,0]);
29917 this.el.removeClass(['show', 'in']);
29933 * @class Roo.bootstrap.LocationPicker
29934 * @extends Roo.bootstrap.Component
29935 * Bootstrap LocationPicker class
29936 * @cfg {Number} latitude Position when init default 0
29937 * @cfg {Number} longitude Position when init default 0
29938 * @cfg {Number} zoom default 15
29939 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29940 * @cfg {Boolean} mapTypeControl default false
29941 * @cfg {Boolean} disableDoubleClickZoom default false
29942 * @cfg {Boolean} scrollwheel default true
29943 * @cfg {Boolean} streetViewControl default false
29944 * @cfg {Number} radius default 0
29945 * @cfg {String} locationName
29946 * @cfg {Boolean} draggable default true
29947 * @cfg {Boolean} enableAutocomplete default false
29948 * @cfg {Boolean} enableReverseGeocode default true
29949 * @cfg {String} markerTitle
29952 * Create a new LocationPicker
29953 * @param {Object} config The config object
29957 Roo.bootstrap.LocationPicker = function(config){
29959 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29964 * Fires when the picker initialized.
29965 * @param {Roo.bootstrap.LocationPicker} this
29966 * @param {Google Location} location
29970 * @event positionchanged
29971 * Fires when the picker position changed.
29972 * @param {Roo.bootstrap.LocationPicker} this
29973 * @param {Google Location} location
29975 positionchanged : true,
29978 * Fires when the map resize.
29979 * @param {Roo.bootstrap.LocationPicker} this
29984 * Fires when the map show.
29985 * @param {Roo.bootstrap.LocationPicker} this
29990 * Fires when the map hide.
29991 * @param {Roo.bootstrap.LocationPicker} this
29996 * Fires when click the map.
29997 * @param {Roo.bootstrap.LocationPicker} this
29998 * @param {Map event} e
30002 * @event mapRightClick
30003 * Fires when right click the map.
30004 * @param {Roo.bootstrap.LocationPicker} this
30005 * @param {Map event} e
30007 mapRightClick : true,
30009 * @event markerClick
30010 * Fires when click the marker.
30011 * @param {Roo.bootstrap.LocationPicker} this
30012 * @param {Map event} e
30014 markerClick : true,
30016 * @event markerRightClick
30017 * Fires when right click the marker.
30018 * @param {Roo.bootstrap.LocationPicker} this
30019 * @param {Map event} e
30021 markerRightClick : true,
30023 * @event OverlayViewDraw
30024 * Fires when OverlayView Draw
30025 * @param {Roo.bootstrap.LocationPicker} this
30027 OverlayViewDraw : true,
30029 * @event OverlayViewOnAdd
30030 * Fires when OverlayView Draw
30031 * @param {Roo.bootstrap.LocationPicker} this
30033 OverlayViewOnAdd : true,
30035 * @event OverlayViewOnRemove
30036 * Fires when OverlayView Draw
30037 * @param {Roo.bootstrap.LocationPicker} this
30039 OverlayViewOnRemove : true,
30041 * @event OverlayViewShow
30042 * Fires when OverlayView Draw
30043 * @param {Roo.bootstrap.LocationPicker} this
30044 * @param {Pixel} cpx
30046 OverlayViewShow : true,
30048 * @event OverlayViewHide
30049 * Fires when OverlayView Draw
30050 * @param {Roo.bootstrap.LocationPicker} this
30052 OverlayViewHide : true,
30054 * @event loadexception
30055 * Fires when load google lib failed.
30056 * @param {Roo.bootstrap.LocationPicker} this
30058 loadexception : true
30063 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30065 gMapContext: false,
30071 mapTypeControl: false,
30072 disableDoubleClickZoom: false,
30074 streetViewControl: false,
30078 enableAutocomplete: false,
30079 enableReverseGeocode: true,
30082 getAutoCreate: function()
30087 cls: 'roo-location-picker'
30093 initEvents: function(ct, position)
30095 if(!this.el.getWidth() || this.isApplied()){
30099 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30104 initial: function()
30106 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30107 this.fireEvent('loadexception', this);
30111 if(!this.mapTypeId){
30112 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30115 this.gMapContext = this.GMapContext();
30117 this.initOverlayView();
30119 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30123 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30124 _this.setPosition(_this.gMapContext.marker.position);
30127 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30128 _this.fireEvent('mapClick', this, event);
30132 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30133 _this.fireEvent('mapRightClick', this, event);
30137 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30138 _this.fireEvent('markerClick', this, event);
30142 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30143 _this.fireEvent('markerRightClick', this, event);
30147 this.setPosition(this.gMapContext.location);
30149 this.fireEvent('initial', this, this.gMapContext.location);
30152 initOverlayView: function()
30156 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30160 _this.fireEvent('OverlayViewDraw', _this);
30165 _this.fireEvent('OverlayViewOnAdd', _this);
30168 onRemove: function()
30170 _this.fireEvent('OverlayViewOnRemove', _this);
30173 show: function(cpx)
30175 _this.fireEvent('OverlayViewShow', _this, cpx);
30180 _this.fireEvent('OverlayViewHide', _this);
30186 fromLatLngToContainerPixel: function(event)
30188 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30191 isApplied: function()
30193 return this.getGmapContext() == false ? false : true;
30196 getGmapContext: function()
30198 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30201 GMapContext: function()
30203 var position = new google.maps.LatLng(this.latitude, this.longitude);
30205 var _map = new google.maps.Map(this.el.dom, {
30208 mapTypeId: this.mapTypeId,
30209 mapTypeControl: this.mapTypeControl,
30210 disableDoubleClickZoom: this.disableDoubleClickZoom,
30211 scrollwheel: this.scrollwheel,
30212 streetViewControl: this.streetViewControl,
30213 locationName: this.locationName,
30214 draggable: this.draggable,
30215 enableAutocomplete: this.enableAutocomplete,
30216 enableReverseGeocode: this.enableReverseGeocode
30219 var _marker = new google.maps.Marker({
30220 position: position,
30222 title: this.markerTitle,
30223 draggable: this.draggable
30230 location: position,
30231 radius: this.radius,
30232 locationName: this.locationName,
30233 addressComponents: {
30234 formatted_address: null,
30235 addressLine1: null,
30236 addressLine2: null,
30238 streetNumber: null,
30242 stateOrProvince: null
30245 domContainer: this.el.dom,
30246 geodecoder: new google.maps.Geocoder()
30250 drawCircle: function(center, radius, options)
30252 if (this.gMapContext.circle != null) {
30253 this.gMapContext.circle.setMap(null);
30257 options = Roo.apply({}, options, {
30258 strokeColor: "#0000FF",
30259 strokeOpacity: .35,
30261 fillColor: "#0000FF",
30265 options.map = this.gMapContext.map;
30266 options.radius = radius;
30267 options.center = center;
30268 this.gMapContext.circle = new google.maps.Circle(options);
30269 return this.gMapContext.circle;
30275 setPosition: function(location)
30277 this.gMapContext.location = location;
30278 this.gMapContext.marker.setPosition(location);
30279 this.gMapContext.map.panTo(location);
30280 this.drawCircle(location, this.gMapContext.radius, {});
30284 if (this.gMapContext.settings.enableReverseGeocode) {
30285 this.gMapContext.geodecoder.geocode({
30286 latLng: this.gMapContext.location
30287 }, function(results, status) {
30289 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30290 _this.gMapContext.locationName = results[0].formatted_address;
30291 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30293 _this.fireEvent('positionchanged', this, location);
30300 this.fireEvent('positionchanged', this, location);
30305 google.maps.event.trigger(this.gMapContext.map, "resize");
30307 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30309 this.fireEvent('resize', this);
30312 setPositionByLatLng: function(latitude, longitude)
30314 this.setPosition(new google.maps.LatLng(latitude, longitude));
30317 getCurrentPosition: function()
30320 latitude: this.gMapContext.location.lat(),
30321 longitude: this.gMapContext.location.lng()
30325 getAddressName: function()
30327 return this.gMapContext.locationName;
30330 getAddressComponents: function()
30332 return this.gMapContext.addressComponents;
30335 address_component_from_google_geocode: function(address_components)
30339 for (var i = 0; i < address_components.length; i++) {
30340 var component = address_components[i];
30341 if (component.types.indexOf("postal_code") >= 0) {
30342 result.postalCode = component.short_name;
30343 } else if (component.types.indexOf("street_number") >= 0) {
30344 result.streetNumber = component.short_name;
30345 } else if (component.types.indexOf("route") >= 0) {
30346 result.streetName = component.short_name;
30347 } else if (component.types.indexOf("neighborhood") >= 0) {
30348 result.city = component.short_name;
30349 } else if (component.types.indexOf("locality") >= 0) {
30350 result.city = component.short_name;
30351 } else if (component.types.indexOf("sublocality") >= 0) {
30352 result.district = component.short_name;
30353 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30354 result.stateOrProvince = component.short_name;
30355 } else if (component.types.indexOf("country") >= 0) {
30356 result.country = component.short_name;
30360 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30361 result.addressLine2 = "";
30365 setZoomLevel: function(zoom)
30367 this.gMapContext.map.setZoom(zoom);
30380 this.fireEvent('show', this);
30391 this.fireEvent('hide', this);
30396 Roo.apply(Roo.bootstrap.LocationPicker, {
30398 OverlayView : function(map, options)
30400 options = options || {};
30407 * @class Roo.bootstrap.Alert
30408 * @extends Roo.bootstrap.Component
30409 * Bootstrap Alert class - shows an alert area box
30411 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30412 Enter a valid email address
30415 * @cfg {String} title The title of alert
30416 * @cfg {String} html The content of alert
30417 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30418 * @cfg {String} fa font-awesomeicon
30419 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30420 * @cfg {Boolean} close true to show a x closer
30424 * Create a new alert
30425 * @param {Object} config The config object
30429 Roo.bootstrap.Alert = function(config){
30430 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30434 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30440 faicon: false, // BC
30444 getAutoCreate : function()
30456 style : this.close ? '' : 'display:none'
30460 cls : 'roo-alert-icon'
30465 cls : 'roo-alert-title',
30470 cls : 'roo-alert-text',
30477 cfg.cn[0].cls += ' fa ' + this.faicon;
30480 cfg.cn[0].cls += ' fa ' + this.fa;
30484 cfg.cls += ' alert-' + this.weight;
30490 initEvents: function()
30492 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30493 this.titleEl = this.el.select('.roo-alert-title',true).first();
30494 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30495 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30496 if (this.seconds > 0) {
30497 this.hide.defer(this.seconds, this);
30501 * Set the Title Message HTML
30502 * @param {String} html
30504 setTitle : function(str)
30506 this.titleEl.dom.innerHTML = str;
30510 * Set the Body Message HTML
30511 * @param {String} html
30513 setHtml : function(str)
30515 this.htmlEl.dom.innerHTML = str;
30518 * Set the Weight of the alert
30519 * @param {String} (success|info|warning|danger) weight
30522 setWeight : function(weight)
30525 this.el.removeClass('alert-' + this.weight);
30528 this.weight = weight;
30530 this.el.addClass('alert-' + this.weight);
30533 * Set the Icon of the alert
30534 * @param {String} see fontawsome names (name without the 'fa-' bit)
30536 setIcon : function(icon)
30539 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30542 this.faicon = icon;
30544 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30569 * @class Roo.bootstrap.UploadCropbox
30570 * @extends Roo.bootstrap.Component
30571 * Bootstrap UploadCropbox class
30572 * @cfg {String} emptyText show when image has been loaded
30573 * @cfg {String} rotateNotify show when image too small to rotate
30574 * @cfg {Number} errorTimeout default 3000
30575 * @cfg {Number} minWidth default 300
30576 * @cfg {Number} minHeight default 300
30577 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30578 * @cfg {Boolean} isDocument (true|false) default false
30579 * @cfg {String} url action url
30580 * @cfg {String} paramName default 'imageUpload'
30581 * @cfg {String} method default POST
30582 * @cfg {Boolean} loadMask (true|false) default true
30583 * @cfg {Boolean} loadingText default 'Loading...'
30586 * Create a new UploadCropbox
30587 * @param {Object} config The config object
30590 Roo.bootstrap.UploadCropbox = function(config){
30591 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30595 * @event beforeselectfile
30596 * Fire before select file
30597 * @param {Roo.bootstrap.UploadCropbox} this
30599 "beforeselectfile" : true,
30602 * Fire after initEvent
30603 * @param {Roo.bootstrap.UploadCropbox} this
30608 * Fire after initEvent
30609 * @param {Roo.bootstrap.UploadCropbox} this
30610 * @param {String} data
30615 * Fire when preparing the file data
30616 * @param {Roo.bootstrap.UploadCropbox} this
30617 * @param {Object} file
30622 * Fire when get exception
30623 * @param {Roo.bootstrap.UploadCropbox} this
30624 * @param {XMLHttpRequest} xhr
30626 "exception" : true,
30628 * @event beforeloadcanvas
30629 * Fire before load the canvas
30630 * @param {Roo.bootstrap.UploadCropbox} this
30631 * @param {String} src
30633 "beforeloadcanvas" : true,
30636 * Fire when trash image
30637 * @param {Roo.bootstrap.UploadCropbox} this
30642 * Fire when download the image
30643 * @param {Roo.bootstrap.UploadCropbox} this
30647 * @event footerbuttonclick
30648 * Fire when footerbuttonclick
30649 * @param {Roo.bootstrap.UploadCropbox} this
30650 * @param {String} type
30652 "footerbuttonclick" : true,
30656 * @param {Roo.bootstrap.UploadCropbox} this
30661 * Fire when rotate the image
30662 * @param {Roo.bootstrap.UploadCropbox} this
30663 * @param {String} pos
30668 * Fire when inspect the file
30669 * @param {Roo.bootstrap.UploadCropbox} this
30670 * @param {Object} file
30675 * Fire when xhr upload the file
30676 * @param {Roo.bootstrap.UploadCropbox} this
30677 * @param {Object} data
30682 * Fire when arrange the file data
30683 * @param {Roo.bootstrap.UploadCropbox} this
30684 * @param {Object} formData
30689 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30692 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30694 emptyText : 'Click to upload image',
30695 rotateNotify : 'Image is too small to rotate',
30696 errorTimeout : 3000,
30710 cropType : 'image/jpeg',
30712 canvasLoaded : false,
30713 isDocument : false,
30715 paramName : 'imageUpload',
30717 loadingText : 'Loading...',
30720 getAutoCreate : function()
30724 cls : 'roo-upload-cropbox',
30728 cls : 'roo-upload-cropbox-selector',
30733 cls : 'roo-upload-cropbox-body',
30734 style : 'cursor:pointer',
30738 cls : 'roo-upload-cropbox-preview'
30742 cls : 'roo-upload-cropbox-thumb'
30746 cls : 'roo-upload-cropbox-empty-notify',
30747 html : this.emptyText
30751 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30752 html : this.rotateNotify
30758 cls : 'roo-upload-cropbox-footer',
30761 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30771 onRender : function(ct, position)
30773 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30775 if (this.buttons.length) {
30777 Roo.each(this.buttons, function(bb) {
30779 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30781 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30787 this.maskEl = this.el;
30791 initEvents : function()
30793 this.urlAPI = (window.createObjectURL && window) ||
30794 (window.URL && URL.revokeObjectURL && URL) ||
30795 (window.webkitURL && webkitURL);
30797 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30798 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30800 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30801 this.selectorEl.hide();
30803 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30804 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30806 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30807 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30808 this.thumbEl.hide();
30810 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30811 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30813 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30814 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30815 this.errorEl.hide();
30817 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30818 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30819 this.footerEl.hide();
30821 this.setThumbBoxSize();
30827 this.fireEvent('initial', this);
30834 window.addEventListener("resize", function() { _this.resize(); } );
30836 this.bodyEl.on('click', this.beforeSelectFile, this);
30839 this.bodyEl.on('touchstart', this.onTouchStart, this);
30840 this.bodyEl.on('touchmove', this.onTouchMove, this);
30841 this.bodyEl.on('touchend', this.onTouchEnd, this);
30845 this.bodyEl.on('mousedown', this.onMouseDown, this);
30846 this.bodyEl.on('mousemove', this.onMouseMove, this);
30847 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30848 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30849 Roo.get(document).on('mouseup', this.onMouseUp, this);
30852 this.selectorEl.on('change', this.onFileSelected, this);
30858 this.baseScale = 1;
30860 this.baseRotate = 1;
30861 this.dragable = false;
30862 this.pinching = false;
30865 this.cropData = false;
30866 this.notifyEl.dom.innerHTML = this.emptyText;
30868 this.selectorEl.dom.value = '';
30872 resize : function()
30874 if(this.fireEvent('resize', this) != false){
30875 this.setThumbBoxPosition();
30876 this.setCanvasPosition();
30880 onFooterButtonClick : function(e, el, o, type)
30883 case 'rotate-left' :
30884 this.onRotateLeft(e);
30886 case 'rotate-right' :
30887 this.onRotateRight(e);
30890 this.beforeSelectFile(e);
30905 this.fireEvent('footerbuttonclick', this, type);
30908 beforeSelectFile : function(e)
30910 e.preventDefault();
30912 if(this.fireEvent('beforeselectfile', this) != false){
30913 this.selectorEl.dom.click();
30917 onFileSelected : function(e)
30919 e.preventDefault();
30921 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30925 var file = this.selectorEl.dom.files[0];
30927 if(this.fireEvent('inspect', this, file) != false){
30928 this.prepare(file);
30933 trash : function(e)
30935 this.fireEvent('trash', this);
30938 download : function(e)
30940 this.fireEvent('download', this);
30943 loadCanvas : function(src)
30945 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30949 this.imageEl = document.createElement('img');
30953 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30955 this.imageEl.src = src;
30959 onLoadCanvas : function()
30961 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30962 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30964 this.bodyEl.un('click', this.beforeSelectFile, this);
30966 this.notifyEl.hide();
30967 this.thumbEl.show();
30968 this.footerEl.show();
30970 this.baseRotateLevel();
30972 if(this.isDocument){
30973 this.setThumbBoxSize();
30976 this.setThumbBoxPosition();
30978 this.baseScaleLevel();
30984 this.canvasLoaded = true;
30987 this.maskEl.unmask();
30992 setCanvasPosition : function()
30994 if(!this.canvasEl){
30998 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30999 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31001 this.previewEl.setLeft(pw);
31002 this.previewEl.setTop(ph);
31006 onMouseDown : function(e)
31010 this.dragable = true;
31011 this.pinching = false;
31013 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31014 this.dragable = false;
31018 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31019 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31023 onMouseMove : function(e)
31027 if(!this.canvasLoaded){
31031 if (!this.dragable){
31035 var minX = Math.ceil(this.thumbEl.getLeft(true));
31036 var minY = Math.ceil(this.thumbEl.getTop(true));
31038 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31039 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31041 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31042 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31044 x = x - this.mouseX;
31045 y = y - this.mouseY;
31047 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31048 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31050 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31051 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31053 this.previewEl.setLeft(bgX);
31054 this.previewEl.setTop(bgY);
31056 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31057 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31060 onMouseUp : function(e)
31064 this.dragable = false;
31067 onMouseWheel : function(e)
31071 this.startScale = this.scale;
31073 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31075 if(!this.zoomable()){
31076 this.scale = this.startScale;
31085 zoomable : function()
31087 var minScale = this.thumbEl.getWidth() / this.minWidth;
31089 if(this.minWidth < this.minHeight){
31090 minScale = this.thumbEl.getHeight() / this.minHeight;
31093 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31094 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31098 (this.rotate == 0 || this.rotate == 180) &&
31100 width > this.imageEl.OriginWidth ||
31101 height > this.imageEl.OriginHeight ||
31102 (width < this.minWidth && height < this.minHeight)
31110 (this.rotate == 90 || this.rotate == 270) &&
31112 width > this.imageEl.OriginWidth ||
31113 height > this.imageEl.OriginHeight ||
31114 (width < this.minHeight && height < this.minWidth)
31121 !this.isDocument &&
31122 (this.rotate == 0 || this.rotate == 180) &&
31124 width < this.minWidth ||
31125 width > this.imageEl.OriginWidth ||
31126 height < this.minHeight ||
31127 height > this.imageEl.OriginHeight
31134 !this.isDocument &&
31135 (this.rotate == 90 || this.rotate == 270) &&
31137 width < this.minHeight ||
31138 width > this.imageEl.OriginWidth ||
31139 height < this.minWidth ||
31140 height > this.imageEl.OriginHeight
31150 onRotateLeft : function(e)
31152 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31154 var minScale = this.thumbEl.getWidth() / this.minWidth;
31156 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31157 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31159 this.startScale = this.scale;
31161 while (this.getScaleLevel() < minScale){
31163 this.scale = this.scale + 1;
31165 if(!this.zoomable()){
31170 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31171 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31176 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31183 this.scale = this.startScale;
31185 this.onRotateFail();
31190 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31192 if(this.isDocument){
31193 this.setThumbBoxSize();
31194 this.setThumbBoxPosition();
31195 this.setCanvasPosition();
31200 this.fireEvent('rotate', this, 'left');
31204 onRotateRight : function(e)
31206 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31208 var minScale = this.thumbEl.getWidth() / this.minWidth;
31210 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31211 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31213 this.startScale = this.scale;
31215 while (this.getScaleLevel() < minScale){
31217 this.scale = this.scale + 1;
31219 if(!this.zoomable()){
31224 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31225 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31230 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31237 this.scale = this.startScale;
31239 this.onRotateFail();
31244 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31246 if(this.isDocument){
31247 this.setThumbBoxSize();
31248 this.setThumbBoxPosition();
31249 this.setCanvasPosition();
31254 this.fireEvent('rotate', this, 'right');
31257 onRotateFail : function()
31259 this.errorEl.show(true);
31263 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31268 this.previewEl.dom.innerHTML = '';
31270 var canvasEl = document.createElement("canvas");
31272 var contextEl = canvasEl.getContext("2d");
31274 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31275 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31276 var center = this.imageEl.OriginWidth / 2;
31278 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31279 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31280 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31281 center = this.imageEl.OriginHeight / 2;
31284 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31286 contextEl.translate(center, center);
31287 contextEl.rotate(this.rotate * Math.PI / 180);
31289 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31291 this.canvasEl = document.createElement("canvas");
31293 this.contextEl = this.canvasEl.getContext("2d");
31295 switch (this.rotate) {
31298 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31299 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31301 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31306 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31307 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31309 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31310 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);
31314 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31319 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31320 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31322 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31323 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);
31327 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);
31332 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31333 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31335 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31336 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31340 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);
31347 this.previewEl.appendChild(this.canvasEl);
31349 this.setCanvasPosition();
31354 if(!this.canvasLoaded){
31358 var imageCanvas = document.createElement("canvas");
31360 var imageContext = imageCanvas.getContext("2d");
31362 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31363 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31365 var center = imageCanvas.width / 2;
31367 imageContext.translate(center, center);
31369 imageContext.rotate(this.rotate * Math.PI / 180);
31371 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31373 var canvas = document.createElement("canvas");
31375 var context = canvas.getContext("2d");
31377 canvas.width = this.minWidth;
31378 canvas.height = this.minHeight;
31380 switch (this.rotate) {
31383 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31384 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31386 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31387 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31389 var targetWidth = this.minWidth - 2 * x;
31390 var targetHeight = this.minHeight - 2 * y;
31394 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31395 scale = targetWidth / width;
31398 if(x > 0 && y == 0){
31399 scale = targetHeight / height;
31402 if(x > 0 && y > 0){
31403 scale = targetWidth / width;
31405 if(width < height){
31406 scale = targetHeight / height;
31410 context.scale(scale, scale);
31412 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31413 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31415 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31416 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31418 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31423 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31424 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31426 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31427 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31429 var targetWidth = this.minWidth - 2 * x;
31430 var targetHeight = this.minHeight - 2 * y;
31434 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31435 scale = targetWidth / width;
31438 if(x > 0 && y == 0){
31439 scale = targetHeight / height;
31442 if(x > 0 && y > 0){
31443 scale = targetWidth / width;
31445 if(width < height){
31446 scale = targetHeight / height;
31450 context.scale(scale, scale);
31452 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31453 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31455 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31456 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31458 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31460 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31465 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31466 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31468 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31469 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31471 var targetWidth = this.minWidth - 2 * x;
31472 var targetHeight = this.minHeight - 2 * y;
31476 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31477 scale = targetWidth / width;
31480 if(x > 0 && y == 0){
31481 scale = targetHeight / height;
31484 if(x > 0 && y > 0){
31485 scale = targetWidth / width;
31487 if(width < height){
31488 scale = targetHeight / height;
31492 context.scale(scale, scale);
31494 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31495 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31497 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31498 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31500 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31501 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31503 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31508 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31509 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31511 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31512 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31514 var targetWidth = this.minWidth - 2 * x;
31515 var targetHeight = this.minHeight - 2 * y;
31519 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31520 scale = targetWidth / width;
31523 if(x > 0 && y == 0){
31524 scale = targetHeight / height;
31527 if(x > 0 && y > 0){
31528 scale = targetWidth / width;
31530 if(width < height){
31531 scale = targetHeight / height;
31535 context.scale(scale, scale);
31537 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31538 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31540 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31541 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31543 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31545 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31552 this.cropData = canvas.toDataURL(this.cropType);
31554 if(this.fireEvent('crop', this, this.cropData) !== false){
31555 this.process(this.file, this.cropData);
31562 setThumbBoxSize : function()
31566 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31567 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31568 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31570 this.minWidth = width;
31571 this.minHeight = height;
31573 if(this.rotate == 90 || this.rotate == 270){
31574 this.minWidth = height;
31575 this.minHeight = width;
31580 width = Math.ceil(this.minWidth * height / this.minHeight);
31582 if(this.minWidth > this.minHeight){
31584 height = Math.ceil(this.minHeight * width / this.minWidth);
31587 this.thumbEl.setStyle({
31588 width : width + 'px',
31589 height : height + 'px'
31596 setThumbBoxPosition : function()
31598 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31599 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31601 this.thumbEl.setLeft(x);
31602 this.thumbEl.setTop(y);
31606 baseRotateLevel : function()
31608 this.baseRotate = 1;
31611 typeof(this.exif) != 'undefined' &&
31612 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31613 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31615 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31618 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31622 baseScaleLevel : function()
31626 if(this.isDocument){
31628 if(this.baseRotate == 6 || this.baseRotate == 8){
31630 height = this.thumbEl.getHeight();
31631 this.baseScale = height / this.imageEl.OriginWidth;
31633 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31634 width = this.thumbEl.getWidth();
31635 this.baseScale = width / this.imageEl.OriginHeight;
31641 height = this.thumbEl.getHeight();
31642 this.baseScale = height / this.imageEl.OriginHeight;
31644 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31645 width = this.thumbEl.getWidth();
31646 this.baseScale = width / this.imageEl.OriginWidth;
31652 if(this.baseRotate == 6 || this.baseRotate == 8){
31654 width = this.thumbEl.getHeight();
31655 this.baseScale = width / this.imageEl.OriginHeight;
31657 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31658 height = this.thumbEl.getWidth();
31659 this.baseScale = height / this.imageEl.OriginHeight;
31662 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31663 height = this.thumbEl.getWidth();
31664 this.baseScale = height / this.imageEl.OriginHeight;
31666 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31667 width = this.thumbEl.getHeight();
31668 this.baseScale = width / this.imageEl.OriginWidth;
31675 width = this.thumbEl.getWidth();
31676 this.baseScale = width / this.imageEl.OriginWidth;
31678 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31679 height = this.thumbEl.getHeight();
31680 this.baseScale = height / this.imageEl.OriginHeight;
31683 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31685 height = this.thumbEl.getHeight();
31686 this.baseScale = height / this.imageEl.OriginHeight;
31688 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31689 width = this.thumbEl.getWidth();
31690 this.baseScale = width / this.imageEl.OriginWidth;
31698 getScaleLevel : function()
31700 return this.baseScale * Math.pow(1.1, this.scale);
31703 onTouchStart : function(e)
31705 if(!this.canvasLoaded){
31706 this.beforeSelectFile(e);
31710 var touches = e.browserEvent.touches;
31716 if(touches.length == 1){
31717 this.onMouseDown(e);
31721 if(touches.length != 2){
31727 for(var i = 0, finger; finger = touches[i]; i++){
31728 coords.push(finger.pageX, finger.pageY);
31731 var x = Math.pow(coords[0] - coords[2], 2);
31732 var y = Math.pow(coords[1] - coords[3], 2);
31734 this.startDistance = Math.sqrt(x + y);
31736 this.startScale = this.scale;
31738 this.pinching = true;
31739 this.dragable = false;
31743 onTouchMove : function(e)
31745 if(!this.pinching && !this.dragable){
31749 var touches = e.browserEvent.touches;
31756 this.onMouseMove(e);
31762 for(var i = 0, finger; finger = touches[i]; i++){
31763 coords.push(finger.pageX, finger.pageY);
31766 var x = Math.pow(coords[0] - coords[2], 2);
31767 var y = Math.pow(coords[1] - coords[3], 2);
31769 this.endDistance = Math.sqrt(x + y);
31771 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31773 if(!this.zoomable()){
31774 this.scale = this.startScale;
31782 onTouchEnd : function(e)
31784 this.pinching = false;
31785 this.dragable = false;
31789 process : function(file, crop)
31792 this.maskEl.mask(this.loadingText);
31795 this.xhr = new XMLHttpRequest();
31797 file.xhr = this.xhr;
31799 this.xhr.open(this.method, this.url, true);
31802 "Accept": "application/json",
31803 "Cache-Control": "no-cache",
31804 "X-Requested-With": "XMLHttpRequest"
31807 for (var headerName in headers) {
31808 var headerValue = headers[headerName];
31810 this.xhr.setRequestHeader(headerName, headerValue);
31816 this.xhr.onload = function()
31818 _this.xhrOnLoad(_this.xhr);
31821 this.xhr.onerror = function()
31823 _this.xhrOnError(_this.xhr);
31826 var formData = new FormData();
31828 formData.append('returnHTML', 'NO');
31831 formData.append('crop', crop);
31834 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31835 formData.append(this.paramName, file, file.name);
31838 if(typeof(file.filename) != 'undefined'){
31839 formData.append('filename', file.filename);
31842 if(typeof(file.mimetype) != 'undefined'){
31843 formData.append('mimetype', file.mimetype);
31846 if(this.fireEvent('arrange', this, formData) != false){
31847 this.xhr.send(formData);
31851 xhrOnLoad : function(xhr)
31854 this.maskEl.unmask();
31857 if (xhr.readyState !== 4) {
31858 this.fireEvent('exception', this, xhr);
31862 var response = Roo.decode(xhr.responseText);
31864 if(!response.success){
31865 this.fireEvent('exception', this, xhr);
31869 var response = Roo.decode(xhr.responseText);
31871 this.fireEvent('upload', this, response);
31875 xhrOnError : function()
31878 this.maskEl.unmask();
31881 Roo.log('xhr on error');
31883 var response = Roo.decode(xhr.responseText);
31889 prepare : function(file)
31892 this.maskEl.mask(this.loadingText);
31898 if(typeof(file) === 'string'){
31899 this.loadCanvas(file);
31903 if(!file || !this.urlAPI){
31908 this.cropType = file.type;
31912 if(this.fireEvent('prepare', this, this.file) != false){
31914 var reader = new FileReader();
31916 reader.onload = function (e) {
31917 if (e.target.error) {
31918 Roo.log(e.target.error);
31922 var buffer = e.target.result,
31923 dataView = new DataView(buffer),
31925 maxOffset = dataView.byteLength - 4,
31929 if (dataView.getUint16(0) === 0xffd8) {
31930 while (offset < maxOffset) {
31931 markerBytes = dataView.getUint16(offset);
31933 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31934 markerLength = dataView.getUint16(offset + 2) + 2;
31935 if (offset + markerLength > dataView.byteLength) {
31936 Roo.log('Invalid meta data: Invalid segment size.');
31940 if(markerBytes == 0xffe1){
31941 _this.parseExifData(
31948 offset += markerLength;
31958 var url = _this.urlAPI.createObjectURL(_this.file);
31960 _this.loadCanvas(url);
31965 reader.readAsArrayBuffer(this.file);
31971 parseExifData : function(dataView, offset, length)
31973 var tiffOffset = offset + 10,
31977 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31978 // No Exif data, might be XMP data instead
31982 // Check for the ASCII code for "Exif" (0x45786966):
31983 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31984 // No Exif data, might be XMP data instead
31987 if (tiffOffset + 8 > dataView.byteLength) {
31988 Roo.log('Invalid Exif data: Invalid segment size.');
31991 // Check for the two null bytes:
31992 if (dataView.getUint16(offset + 8) !== 0x0000) {
31993 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31996 // Check the byte alignment:
31997 switch (dataView.getUint16(tiffOffset)) {
31999 littleEndian = true;
32002 littleEndian = false;
32005 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32008 // Check for the TIFF tag marker (0x002A):
32009 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32010 Roo.log('Invalid Exif data: Missing TIFF marker.');
32013 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32014 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32016 this.parseExifTags(
32019 tiffOffset + dirOffset,
32024 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32029 if (dirOffset + 6 > dataView.byteLength) {
32030 Roo.log('Invalid Exif data: Invalid directory offset.');
32033 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32034 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32035 if (dirEndOffset + 4 > dataView.byteLength) {
32036 Roo.log('Invalid Exif data: Invalid directory size.');
32039 for (i = 0; i < tagsNumber; i += 1) {
32043 dirOffset + 2 + 12 * i, // tag offset
32047 // Return the offset to the next directory:
32048 return dataView.getUint32(dirEndOffset, littleEndian);
32051 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32053 var tag = dataView.getUint16(offset, littleEndian);
32055 this.exif[tag] = this.getExifValue(
32059 dataView.getUint16(offset + 2, littleEndian), // tag type
32060 dataView.getUint32(offset + 4, littleEndian), // tag length
32065 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32067 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32076 Roo.log('Invalid Exif data: Invalid tag type.');
32080 tagSize = tagType.size * length;
32081 // Determine if the value is contained in the dataOffset bytes,
32082 // or if the value at the dataOffset is a pointer to the actual data:
32083 dataOffset = tagSize > 4 ?
32084 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32085 if (dataOffset + tagSize > dataView.byteLength) {
32086 Roo.log('Invalid Exif data: Invalid data offset.');
32089 if (length === 1) {
32090 return tagType.getValue(dataView, dataOffset, littleEndian);
32093 for (i = 0; i < length; i += 1) {
32094 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32097 if (tagType.ascii) {
32099 // Concatenate the chars:
32100 for (i = 0; i < values.length; i += 1) {
32102 // Ignore the terminating NULL byte(s):
32103 if (c === '\u0000') {
32115 Roo.apply(Roo.bootstrap.UploadCropbox, {
32117 'Orientation': 0x0112
32121 1: 0, //'top-left',
32123 3: 180, //'bottom-right',
32124 // 4: 'bottom-left',
32126 6: 90, //'right-top',
32127 // 7: 'right-bottom',
32128 8: 270 //'left-bottom'
32132 // byte, 8-bit unsigned int:
32134 getValue: function (dataView, dataOffset) {
32135 return dataView.getUint8(dataOffset);
32139 // ascii, 8-bit byte:
32141 getValue: function (dataView, dataOffset) {
32142 return String.fromCharCode(dataView.getUint8(dataOffset));
32147 // short, 16 bit int:
32149 getValue: function (dataView, dataOffset, littleEndian) {
32150 return dataView.getUint16(dataOffset, littleEndian);
32154 // long, 32 bit int:
32156 getValue: function (dataView, dataOffset, littleEndian) {
32157 return dataView.getUint32(dataOffset, littleEndian);
32161 // rational = two long values, first is numerator, second is denominator:
32163 getValue: function (dataView, dataOffset, littleEndian) {
32164 return dataView.getUint32(dataOffset, littleEndian) /
32165 dataView.getUint32(dataOffset + 4, littleEndian);
32169 // slong, 32 bit signed int:
32171 getValue: function (dataView, dataOffset, littleEndian) {
32172 return dataView.getInt32(dataOffset, littleEndian);
32176 // srational, two slongs, first is numerator, second is denominator:
32178 getValue: function (dataView, dataOffset, littleEndian) {
32179 return dataView.getInt32(dataOffset, littleEndian) /
32180 dataView.getInt32(dataOffset + 4, littleEndian);
32190 cls : 'btn-group roo-upload-cropbox-rotate-left',
32191 action : 'rotate-left',
32195 cls : 'btn btn-default',
32196 html : '<i class="fa fa-undo"></i>'
32202 cls : 'btn-group roo-upload-cropbox-picture',
32203 action : 'picture',
32207 cls : 'btn btn-default',
32208 html : '<i class="fa fa-picture-o"></i>'
32214 cls : 'btn-group roo-upload-cropbox-rotate-right',
32215 action : 'rotate-right',
32219 cls : 'btn btn-default',
32220 html : '<i class="fa fa-repeat"></i>'
32228 cls : 'btn-group roo-upload-cropbox-rotate-left',
32229 action : 'rotate-left',
32233 cls : 'btn btn-default',
32234 html : '<i class="fa fa-undo"></i>'
32240 cls : 'btn-group roo-upload-cropbox-download',
32241 action : 'download',
32245 cls : 'btn btn-default',
32246 html : '<i class="fa fa-download"></i>'
32252 cls : 'btn-group roo-upload-cropbox-crop',
32257 cls : 'btn btn-default',
32258 html : '<i class="fa fa-crop"></i>'
32264 cls : 'btn-group roo-upload-cropbox-trash',
32269 cls : 'btn btn-default',
32270 html : '<i class="fa fa-trash"></i>'
32276 cls : 'btn-group roo-upload-cropbox-rotate-right',
32277 action : 'rotate-right',
32281 cls : 'btn btn-default',
32282 html : '<i class="fa fa-repeat"></i>'
32290 cls : 'btn-group roo-upload-cropbox-rotate-left',
32291 action : 'rotate-left',
32295 cls : 'btn btn-default',
32296 html : '<i class="fa fa-undo"></i>'
32302 cls : 'btn-group roo-upload-cropbox-rotate-right',
32303 action : 'rotate-right',
32307 cls : 'btn btn-default',
32308 html : '<i class="fa fa-repeat"></i>'
32321 * @class Roo.bootstrap.DocumentManager
32322 * @extends Roo.bootstrap.Component
32323 * Bootstrap DocumentManager class
32324 * @cfg {String} paramName default 'imageUpload'
32325 * @cfg {String} toolTipName default 'filename'
32326 * @cfg {String} method default POST
32327 * @cfg {String} url action url
32328 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32329 * @cfg {Boolean} multiple multiple upload default true
32330 * @cfg {Number} thumbSize default 300
32331 * @cfg {String} fieldLabel
32332 * @cfg {Number} labelWidth default 4
32333 * @cfg {String} labelAlign (left|top) default left
32334 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32335 * @cfg {Number} labellg set the width of label (1-12)
32336 * @cfg {Number} labelmd set the width of label (1-12)
32337 * @cfg {Number} labelsm set the width of label (1-12)
32338 * @cfg {Number} labelxs set the width of label (1-12)
32341 * Create a new DocumentManager
32342 * @param {Object} config The config object
32345 Roo.bootstrap.DocumentManager = function(config){
32346 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32349 this.delegates = [];
32354 * Fire when initial the DocumentManager
32355 * @param {Roo.bootstrap.DocumentManager} this
32360 * inspect selected file
32361 * @param {Roo.bootstrap.DocumentManager} this
32362 * @param {File} file
32367 * Fire when xhr load exception
32368 * @param {Roo.bootstrap.DocumentManager} this
32369 * @param {XMLHttpRequest} xhr
32371 "exception" : true,
32373 * @event afterupload
32374 * Fire when xhr load exception
32375 * @param {Roo.bootstrap.DocumentManager} this
32376 * @param {XMLHttpRequest} xhr
32378 "afterupload" : true,
32381 * prepare the form data
32382 * @param {Roo.bootstrap.DocumentManager} this
32383 * @param {Object} formData
32388 * Fire when remove the file
32389 * @param {Roo.bootstrap.DocumentManager} this
32390 * @param {Object} file
32395 * Fire after refresh the file
32396 * @param {Roo.bootstrap.DocumentManager} this
32401 * Fire after click the image
32402 * @param {Roo.bootstrap.DocumentManager} this
32403 * @param {Object} file
32408 * Fire when upload a image and editable set to true
32409 * @param {Roo.bootstrap.DocumentManager} this
32410 * @param {Object} file
32414 * @event beforeselectfile
32415 * Fire before select file
32416 * @param {Roo.bootstrap.DocumentManager} this
32418 "beforeselectfile" : true,
32421 * Fire before process file
32422 * @param {Roo.bootstrap.DocumentManager} this
32423 * @param {Object} file
32427 * @event previewrendered
32428 * Fire when preview rendered
32429 * @param {Roo.bootstrap.DocumentManager} this
32430 * @param {Object} file
32432 "previewrendered" : true,
32435 "previewResize" : true
32440 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32449 paramName : 'imageUpload',
32450 toolTipName : 'filename',
32453 labelAlign : 'left',
32463 getAutoCreate : function()
32465 var managerWidget = {
32467 cls : 'roo-document-manager',
32471 cls : 'roo-document-manager-selector',
32476 cls : 'roo-document-manager-uploader',
32480 cls : 'roo-document-manager-upload-btn',
32481 html : '<i class="fa fa-plus"></i>'
32492 cls : 'column col-md-12',
32497 if(this.fieldLabel.length){
32502 cls : 'column col-md-12',
32503 html : this.fieldLabel
32507 cls : 'column col-md-12',
32512 if(this.labelAlign == 'left'){
32517 html : this.fieldLabel
32526 if(this.labelWidth > 12){
32527 content[0].style = "width: " + this.labelWidth + 'px';
32530 if(this.labelWidth < 13 && this.labelmd == 0){
32531 this.labelmd = this.labelWidth;
32534 if(this.labellg > 0){
32535 content[0].cls += ' col-lg-' + this.labellg;
32536 content[1].cls += ' col-lg-' + (12 - this.labellg);
32539 if(this.labelmd > 0){
32540 content[0].cls += ' col-md-' + this.labelmd;
32541 content[1].cls += ' col-md-' + (12 - this.labelmd);
32544 if(this.labelsm > 0){
32545 content[0].cls += ' col-sm-' + this.labelsm;
32546 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32549 if(this.labelxs > 0){
32550 content[0].cls += ' col-xs-' + this.labelxs;
32551 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32559 cls : 'row clearfix',
32567 initEvents : function()
32569 this.managerEl = this.el.select('.roo-document-manager', true).first();
32570 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32572 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32573 this.selectorEl.hide();
32576 this.selectorEl.attr('multiple', 'multiple');
32579 this.selectorEl.on('change', this.onFileSelected, this);
32581 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32582 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32584 this.uploader.on('click', this.onUploaderClick, this);
32586 this.renderProgressDialog();
32590 window.addEventListener("resize", function() { _this.refresh(); } );
32592 this.fireEvent('initial', this);
32595 renderProgressDialog : function()
32599 this.progressDialog = new Roo.bootstrap.Modal({
32600 cls : 'roo-document-manager-progress-dialog',
32601 allow_close : false,
32612 btnclick : function() {
32613 _this.uploadCancel();
32619 this.progressDialog.render(Roo.get(document.body));
32621 this.progress = new Roo.bootstrap.Progress({
32622 cls : 'roo-document-manager-progress',
32627 this.progress.render(this.progressDialog.getChildContainer());
32629 this.progressBar = new Roo.bootstrap.ProgressBar({
32630 cls : 'roo-document-manager-progress-bar',
32633 aria_valuemax : 12,
32637 this.progressBar.render(this.progress.getChildContainer());
32640 onUploaderClick : function(e)
32642 e.preventDefault();
32644 if(this.fireEvent('beforeselectfile', this) != false){
32645 this.selectorEl.dom.click();
32650 onFileSelected : function(e)
32652 e.preventDefault();
32654 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32658 Roo.each(this.selectorEl.dom.files, function(file){
32659 if(this.fireEvent('inspect', this, file) != false){
32660 this.files.push(file);
32670 this.selectorEl.dom.value = '';
32672 if(!this.files || !this.files.length){
32676 if(this.boxes > 0 && this.files.length > this.boxes){
32677 this.files = this.files.slice(0, this.boxes);
32680 this.uploader.show();
32682 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32683 this.uploader.hide();
32692 Roo.each(this.files, function(file){
32694 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32695 var f = this.renderPreview(file);
32700 if(file.type.indexOf('image') != -1){
32701 this.delegates.push(
32703 _this.process(file);
32704 }).createDelegate(this)
32712 _this.process(file);
32713 }).createDelegate(this)
32718 this.files = files;
32720 this.delegates = this.delegates.concat(docs);
32722 if(!this.delegates.length){
32727 this.progressBar.aria_valuemax = this.delegates.length;
32734 arrange : function()
32736 if(!this.delegates.length){
32737 this.progressDialog.hide();
32742 var delegate = this.delegates.shift();
32744 this.progressDialog.show();
32746 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32748 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32753 refresh : function()
32755 this.uploader.show();
32757 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32758 this.uploader.hide();
32761 Roo.isTouch ? this.closable(false) : this.closable(true);
32763 this.fireEvent('refresh', this);
32766 onRemove : function(e, el, o)
32768 e.preventDefault();
32770 this.fireEvent('remove', this, o);
32774 remove : function(o)
32778 Roo.each(this.files, function(file){
32779 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32788 this.files = files;
32795 Roo.each(this.files, function(file){
32800 file.target.remove();
32809 onClick : function(e, el, o)
32811 e.preventDefault();
32813 this.fireEvent('click', this, o);
32817 closable : function(closable)
32819 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32821 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32833 xhrOnLoad : function(xhr)
32835 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32839 if (xhr.readyState !== 4) {
32841 this.fireEvent('exception', this, xhr);
32845 var response = Roo.decode(xhr.responseText);
32847 if(!response.success){
32849 this.fireEvent('exception', this, xhr);
32853 var file = this.renderPreview(response.data);
32855 this.files.push(file);
32859 this.fireEvent('afterupload', this, xhr);
32863 xhrOnError : function(xhr)
32865 Roo.log('xhr on error');
32867 var response = Roo.decode(xhr.responseText);
32874 process : function(file)
32876 if(this.fireEvent('process', this, file) !== false){
32877 if(this.editable && file.type.indexOf('image') != -1){
32878 this.fireEvent('edit', this, file);
32882 this.uploadStart(file, false);
32889 uploadStart : function(file, crop)
32891 this.xhr = new XMLHttpRequest();
32893 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32898 file.xhr = this.xhr;
32900 this.managerEl.createChild({
32902 cls : 'roo-document-manager-loading',
32906 tooltip : file.name,
32907 cls : 'roo-document-manager-thumb',
32908 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32914 this.xhr.open(this.method, this.url, true);
32917 "Accept": "application/json",
32918 "Cache-Control": "no-cache",
32919 "X-Requested-With": "XMLHttpRequest"
32922 for (var headerName in headers) {
32923 var headerValue = headers[headerName];
32925 this.xhr.setRequestHeader(headerName, headerValue);
32931 this.xhr.onload = function()
32933 _this.xhrOnLoad(_this.xhr);
32936 this.xhr.onerror = function()
32938 _this.xhrOnError(_this.xhr);
32941 var formData = new FormData();
32943 formData.append('returnHTML', 'NO');
32946 formData.append('crop', crop);
32949 formData.append(this.paramName, file, file.name);
32956 if(this.fireEvent('prepare', this, formData, options) != false){
32958 if(options.manually){
32962 this.xhr.send(formData);
32966 this.uploadCancel();
32969 uploadCancel : function()
32975 this.delegates = [];
32977 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32984 renderPreview : function(file)
32986 if(typeof(file.target) != 'undefined' && file.target){
32990 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32992 var previewEl = this.managerEl.createChild({
32994 cls : 'roo-document-manager-preview',
32998 tooltip : file[this.toolTipName],
32999 cls : 'roo-document-manager-thumb',
33000 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33005 html : '<i class="fa fa-times-circle"></i>'
33010 var close = previewEl.select('button.close', true).first();
33012 close.on('click', this.onRemove, this, file);
33014 file.target = previewEl;
33016 var image = previewEl.select('img', true).first();
33020 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33022 image.on('click', this.onClick, this, file);
33024 this.fireEvent('previewrendered', this, file);
33030 onPreviewLoad : function(file, image)
33032 if(typeof(file.target) == 'undefined' || !file.target){
33036 var width = image.dom.naturalWidth || image.dom.width;
33037 var height = image.dom.naturalHeight || image.dom.height;
33039 if(!this.previewResize) {
33043 if(width > height){
33044 file.target.addClass('wide');
33048 file.target.addClass('tall');
33053 uploadFromSource : function(file, crop)
33055 this.xhr = new XMLHttpRequest();
33057 this.managerEl.createChild({
33059 cls : 'roo-document-manager-loading',
33063 tooltip : file.name,
33064 cls : 'roo-document-manager-thumb',
33065 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33071 this.xhr.open(this.method, this.url, true);
33074 "Accept": "application/json",
33075 "Cache-Control": "no-cache",
33076 "X-Requested-With": "XMLHttpRequest"
33079 for (var headerName in headers) {
33080 var headerValue = headers[headerName];
33082 this.xhr.setRequestHeader(headerName, headerValue);
33088 this.xhr.onload = function()
33090 _this.xhrOnLoad(_this.xhr);
33093 this.xhr.onerror = function()
33095 _this.xhrOnError(_this.xhr);
33098 var formData = new FormData();
33100 formData.append('returnHTML', 'NO');
33102 formData.append('crop', crop);
33104 if(typeof(file.filename) != 'undefined'){
33105 formData.append('filename', file.filename);
33108 if(typeof(file.mimetype) != 'undefined'){
33109 formData.append('mimetype', file.mimetype);
33114 if(this.fireEvent('prepare', this, formData) != false){
33115 this.xhr.send(formData);
33125 * @class Roo.bootstrap.DocumentViewer
33126 * @extends Roo.bootstrap.Component
33127 * Bootstrap DocumentViewer class
33128 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33129 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33132 * Create a new DocumentViewer
33133 * @param {Object} config The config object
33136 Roo.bootstrap.DocumentViewer = function(config){
33137 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33142 * Fire after initEvent
33143 * @param {Roo.bootstrap.DocumentViewer} this
33149 * @param {Roo.bootstrap.DocumentViewer} this
33154 * Fire after download button
33155 * @param {Roo.bootstrap.DocumentViewer} this
33160 * Fire after trash button
33161 * @param {Roo.bootstrap.DocumentViewer} this
33168 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33170 showDownload : true,
33174 getAutoCreate : function()
33178 cls : 'roo-document-viewer',
33182 cls : 'roo-document-viewer-body',
33186 cls : 'roo-document-viewer-thumb',
33190 cls : 'roo-document-viewer-image'
33198 cls : 'roo-document-viewer-footer',
33201 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33205 cls : 'btn-group roo-document-viewer-download',
33209 cls : 'btn btn-default',
33210 html : '<i class="fa fa-download"></i>'
33216 cls : 'btn-group roo-document-viewer-trash',
33220 cls : 'btn btn-default',
33221 html : '<i class="fa fa-trash"></i>'
33234 initEvents : function()
33236 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33237 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33239 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33240 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33242 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33243 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33245 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33246 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33248 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33249 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33251 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33252 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33254 this.bodyEl.on('click', this.onClick, this);
33255 this.downloadBtn.on('click', this.onDownload, this);
33256 this.trashBtn.on('click', this.onTrash, this);
33258 this.downloadBtn.hide();
33259 this.trashBtn.hide();
33261 if(this.showDownload){
33262 this.downloadBtn.show();
33265 if(this.showTrash){
33266 this.trashBtn.show();
33269 if(!this.showDownload && !this.showTrash) {
33270 this.footerEl.hide();
33275 initial : function()
33277 this.fireEvent('initial', this);
33281 onClick : function(e)
33283 e.preventDefault();
33285 this.fireEvent('click', this);
33288 onDownload : function(e)
33290 e.preventDefault();
33292 this.fireEvent('download', this);
33295 onTrash : function(e)
33297 e.preventDefault();
33299 this.fireEvent('trash', this);
33311 * @class Roo.bootstrap.NavProgressBar
33312 * @extends Roo.bootstrap.Component
33313 * Bootstrap NavProgressBar class
33316 * Create a new nav progress bar
33317 * @param {Object} config The config object
33320 Roo.bootstrap.NavProgressBar = function(config){
33321 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33323 this.bullets = this.bullets || [];
33325 // Roo.bootstrap.NavProgressBar.register(this);
33329 * Fires when the active item changes
33330 * @param {Roo.bootstrap.NavProgressBar} this
33331 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33332 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33339 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33344 getAutoCreate : function()
33346 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33350 cls : 'roo-navigation-bar-group',
33354 cls : 'roo-navigation-top-bar'
33358 cls : 'roo-navigation-bullets-bar',
33362 cls : 'roo-navigation-bar'
33369 cls : 'roo-navigation-bottom-bar'
33379 initEvents: function()
33384 onRender : function(ct, position)
33386 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33388 if(this.bullets.length){
33389 Roo.each(this.bullets, function(b){
33398 addItem : function(cfg)
33400 var item = new Roo.bootstrap.NavProgressItem(cfg);
33402 item.parentId = this.id;
33403 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33406 var top = new Roo.bootstrap.Element({
33408 cls : 'roo-navigation-bar-text'
33411 var bottom = new Roo.bootstrap.Element({
33413 cls : 'roo-navigation-bar-text'
33416 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33417 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33419 var topText = new Roo.bootstrap.Element({
33421 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33424 var bottomText = new Roo.bootstrap.Element({
33426 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33429 topText.onRender(top.el, null);
33430 bottomText.onRender(bottom.el, null);
33433 item.bottomEl = bottom;
33436 this.barItems.push(item);
33441 getActive : function()
33443 var active = false;
33445 Roo.each(this.barItems, function(v){
33447 if (!v.isActive()) {
33459 setActiveItem : function(item)
33463 Roo.each(this.barItems, function(v){
33464 if (v.rid == item.rid) {
33468 if (v.isActive()) {
33469 v.setActive(false);
33474 item.setActive(true);
33476 this.fireEvent('changed', this, item, prev);
33479 getBarItem: function(rid)
33483 Roo.each(this.barItems, function(e) {
33484 if (e.rid != rid) {
33495 indexOfItem : function(item)
33499 Roo.each(this.barItems, function(v, i){
33501 if (v.rid != item.rid) {
33512 setActiveNext : function()
33514 var i = this.indexOfItem(this.getActive());
33516 if (i > this.barItems.length) {
33520 this.setActiveItem(this.barItems[i+1]);
33523 setActivePrev : function()
33525 var i = this.indexOfItem(this.getActive());
33531 this.setActiveItem(this.barItems[i-1]);
33534 format : function()
33536 if(!this.barItems.length){
33540 var width = 100 / this.barItems.length;
33542 Roo.each(this.barItems, function(i){
33543 i.el.setStyle('width', width + '%');
33544 i.topEl.el.setStyle('width', width + '%');
33545 i.bottomEl.el.setStyle('width', width + '%');
33554 * Nav Progress Item
33559 * @class Roo.bootstrap.NavProgressItem
33560 * @extends Roo.bootstrap.Component
33561 * Bootstrap NavProgressItem class
33562 * @cfg {String} rid the reference id
33563 * @cfg {Boolean} active (true|false) Is item active default false
33564 * @cfg {Boolean} disabled (true|false) Is item active default false
33565 * @cfg {String} html
33566 * @cfg {String} position (top|bottom) text position default bottom
33567 * @cfg {String} icon show icon instead of number
33570 * Create a new NavProgressItem
33571 * @param {Object} config The config object
33573 Roo.bootstrap.NavProgressItem = function(config){
33574 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33579 * The raw click event for the entire grid.
33580 * @param {Roo.bootstrap.NavProgressItem} this
33581 * @param {Roo.EventObject} e
33588 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33594 position : 'bottom',
33597 getAutoCreate : function()
33599 var iconCls = 'roo-navigation-bar-item-icon';
33601 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33605 cls: 'roo-navigation-bar-item',
33615 cfg.cls += ' active';
33618 cfg.cls += ' disabled';
33624 disable : function()
33626 this.setDisabled(true);
33629 enable : function()
33631 this.setDisabled(false);
33634 initEvents: function()
33636 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33638 this.iconEl.on('click', this.onClick, this);
33641 onClick : function(e)
33643 e.preventDefault();
33649 if(this.fireEvent('click', this, e) === false){
33653 this.parent().setActiveItem(this);
33656 isActive: function ()
33658 return this.active;
33661 setActive : function(state)
33663 if(this.active == state){
33667 this.active = state;
33670 this.el.addClass('active');
33674 this.el.removeClass('active');
33679 setDisabled : function(state)
33681 if(this.disabled == state){
33685 this.disabled = state;
33688 this.el.addClass('disabled');
33692 this.el.removeClass('disabled');
33695 tooltipEl : function()
33697 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33710 * @class Roo.bootstrap.FieldLabel
33711 * @extends Roo.bootstrap.Component
33712 * Bootstrap FieldLabel class
33713 * @cfg {String} html contents of the element
33714 * @cfg {String} tag tag of the element default label
33715 * @cfg {String} cls class of the element
33716 * @cfg {String} target label target
33717 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33718 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33719 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33720 * @cfg {String} iconTooltip default "This field is required"
33721 * @cfg {String} indicatorpos (left|right) default left
33724 * Create a new FieldLabel
33725 * @param {Object} config The config object
33728 Roo.bootstrap.FieldLabel = function(config){
33729 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33734 * Fires after the field has been marked as invalid.
33735 * @param {Roo.form.FieldLabel} this
33736 * @param {String} msg The validation message
33741 * Fires after the field has been validated with no errors.
33742 * @param {Roo.form.FieldLabel} this
33748 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33755 invalidClass : 'has-warning',
33756 validClass : 'has-success',
33757 iconTooltip : 'This field is required',
33758 indicatorpos : 'left',
33760 getAutoCreate : function(){
33763 if (!this.allowBlank) {
33769 cls : 'roo-bootstrap-field-label ' + this.cls,
33774 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33775 tooltip : this.iconTooltip
33784 if(this.indicatorpos == 'right'){
33787 cls : 'roo-bootstrap-field-label ' + this.cls,
33796 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33797 tooltip : this.iconTooltip
33806 initEvents: function()
33808 Roo.bootstrap.Element.superclass.initEvents.call(this);
33810 this.indicator = this.indicatorEl();
33812 if(this.indicator){
33813 this.indicator.removeClass('visible');
33814 this.indicator.addClass('invisible');
33817 Roo.bootstrap.FieldLabel.register(this);
33820 indicatorEl : function()
33822 var indicator = this.el.select('i.roo-required-indicator',true).first();
33833 * Mark this field as valid
33835 markValid : function()
33837 if(this.indicator){
33838 this.indicator.removeClass('visible');
33839 this.indicator.addClass('invisible');
33841 if (Roo.bootstrap.version == 3) {
33842 this.el.removeClass(this.invalidClass);
33843 this.el.addClass(this.validClass);
33845 this.el.removeClass('is-invalid');
33846 this.el.addClass('is-valid');
33850 this.fireEvent('valid', this);
33854 * Mark this field as invalid
33855 * @param {String} msg The validation message
33857 markInvalid : function(msg)
33859 if(this.indicator){
33860 this.indicator.removeClass('invisible');
33861 this.indicator.addClass('visible');
33863 if (Roo.bootstrap.version == 3) {
33864 this.el.removeClass(this.validClass);
33865 this.el.addClass(this.invalidClass);
33867 this.el.removeClass('is-valid');
33868 this.el.addClass('is-invalid');
33872 this.fireEvent('invalid', this, msg);
33878 Roo.apply(Roo.bootstrap.FieldLabel, {
33883 * register a FieldLabel Group
33884 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33886 register : function(label)
33888 if(this.groups.hasOwnProperty(label.target)){
33892 this.groups[label.target] = label;
33896 * fetch a FieldLabel Group based on the target
33897 * @param {string} target
33898 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33900 get: function(target) {
33901 if (typeof(this.groups[target]) == 'undefined') {
33905 return this.groups[target] ;
33914 * page DateSplitField.
33920 * @class Roo.bootstrap.DateSplitField
33921 * @extends Roo.bootstrap.Component
33922 * Bootstrap DateSplitField class
33923 * @cfg {string} fieldLabel - the label associated
33924 * @cfg {Number} labelWidth set the width of label (0-12)
33925 * @cfg {String} labelAlign (top|left)
33926 * @cfg {Boolean} dayAllowBlank (true|false) default false
33927 * @cfg {Boolean} monthAllowBlank (true|false) default false
33928 * @cfg {Boolean} yearAllowBlank (true|false) default false
33929 * @cfg {string} dayPlaceholder
33930 * @cfg {string} monthPlaceholder
33931 * @cfg {string} yearPlaceholder
33932 * @cfg {string} dayFormat default 'd'
33933 * @cfg {string} monthFormat default 'm'
33934 * @cfg {string} yearFormat default 'Y'
33935 * @cfg {Number} labellg set the width of label (1-12)
33936 * @cfg {Number} labelmd set the width of label (1-12)
33937 * @cfg {Number} labelsm set the width of label (1-12)
33938 * @cfg {Number} labelxs set the width of label (1-12)
33942 * Create a new DateSplitField
33943 * @param {Object} config The config object
33946 Roo.bootstrap.DateSplitField = function(config){
33947 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33953 * getting the data of years
33954 * @param {Roo.bootstrap.DateSplitField} this
33955 * @param {Object} years
33960 * getting the data of days
33961 * @param {Roo.bootstrap.DateSplitField} this
33962 * @param {Object} days
33967 * Fires after the field has been marked as invalid.
33968 * @param {Roo.form.Field} this
33969 * @param {String} msg The validation message
33974 * Fires after the field has been validated with no errors.
33975 * @param {Roo.form.Field} this
33981 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33984 labelAlign : 'top',
33986 dayAllowBlank : false,
33987 monthAllowBlank : false,
33988 yearAllowBlank : false,
33989 dayPlaceholder : '',
33990 monthPlaceholder : '',
33991 yearPlaceholder : '',
33995 isFormField : true,
34001 getAutoCreate : function()
34005 cls : 'row roo-date-split-field-group',
34010 cls : 'form-hidden-field roo-date-split-field-group-value',
34016 var labelCls = 'col-md-12';
34017 var contentCls = 'col-md-4';
34019 if(this.fieldLabel){
34023 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34027 html : this.fieldLabel
34032 if(this.labelAlign == 'left'){
34034 if(this.labelWidth > 12){
34035 label.style = "width: " + this.labelWidth + 'px';
34038 if(this.labelWidth < 13 && this.labelmd == 0){
34039 this.labelmd = this.labelWidth;
34042 if(this.labellg > 0){
34043 labelCls = ' col-lg-' + this.labellg;
34044 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34047 if(this.labelmd > 0){
34048 labelCls = ' col-md-' + this.labelmd;
34049 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34052 if(this.labelsm > 0){
34053 labelCls = ' col-sm-' + this.labelsm;
34054 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34057 if(this.labelxs > 0){
34058 labelCls = ' col-xs-' + this.labelxs;
34059 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34063 label.cls += ' ' + labelCls;
34065 cfg.cn.push(label);
34068 Roo.each(['day', 'month', 'year'], function(t){
34071 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34078 inputEl: function ()
34080 return this.el.select('.roo-date-split-field-group-value', true).first();
34083 onRender : function(ct, position)
34087 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34089 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34091 this.dayField = new Roo.bootstrap.ComboBox({
34092 allowBlank : this.dayAllowBlank,
34093 alwaysQuery : true,
34094 displayField : 'value',
34097 forceSelection : true,
34099 placeholder : this.dayPlaceholder,
34100 selectOnFocus : true,
34101 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34102 triggerAction : 'all',
34104 valueField : 'value',
34105 store : new Roo.data.SimpleStore({
34106 data : (function() {
34108 _this.fireEvent('days', _this, days);
34111 fields : [ 'value' ]
34114 select : function (_self, record, index)
34116 _this.setValue(_this.getValue());
34121 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34123 this.monthField = new Roo.bootstrap.MonthField({
34124 after : '<i class=\"fa fa-calendar\"></i>',
34125 allowBlank : this.monthAllowBlank,
34126 placeholder : this.monthPlaceholder,
34129 render : function (_self)
34131 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34132 e.preventDefault();
34136 select : function (_self, oldvalue, newvalue)
34138 _this.setValue(_this.getValue());
34143 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34145 this.yearField = new Roo.bootstrap.ComboBox({
34146 allowBlank : this.yearAllowBlank,
34147 alwaysQuery : true,
34148 displayField : 'value',
34151 forceSelection : true,
34153 placeholder : this.yearPlaceholder,
34154 selectOnFocus : true,
34155 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34156 triggerAction : 'all',
34158 valueField : 'value',
34159 store : new Roo.data.SimpleStore({
34160 data : (function() {
34162 _this.fireEvent('years', _this, years);
34165 fields : [ 'value' ]
34168 select : function (_self, record, index)
34170 _this.setValue(_this.getValue());
34175 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34178 setValue : function(v, format)
34180 this.inputEl.dom.value = v;
34182 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34184 var d = Date.parseDate(v, f);
34191 this.setDay(d.format(this.dayFormat));
34192 this.setMonth(d.format(this.monthFormat));
34193 this.setYear(d.format(this.yearFormat));
34200 setDay : function(v)
34202 this.dayField.setValue(v);
34203 this.inputEl.dom.value = this.getValue();
34208 setMonth : function(v)
34210 this.monthField.setValue(v, true);
34211 this.inputEl.dom.value = this.getValue();
34216 setYear : function(v)
34218 this.yearField.setValue(v);
34219 this.inputEl.dom.value = this.getValue();
34224 getDay : function()
34226 return this.dayField.getValue();
34229 getMonth : function()
34231 return this.monthField.getValue();
34234 getYear : function()
34236 return this.yearField.getValue();
34239 getValue : function()
34241 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34243 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34253 this.inputEl.dom.value = '';
34258 validate : function()
34260 var d = this.dayField.validate();
34261 var m = this.monthField.validate();
34262 var y = this.yearField.validate();
34267 (!this.dayAllowBlank && !d) ||
34268 (!this.monthAllowBlank && !m) ||
34269 (!this.yearAllowBlank && !y)
34274 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34283 this.markInvalid();
34288 markValid : function()
34291 var label = this.el.select('label', true).first();
34292 var icon = this.el.select('i.fa-star', true).first();
34298 this.fireEvent('valid', this);
34302 * Mark this field as invalid
34303 * @param {String} msg The validation message
34305 markInvalid : function(msg)
34308 var label = this.el.select('label', true).first();
34309 var icon = this.el.select('i.fa-star', true).first();
34311 if(label && !icon){
34312 this.el.select('.roo-date-split-field-label', true).createChild({
34314 cls : 'text-danger fa fa-lg fa-star',
34315 tooltip : 'This field is required',
34316 style : 'margin-right:5px;'
34320 this.fireEvent('invalid', this, msg);
34323 clearInvalid : function()
34325 var label = this.el.select('label', true).first();
34326 var icon = this.el.select('i.fa-star', true).first();
34332 this.fireEvent('valid', this);
34335 getName: function()
34345 * http://masonry.desandro.com
34347 * The idea is to render all the bricks based on vertical width...
34349 * The original code extends 'outlayer' - we might need to use that....
34355 * @class Roo.bootstrap.LayoutMasonry
34356 * @extends Roo.bootstrap.Component
34357 * Bootstrap Layout Masonry class
34360 * Create a new Element
34361 * @param {Object} config The config object
34364 Roo.bootstrap.LayoutMasonry = function(config){
34366 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34370 Roo.bootstrap.LayoutMasonry.register(this);
34376 * Fire after layout the items
34377 * @param {Roo.bootstrap.LayoutMasonry} this
34378 * @param {Roo.EventObject} e
34385 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34388 * @cfg {Boolean} isLayoutInstant = no animation?
34390 isLayoutInstant : false, // needed?
34393 * @cfg {Number} boxWidth width of the columns
34398 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34403 * @cfg {Number} padWidth padding below box..
34408 * @cfg {Number} gutter gutter width..
34413 * @cfg {Number} maxCols maximum number of columns
34419 * @cfg {Boolean} isAutoInitial defalut true
34421 isAutoInitial : true,
34426 * @cfg {Boolean} isHorizontal defalut false
34428 isHorizontal : false,
34430 currentSize : null,
34436 bricks: null, //CompositeElement
34440 _isLayoutInited : false,
34442 // isAlternative : false, // only use for vertical layout...
34445 * @cfg {Number} alternativePadWidth padding below box..
34447 alternativePadWidth : 50,
34449 selectedBrick : [],
34451 getAutoCreate : function(){
34453 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34457 cls: 'blog-masonary-wrapper ' + this.cls,
34459 cls : 'mas-boxes masonary'
34466 getChildContainer: function( )
34468 if (this.boxesEl) {
34469 return this.boxesEl;
34472 this.boxesEl = this.el.select('.mas-boxes').first();
34474 return this.boxesEl;
34478 initEvents : function()
34482 if(this.isAutoInitial){
34483 Roo.log('hook children rendered');
34484 this.on('childrenrendered', function() {
34485 Roo.log('children rendered');
34491 initial : function()
34493 this.selectedBrick = [];
34495 this.currentSize = this.el.getBox(true);
34497 Roo.EventManager.onWindowResize(this.resize, this);
34499 if(!this.isAutoInitial){
34507 //this.layout.defer(500,this);
34511 resize : function()
34513 var cs = this.el.getBox(true);
34516 this.currentSize.width == cs.width &&
34517 this.currentSize.x == cs.x &&
34518 this.currentSize.height == cs.height &&
34519 this.currentSize.y == cs.y
34521 Roo.log("no change in with or X or Y");
34525 this.currentSize = cs;
34531 layout : function()
34533 this._resetLayout();
34535 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34537 this.layoutItems( isInstant );
34539 this._isLayoutInited = true;
34541 this.fireEvent('layout', this);
34545 _resetLayout : function()
34547 if(this.isHorizontal){
34548 this.horizontalMeasureColumns();
34552 this.verticalMeasureColumns();
34556 verticalMeasureColumns : function()
34558 this.getContainerWidth();
34560 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34561 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34565 var boxWidth = this.boxWidth + this.padWidth;
34567 if(this.containerWidth < this.boxWidth){
34568 boxWidth = this.containerWidth
34571 var containerWidth = this.containerWidth;
34573 var cols = Math.floor(containerWidth / boxWidth);
34575 this.cols = Math.max( cols, 1 );
34577 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34579 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34581 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34583 this.colWidth = boxWidth + avail - this.padWidth;
34585 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34586 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34589 horizontalMeasureColumns : function()
34591 this.getContainerWidth();
34593 var boxWidth = this.boxWidth;
34595 if(this.containerWidth < boxWidth){
34596 boxWidth = this.containerWidth;
34599 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34601 this.el.setHeight(boxWidth);
34605 getContainerWidth : function()
34607 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34610 layoutItems : function( isInstant )
34612 Roo.log(this.bricks);
34614 var items = Roo.apply([], this.bricks);
34616 if(this.isHorizontal){
34617 this._horizontalLayoutItems( items , isInstant );
34621 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34622 // this._verticalAlternativeLayoutItems( items , isInstant );
34626 this._verticalLayoutItems( items , isInstant );
34630 _verticalLayoutItems : function ( items , isInstant)
34632 if ( !items || !items.length ) {
34637 ['xs', 'xs', 'xs', 'tall'],
34638 ['xs', 'xs', 'tall'],
34639 ['xs', 'xs', 'sm'],
34640 ['xs', 'xs', 'xs'],
34646 ['sm', 'xs', 'xs'],
34650 ['tall', 'xs', 'xs', 'xs'],
34651 ['tall', 'xs', 'xs'],
34663 Roo.each(items, function(item, k){
34665 switch (item.size) {
34666 // these layouts take up a full box,
34677 boxes.push([item]);
34700 var filterPattern = function(box, length)
34708 var pattern = box.slice(0, length);
34712 Roo.each(pattern, function(i){
34713 format.push(i.size);
34716 Roo.each(standard, function(s){
34718 if(String(s) != String(format)){
34727 if(!match && length == 1){
34732 filterPattern(box, length - 1);
34736 queue.push(pattern);
34738 box = box.slice(length, box.length);
34740 filterPattern(box, 4);
34746 Roo.each(boxes, function(box, k){
34752 if(box.length == 1){
34757 filterPattern(box, 4);
34761 this._processVerticalLayoutQueue( queue, isInstant );
34765 // _verticalAlternativeLayoutItems : function( items , isInstant )
34767 // if ( !items || !items.length ) {
34771 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34775 _horizontalLayoutItems : function ( items , isInstant)
34777 if ( !items || !items.length || items.length < 3) {
34783 var eItems = items.slice(0, 3);
34785 items = items.slice(3, items.length);
34788 ['xs', 'xs', 'xs', 'wide'],
34789 ['xs', 'xs', 'wide'],
34790 ['xs', 'xs', 'sm'],
34791 ['xs', 'xs', 'xs'],
34797 ['sm', 'xs', 'xs'],
34801 ['wide', 'xs', 'xs', 'xs'],
34802 ['wide', 'xs', 'xs'],
34815 Roo.each(items, function(item, k){
34817 switch (item.size) {
34828 boxes.push([item]);
34852 var filterPattern = function(box, length)
34860 var pattern = box.slice(0, length);
34864 Roo.each(pattern, function(i){
34865 format.push(i.size);
34868 Roo.each(standard, function(s){
34870 if(String(s) != String(format)){
34879 if(!match && length == 1){
34884 filterPattern(box, length - 1);
34888 queue.push(pattern);
34890 box = box.slice(length, box.length);
34892 filterPattern(box, 4);
34898 Roo.each(boxes, function(box, k){
34904 if(box.length == 1){
34909 filterPattern(box, 4);
34916 var pos = this.el.getBox(true);
34920 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34922 var hit_end = false;
34924 Roo.each(queue, function(box){
34928 Roo.each(box, function(b){
34930 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34940 Roo.each(box, function(b){
34942 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34945 mx = Math.max(mx, b.x);
34949 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34953 Roo.each(box, function(b){
34955 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34969 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34972 /** Sets position of item in DOM
34973 * @param {Element} item
34974 * @param {Number} x - horizontal position
34975 * @param {Number} y - vertical position
34976 * @param {Boolean} isInstant - disables transitions
34978 _processVerticalLayoutQueue : function( queue, isInstant )
34980 var pos = this.el.getBox(true);
34985 for (var i = 0; i < this.cols; i++){
34989 Roo.each(queue, function(box, k){
34991 var col = k % this.cols;
34993 Roo.each(box, function(b,kk){
34995 b.el.position('absolute');
34997 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34998 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35000 if(b.size == 'md-left' || b.size == 'md-right'){
35001 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35002 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35005 b.el.setWidth(width);
35006 b.el.setHeight(height);
35008 b.el.select('iframe',true).setSize(width,height);
35012 for (var i = 0; i < this.cols; i++){
35014 if(maxY[i] < maxY[col]){
35019 col = Math.min(col, i);
35023 x = pos.x + col * (this.colWidth + this.padWidth);
35027 var positions = [];
35029 switch (box.length){
35031 positions = this.getVerticalOneBoxColPositions(x, y, box);
35034 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35037 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35040 positions = this.getVerticalFourBoxColPositions(x, y, box);
35046 Roo.each(box, function(b,kk){
35048 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35050 var sz = b.el.getSize();
35052 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35060 for (var i = 0; i < this.cols; i++){
35061 mY = Math.max(mY, maxY[i]);
35064 this.el.setHeight(mY - pos.y);
35068 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35070 // var pos = this.el.getBox(true);
35073 // var maxX = pos.right;
35075 // var maxHeight = 0;
35077 // Roo.each(items, function(item, k){
35081 // item.el.position('absolute');
35083 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35085 // item.el.setWidth(width);
35087 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35089 // item.el.setHeight(height);
35092 // item.el.setXY([x, y], isInstant ? false : true);
35094 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35097 // y = y + height + this.alternativePadWidth;
35099 // maxHeight = maxHeight + height + this.alternativePadWidth;
35103 // this.el.setHeight(maxHeight);
35107 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35109 var pos = this.el.getBox(true);
35114 var maxX = pos.right;
35116 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35118 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35120 Roo.each(queue, function(box, k){
35122 Roo.each(box, function(b, kk){
35124 b.el.position('absolute');
35126 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35127 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35129 if(b.size == 'md-left' || b.size == 'md-right'){
35130 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35131 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35134 b.el.setWidth(width);
35135 b.el.setHeight(height);
35143 var positions = [];
35145 switch (box.length){
35147 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35150 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35153 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35156 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35162 Roo.each(box, function(b,kk){
35164 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35166 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35174 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35176 Roo.each(eItems, function(b,k){
35178 b.size = (k == 0) ? 'sm' : 'xs';
35179 b.x = (k == 0) ? 2 : 1;
35180 b.y = (k == 0) ? 2 : 1;
35182 b.el.position('absolute');
35184 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35186 b.el.setWidth(width);
35188 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35190 b.el.setHeight(height);
35194 var positions = [];
35197 x : maxX - this.unitWidth * 2 - this.gutter,
35202 x : maxX - this.unitWidth,
35203 y : minY + (this.unitWidth + this.gutter) * 2
35207 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35211 Roo.each(eItems, function(b,k){
35213 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35219 getVerticalOneBoxColPositions : function(x, y, box)
35223 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35225 if(box[0].size == 'md-left'){
35229 if(box[0].size == 'md-right'){
35234 x : x + (this.unitWidth + this.gutter) * rand,
35241 getVerticalTwoBoxColPositions : function(x, y, box)
35245 if(box[0].size == 'xs'){
35249 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35253 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35267 x : x + (this.unitWidth + this.gutter) * 2,
35268 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35275 getVerticalThreeBoxColPositions : function(x, y, box)
35279 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35287 x : x + (this.unitWidth + this.gutter) * 1,
35292 x : x + (this.unitWidth + this.gutter) * 2,
35300 if(box[0].size == 'xs' && box[1].size == 'xs'){
35309 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35313 x : x + (this.unitWidth + this.gutter) * 1,
35327 x : x + (this.unitWidth + this.gutter) * 2,
35332 x : x + (this.unitWidth + this.gutter) * 2,
35333 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35340 getVerticalFourBoxColPositions : function(x, y, box)
35344 if(box[0].size == 'xs'){
35353 y : y + (this.unitHeight + this.gutter) * 1
35358 y : y + (this.unitHeight + this.gutter) * 2
35362 x : x + (this.unitWidth + this.gutter) * 1,
35376 x : x + (this.unitWidth + this.gutter) * 2,
35381 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35382 y : y + (this.unitHeight + this.gutter) * 1
35386 x : x + (this.unitWidth + this.gutter) * 2,
35387 y : y + (this.unitWidth + this.gutter) * 2
35394 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35398 if(box[0].size == 'md-left'){
35400 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35407 if(box[0].size == 'md-right'){
35409 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35410 y : minY + (this.unitWidth + this.gutter) * 1
35416 var rand = Math.floor(Math.random() * (4 - box[0].y));
35419 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35420 y : minY + (this.unitWidth + this.gutter) * rand
35427 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35431 if(box[0].size == 'xs'){
35434 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35439 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35440 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35448 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35453 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35454 y : minY + (this.unitWidth + this.gutter) * 2
35461 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35465 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35468 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35473 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35474 y : minY + (this.unitWidth + this.gutter) * 1
35478 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35479 y : minY + (this.unitWidth + this.gutter) * 2
35486 if(box[0].size == 'xs' && box[1].size == 'xs'){
35489 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35494 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35499 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35500 y : minY + (this.unitWidth + this.gutter) * 1
35508 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35513 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35514 y : minY + (this.unitWidth + this.gutter) * 2
35518 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35519 y : minY + (this.unitWidth + this.gutter) * 2
35526 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35530 if(box[0].size == 'xs'){
35533 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35538 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35543 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35548 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35549 y : minY + (this.unitWidth + this.gutter) * 1
35557 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35562 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35563 y : minY + (this.unitWidth + this.gutter) * 2
35567 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35568 y : minY + (this.unitWidth + this.gutter) * 2
35572 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35573 y : minY + (this.unitWidth + this.gutter) * 2
35581 * remove a Masonry Brick
35582 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35584 removeBrick : function(brick_id)
35590 for (var i = 0; i<this.bricks.length; i++) {
35591 if (this.bricks[i].id == brick_id) {
35592 this.bricks.splice(i,1);
35593 this.el.dom.removeChild(Roo.get(brick_id).dom);
35600 * adds a Masonry Brick
35601 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35603 addBrick : function(cfg)
35605 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35606 //this.register(cn);
35607 cn.parentId = this.id;
35608 cn.render(this.el);
35613 * register a Masonry Brick
35614 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35617 register : function(brick)
35619 this.bricks.push(brick);
35620 brick.masonryId = this.id;
35624 * clear all the Masonry Brick
35626 clearAll : function()
35629 //this.getChildContainer().dom.innerHTML = "";
35630 this.el.dom.innerHTML = '';
35633 getSelected : function()
35635 if (!this.selectedBrick) {
35639 return this.selectedBrick;
35643 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35647 * register a Masonry Layout
35648 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35651 register : function(layout)
35653 this.groups[layout.id] = layout;
35656 * fetch a Masonry Layout based on the masonry layout ID
35657 * @param {string} the masonry layout to add
35658 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35661 get: function(layout_id) {
35662 if (typeof(this.groups[layout_id]) == 'undefined') {
35665 return this.groups[layout_id] ;
35677 * http://masonry.desandro.com
35679 * The idea is to render all the bricks based on vertical width...
35681 * The original code extends 'outlayer' - we might need to use that....
35687 * @class Roo.bootstrap.LayoutMasonryAuto
35688 * @extends Roo.bootstrap.Component
35689 * Bootstrap Layout Masonry class
35692 * Create a new Element
35693 * @param {Object} config The config object
35696 Roo.bootstrap.LayoutMasonryAuto = function(config){
35697 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35700 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35703 * @cfg {Boolean} isFitWidth - resize the width..
35705 isFitWidth : false, // options..
35707 * @cfg {Boolean} isOriginLeft = left align?
35709 isOriginLeft : true,
35711 * @cfg {Boolean} isOriginTop = top align?
35713 isOriginTop : false,
35715 * @cfg {Boolean} isLayoutInstant = no animation?
35717 isLayoutInstant : false, // needed?
35719 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35721 isResizingContainer : true,
35723 * @cfg {Number} columnWidth width of the columns
35729 * @cfg {Number} maxCols maximum number of columns
35734 * @cfg {Number} padHeight padding below box..
35740 * @cfg {Boolean} isAutoInitial defalut true
35743 isAutoInitial : true,
35749 initialColumnWidth : 0,
35750 currentSize : null,
35752 colYs : null, // array.
35759 bricks: null, //CompositeElement
35760 cols : 0, // array?
35761 // element : null, // wrapped now this.el
35762 _isLayoutInited : null,
35765 getAutoCreate : function(){
35769 cls: 'blog-masonary-wrapper ' + this.cls,
35771 cls : 'mas-boxes masonary'
35778 getChildContainer: function( )
35780 if (this.boxesEl) {
35781 return this.boxesEl;
35784 this.boxesEl = this.el.select('.mas-boxes').first();
35786 return this.boxesEl;
35790 initEvents : function()
35794 if(this.isAutoInitial){
35795 Roo.log('hook children rendered');
35796 this.on('childrenrendered', function() {
35797 Roo.log('children rendered');
35804 initial : function()
35806 this.reloadItems();
35808 this.currentSize = this.el.getBox(true);
35810 /// was window resize... - let's see if this works..
35811 Roo.EventManager.onWindowResize(this.resize, this);
35813 if(!this.isAutoInitial){
35818 this.layout.defer(500,this);
35821 reloadItems: function()
35823 this.bricks = this.el.select('.masonry-brick', true);
35825 this.bricks.each(function(b) {
35826 //Roo.log(b.getSize());
35827 if (!b.attr('originalwidth')) {
35828 b.attr('originalwidth', b.getSize().width);
35833 Roo.log(this.bricks.elements.length);
35836 resize : function()
35839 var cs = this.el.getBox(true);
35841 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35842 Roo.log("no change in with or X");
35845 this.currentSize = cs;
35849 layout : function()
35852 this._resetLayout();
35853 //this._manageStamps();
35855 // don't animate first layout
35856 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35857 this.layoutItems( isInstant );
35859 // flag for initalized
35860 this._isLayoutInited = true;
35863 layoutItems : function( isInstant )
35865 //var items = this._getItemsForLayout( this.items );
35866 // original code supports filtering layout items.. we just ignore it..
35868 this._layoutItems( this.bricks , isInstant );
35870 this._postLayout();
35872 _layoutItems : function ( items , isInstant)
35874 //this.fireEvent( 'layout', this, items );
35877 if ( !items || !items.elements.length ) {
35878 // no items, emit event with empty array
35883 items.each(function(item) {
35884 Roo.log("layout item");
35886 // get x/y object from method
35887 var position = this._getItemLayoutPosition( item );
35889 position.item = item;
35890 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35891 queue.push( position );
35894 this._processLayoutQueue( queue );
35896 /** Sets position of item in DOM
35897 * @param {Element} item
35898 * @param {Number} x - horizontal position
35899 * @param {Number} y - vertical position
35900 * @param {Boolean} isInstant - disables transitions
35902 _processLayoutQueue : function( queue )
35904 for ( var i=0, len = queue.length; i < len; i++ ) {
35905 var obj = queue[i];
35906 obj.item.position('absolute');
35907 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35913 * Any logic you want to do after each layout,
35914 * i.e. size the container
35916 _postLayout : function()
35918 this.resizeContainer();
35921 resizeContainer : function()
35923 if ( !this.isResizingContainer ) {
35926 var size = this._getContainerSize();
35928 this.el.setSize(size.width,size.height);
35929 this.boxesEl.setSize(size.width,size.height);
35935 _resetLayout : function()
35937 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35938 this.colWidth = this.el.getWidth();
35939 //this.gutter = this.el.getWidth();
35941 this.measureColumns();
35947 this.colYs.push( 0 );
35953 measureColumns : function()
35955 this.getContainerWidth();
35956 // if columnWidth is 0, default to outerWidth of first item
35957 if ( !this.columnWidth ) {
35958 var firstItem = this.bricks.first();
35959 Roo.log(firstItem);
35960 this.columnWidth = this.containerWidth;
35961 if (firstItem && firstItem.attr('originalwidth') ) {
35962 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35964 // columnWidth fall back to item of first element
35965 Roo.log("set column width?");
35966 this.initialColumnWidth = this.columnWidth ;
35968 // if first elem has no width, default to size of container
35973 if (this.initialColumnWidth) {
35974 this.columnWidth = this.initialColumnWidth;
35979 // column width is fixed at the top - however if container width get's smaller we should
35982 // this bit calcs how man columns..
35984 var columnWidth = this.columnWidth += this.gutter;
35986 // calculate columns
35987 var containerWidth = this.containerWidth + this.gutter;
35989 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35990 // fix rounding errors, typically with gutters
35991 var excess = columnWidth - containerWidth % columnWidth;
35994 // if overshoot is less than a pixel, round up, otherwise floor it
35995 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35996 cols = Math[ mathMethod ]( cols );
35997 this.cols = Math.max( cols, 1 );
35998 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36000 // padding positioning..
36001 var totalColWidth = this.cols * this.columnWidth;
36002 var padavail = this.containerWidth - totalColWidth;
36003 // so for 2 columns - we need 3 'pads'
36005 var padNeeded = (1+this.cols) * this.padWidth;
36007 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36009 this.columnWidth += padExtra
36010 //this.padWidth = Math.floor(padavail / ( this.cols));
36012 // adjust colum width so that padding is fixed??
36014 // we have 3 columns ... total = width * 3
36015 // we have X left over... that should be used by
36017 //if (this.expandC) {
36025 getContainerWidth : function()
36027 /* // container is parent if fit width
36028 var container = this.isFitWidth ? this.element.parentNode : this.element;
36029 // check that this.size and size are there
36030 // IE8 triggers resize on body size change, so they might not be
36032 var size = getSize( container ); //FIXME
36033 this.containerWidth = size && size.innerWidth; //FIXME
36036 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36040 _getItemLayoutPosition : function( item ) // what is item?
36042 // we resize the item to our columnWidth..
36044 item.setWidth(this.columnWidth);
36045 item.autoBoxAdjust = false;
36047 var sz = item.getSize();
36049 // how many columns does this brick span
36050 var remainder = this.containerWidth % this.columnWidth;
36052 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36053 // round if off by 1 pixel, otherwise use ceil
36054 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36055 colSpan = Math.min( colSpan, this.cols );
36057 // normally this should be '1' as we dont' currently allow multi width columns..
36059 var colGroup = this._getColGroup( colSpan );
36060 // get the minimum Y value from the columns
36061 var minimumY = Math.min.apply( Math, colGroup );
36062 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36064 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36066 // position the brick
36068 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36069 y: this.currentSize.y + minimumY + this.padHeight
36073 // apply setHeight to necessary columns
36074 var setHeight = minimumY + sz.height + this.padHeight;
36075 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36077 var setSpan = this.cols + 1 - colGroup.length;
36078 for ( var i = 0; i < setSpan; i++ ) {
36079 this.colYs[ shortColIndex + i ] = setHeight ;
36086 * @param {Number} colSpan - number of columns the element spans
36087 * @returns {Array} colGroup
36089 _getColGroup : function( colSpan )
36091 if ( colSpan < 2 ) {
36092 // if brick spans only one column, use all the column Ys
36097 // how many different places could this brick fit horizontally
36098 var groupCount = this.cols + 1 - colSpan;
36099 // for each group potential horizontal position
36100 for ( var i = 0; i < groupCount; i++ ) {
36101 // make an array of colY values for that one group
36102 var groupColYs = this.colYs.slice( i, i + colSpan );
36103 // and get the max value of the array
36104 colGroup[i] = Math.max.apply( Math, groupColYs );
36109 _manageStamp : function( stamp )
36111 var stampSize = stamp.getSize();
36112 var offset = stamp.getBox();
36113 // get the columns that this stamp affects
36114 var firstX = this.isOriginLeft ? offset.x : offset.right;
36115 var lastX = firstX + stampSize.width;
36116 var firstCol = Math.floor( firstX / this.columnWidth );
36117 firstCol = Math.max( 0, firstCol );
36119 var lastCol = Math.floor( lastX / this.columnWidth );
36120 // lastCol should not go over if multiple of columnWidth #425
36121 lastCol -= lastX % this.columnWidth ? 0 : 1;
36122 lastCol = Math.min( this.cols - 1, lastCol );
36124 // set colYs to bottom of the stamp
36125 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36128 for ( var i = firstCol; i <= lastCol; i++ ) {
36129 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36134 _getContainerSize : function()
36136 this.maxY = Math.max.apply( Math, this.colYs );
36141 if ( this.isFitWidth ) {
36142 size.width = this._getContainerFitWidth();
36148 _getContainerFitWidth : function()
36150 var unusedCols = 0;
36151 // count unused columns
36154 if ( this.colYs[i] !== 0 ) {
36159 // fit container to columns that have been used
36160 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36163 needsResizeLayout : function()
36165 var previousWidth = this.containerWidth;
36166 this.getContainerWidth();
36167 return previousWidth !== this.containerWidth;
36182 * @class Roo.bootstrap.MasonryBrick
36183 * @extends Roo.bootstrap.Component
36184 * Bootstrap MasonryBrick class
36187 * Create a new MasonryBrick
36188 * @param {Object} config The config object
36191 Roo.bootstrap.MasonryBrick = function(config){
36193 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36195 Roo.bootstrap.MasonryBrick.register(this);
36201 * When a MasonryBrick is clcik
36202 * @param {Roo.bootstrap.MasonryBrick} this
36203 * @param {Roo.EventObject} e
36209 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36212 * @cfg {String} title
36216 * @cfg {String} html
36220 * @cfg {String} bgimage
36224 * @cfg {String} videourl
36228 * @cfg {String} cls
36232 * @cfg {String} href
36236 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36241 * @cfg {String} placetitle (center|bottom)
36246 * @cfg {Boolean} isFitContainer defalut true
36248 isFitContainer : true,
36251 * @cfg {Boolean} preventDefault defalut false
36253 preventDefault : false,
36256 * @cfg {Boolean} inverse defalut false
36258 maskInverse : false,
36260 getAutoCreate : function()
36262 if(!this.isFitContainer){
36263 return this.getSplitAutoCreate();
36266 var cls = 'masonry-brick masonry-brick-full';
36268 if(this.href.length){
36269 cls += ' masonry-brick-link';
36272 if(this.bgimage.length){
36273 cls += ' masonry-brick-image';
36276 if(this.maskInverse){
36277 cls += ' mask-inverse';
36280 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36281 cls += ' enable-mask';
36285 cls += ' masonry-' + this.size + '-brick';
36288 if(this.placetitle.length){
36290 switch (this.placetitle) {
36292 cls += ' masonry-center-title';
36295 cls += ' masonry-bottom-title';
36302 if(!this.html.length && !this.bgimage.length){
36303 cls += ' masonry-center-title';
36306 if(!this.html.length && this.bgimage.length){
36307 cls += ' masonry-bottom-title';
36312 cls += ' ' + this.cls;
36316 tag: (this.href.length) ? 'a' : 'div',
36321 cls: 'masonry-brick-mask'
36325 cls: 'masonry-brick-paragraph',
36331 if(this.href.length){
36332 cfg.href = this.href;
36335 var cn = cfg.cn[1].cn;
36337 if(this.title.length){
36340 cls: 'masonry-brick-title',
36345 if(this.html.length){
36348 cls: 'masonry-brick-text',
36353 if (!this.title.length && !this.html.length) {
36354 cfg.cn[1].cls += ' hide';
36357 if(this.bgimage.length){
36360 cls: 'masonry-brick-image-view',
36365 if(this.videourl.length){
36366 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36367 // youtube support only?
36370 cls: 'masonry-brick-image-view',
36373 allowfullscreen : true
36381 getSplitAutoCreate : function()
36383 var cls = 'masonry-brick masonry-brick-split';
36385 if(this.href.length){
36386 cls += ' masonry-brick-link';
36389 if(this.bgimage.length){
36390 cls += ' masonry-brick-image';
36394 cls += ' masonry-' + this.size + '-brick';
36397 switch (this.placetitle) {
36399 cls += ' masonry-center-title';
36402 cls += ' masonry-bottom-title';
36405 if(!this.bgimage.length){
36406 cls += ' masonry-center-title';
36409 if(this.bgimage.length){
36410 cls += ' masonry-bottom-title';
36416 cls += ' ' + this.cls;
36420 tag: (this.href.length) ? 'a' : 'div',
36425 cls: 'masonry-brick-split-head',
36429 cls: 'masonry-brick-paragraph',
36436 cls: 'masonry-brick-split-body',
36442 if(this.href.length){
36443 cfg.href = this.href;
36446 if(this.title.length){
36447 cfg.cn[0].cn[0].cn.push({
36449 cls: 'masonry-brick-title',
36454 if(this.html.length){
36455 cfg.cn[1].cn.push({
36457 cls: 'masonry-brick-text',
36462 if(this.bgimage.length){
36463 cfg.cn[0].cn.push({
36465 cls: 'masonry-brick-image-view',
36470 if(this.videourl.length){
36471 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36472 // youtube support only?
36473 cfg.cn[0].cn.cn.push({
36475 cls: 'masonry-brick-image-view',
36478 allowfullscreen : true
36485 initEvents: function()
36487 switch (this.size) {
36520 this.el.on('touchstart', this.onTouchStart, this);
36521 this.el.on('touchmove', this.onTouchMove, this);
36522 this.el.on('touchend', this.onTouchEnd, this);
36523 this.el.on('contextmenu', this.onContextMenu, this);
36525 this.el.on('mouseenter' ,this.enter, this);
36526 this.el.on('mouseleave', this.leave, this);
36527 this.el.on('click', this.onClick, this);
36530 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36531 this.parent().bricks.push(this);
36536 onClick: function(e, el)
36538 var time = this.endTimer - this.startTimer;
36539 // Roo.log(e.preventDefault());
36542 e.preventDefault();
36547 if(!this.preventDefault){
36551 e.preventDefault();
36553 if (this.activeClass != '') {
36554 this.selectBrick();
36557 this.fireEvent('click', this, e);
36560 enter: function(e, el)
36562 e.preventDefault();
36564 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36568 if(this.bgimage.length && this.html.length){
36569 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36573 leave: function(e, el)
36575 e.preventDefault();
36577 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36581 if(this.bgimage.length && this.html.length){
36582 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36586 onTouchStart: function(e, el)
36588 // e.preventDefault();
36590 this.touchmoved = false;
36592 if(!this.isFitContainer){
36596 if(!this.bgimage.length || !this.html.length){
36600 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36602 this.timer = new Date().getTime();
36606 onTouchMove: function(e, el)
36608 this.touchmoved = true;
36611 onContextMenu : function(e,el)
36613 e.preventDefault();
36614 e.stopPropagation();
36618 onTouchEnd: function(e, el)
36620 // e.preventDefault();
36622 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36629 if(!this.bgimage.length || !this.html.length){
36631 if(this.href.length){
36632 window.location.href = this.href;
36638 if(!this.isFitContainer){
36642 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36644 window.location.href = this.href;
36647 //selection on single brick only
36648 selectBrick : function() {
36650 if (!this.parentId) {
36654 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36655 var index = m.selectedBrick.indexOf(this.id);
36658 m.selectedBrick.splice(index,1);
36659 this.el.removeClass(this.activeClass);
36663 for(var i = 0; i < m.selectedBrick.length; i++) {
36664 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36665 b.el.removeClass(b.activeClass);
36668 m.selectedBrick = [];
36670 m.selectedBrick.push(this.id);
36671 this.el.addClass(this.activeClass);
36675 isSelected : function(){
36676 return this.el.hasClass(this.activeClass);
36681 Roo.apply(Roo.bootstrap.MasonryBrick, {
36684 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36686 * register a Masonry Brick
36687 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36690 register : function(brick)
36692 //this.groups[brick.id] = brick;
36693 this.groups.add(brick.id, brick);
36696 * fetch a masonry brick based on the masonry brick ID
36697 * @param {string} the masonry brick to add
36698 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36701 get: function(brick_id)
36703 // if (typeof(this.groups[brick_id]) == 'undefined') {
36706 // return this.groups[brick_id] ;
36708 if(this.groups.key(brick_id)) {
36709 return this.groups.key(brick_id);
36727 * @class Roo.bootstrap.Brick
36728 * @extends Roo.bootstrap.Component
36729 * Bootstrap Brick class
36732 * Create a new Brick
36733 * @param {Object} config The config object
36736 Roo.bootstrap.Brick = function(config){
36737 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36743 * When a Brick is click
36744 * @param {Roo.bootstrap.Brick} this
36745 * @param {Roo.EventObject} e
36751 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36754 * @cfg {String} title
36758 * @cfg {String} html
36762 * @cfg {String} bgimage
36766 * @cfg {String} cls
36770 * @cfg {String} href
36774 * @cfg {String} video
36778 * @cfg {Boolean} square
36782 getAutoCreate : function()
36784 var cls = 'roo-brick';
36786 if(this.href.length){
36787 cls += ' roo-brick-link';
36790 if(this.bgimage.length){
36791 cls += ' roo-brick-image';
36794 if(!this.html.length && !this.bgimage.length){
36795 cls += ' roo-brick-center-title';
36798 if(!this.html.length && this.bgimage.length){
36799 cls += ' roo-brick-bottom-title';
36803 cls += ' ' + this.cls;
36807 tag: (this.href.length) ? 'a' : 'div',
36812 cls: 'roo-brick-paragraph',
36818 if(this.href.length){
36819 cfg.href = this.href;
36822 var cn = cfg.cn[0].cn;
36824 if(this.title.length){
36827 cls: 'roo-brick-title',
36832 if(this.html.length){
36835 cls: 'roo-brick-text',
36842 if(this.bgimage.length){
36845 cls: 'roo-brick-image-view',
36853 initEvents: function()
36855 if(this.title.length || this.html.length){
36856 this.el.on('mouseenter' ,this.enter, this);
36857 this.el.on('mouseleave', this.leave, this);
36860 Roo.EventManager.onWindowResize(this.resize, this);
36862 if(this.bgimage.length){
36863 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36864 this.imageEl.on('load', this.onImageLoad, this);
36871 onImageLoad : function()
36876 resize : function()
36878 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36880 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36882 if(this.bgimage.length){
36883 var image = this.el.select('.roo-brick-image-view', true).first();
36885 image.setWidth(paragraph.getWidth());
36888 image.setHeight(paragraph.getWidth());
36891 this.el.setHeight(image.getHeight());
36892 paragraph.setHeight(image.getHeight());
36898 enter: function(e, el)
36900 e.preventDefault();
36902 if(this.bgimage.length){
36903 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36904 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36908 leave: function(e, el)
36910 e.preventDefault();
36912 if(this.bgimage.length){
36913 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36914 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36929 * @class Roo.bootstrap.NumberField
36930 * @extends Roo.bootstrap.Input
36931 * Bootstrap NumberField class
36937 * Create a new NumberField
36938 * @param {Object} config The config object
36941 Roo.bootstrap.NumberField = function(config){
36942 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36945 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36948 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36950 allowDecimals : true,
36952 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36954 decimalSeparator : ".",
36956 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36958 decimalPrecision : 2,
36960 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36962 allowNegative : true,
36965 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36969 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36971 minValue : Number.NEGATIVE_INFINITY,
36973 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36975 maxValue : Number.MAX_VALUE,
36977 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36979 minText : "The minimum value for this field is {0}",
36981 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36983 maxText : "The maximum value for this field is {0}",
36985 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36986 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36988 nanText : "{0} is not a valid number",
36990 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36992 thousandsDelimiter : false,
36994 * @cfg {String} valueAlign alignment of value
36996 valueAlign : "left",
36998 getAutoCreate : function()
37000 var hiddenInput = {
37004 cls: 'hidden-number-input'
37008 hiddenInput.name = this.name;
37013 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37015 this.name = hiddenInput.name;
37017 if(cfg.cn.length > 0) {
37018 cfg.cn.push(hiddenInput);
37025 initEvents : function()
37027 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37029 var allowed = "0123456789";
37031 if(this.allowDecimals){
37032 allowed += this.decimalSeparator;
37035 if(this.allowNegative){
37039 if(this.thousandsDelimiter) {
37043 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37045 var keyPress = function(e){
37047 var k = e.getKey();
37049 var c = e.getCharCode();
37052 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37053 allowed.indexOf(String.fromCharCode(c)) === -1
37059 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37063 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37068 this.el.on("keypress", keyPress, this);
37071 validateValue : function(value)
37074 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37078 var num = this.parseValue(value);
37081 this.markInvalid(String.format(this.nanText, value));
37085 if(num < this.minValue){
37086 this.markInvalid(String.format(this.minText, this.minValue));
37090 if(num > this.maxValue){
37091 this.markInvalid(String.format(this.maxText, this.maxValue));
37098 getValue : function()
37100 var v = this.hiddenEl().getValue();
37102 return this.fixPrecision(this.parseValue(v));
37105 parseValue : function(value)
37107 if(this.thousandsDelimiter) {
37109 r = new RegExp(",", "g");
37110 value = value.replace(r, "");
37113 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37114 return isNaN(value) ? '' : value;
37117 fixPrecision : function(value)
37119 if(this.thousandsDelimiter) {
37121 r = new RegExp(",", "g");
37122 value = value.replace(r, "");
37125 var nan = isNaN(value);
37127 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37128 return nan ? '' : value;
37130 return parseFloat(value).toFixed(this.decimalPrecision);
37133 setValue : function(v)
37135 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37141 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37143 this.inputEl().dom.value = (v == '') ? '' :
37144 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37146 if(!this.allowZero && v === '0') {
37147 this.hiddenEl().dom.value = '';
37148 this.inputEl().dom.value = '';
37155 decimalPrecisionFcn : function(v)
37157 return Math.floor(v);
37160 beforeBlur : function()
37162 var v = this.parseValue(this.getRawValue());
37164 if(v || v === 0 || v === ''){
37169 hiddenEl : function()
37171 return this.el.select('input.hidden-number-input',true).first();
37183 * @class Roo.bootstrap.DocumentSlider
37184 * @extends Roo.bootstrap.Component
37185 * Bootstrap DocumentSlider class
37188 * Create a new DocumentViewer
37189 * @param {Object} config The config object
37192 Roo.bootstrap.DocumentSlider = function(config){
37193 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37200 * Fire after initEvent
37201 * @param {Roo.bootstrap.DocumentSlider} this
37206 * Fire after update
37207 * @param {Roo.bootstrap.DocumentSlider} this
37213 * @param {Roo.bootstrap.DocumentSlider} this
37219 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37225 getAutoCreate : function()
37229 cls : 'roo-document-slider',
37233 cls : 'roo-document-slider-header',
37237 cls : 'roo-document-slider-header-title'
37243 cls : 'roo-document-slider-body',
37247 cls : 'roo-document-slider-prev',
37251 cls : 'fa fa-chevron-left'
37257 cls : 'roo-document-slider-thumb',
37261 cls : 'roo-document-slider-image'
37267 cls : 'roo-document-slider-next',
37271 cls : 'fa fa-chevron-right'
37283 initEvents : function()
37285 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37286 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37288 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37289 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37291 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37292 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37294 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37295 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37297 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37298 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37300 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37301 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37303 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37304 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37306 this.thumbEl.on('click', this.onClick, this);
37308 this.prevIndicator.on('click', this.prev, this);
37310 this.nextIndicator.on('click', this.next, this);
37314 initial : function()
37316 if(this.files.length){
37317 this.indicator = 1;
37321 this.fireEvent('initial', this);
37324 update : function()
37326 this.imageEl.attr('src', this.files[this.indicator - 1]);
37328 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37330 this.prevIndicator.show();
37332 if(this.indicator == 1){
37333 this.prevIndicator.hide();
37336 this.nextIndicator.show();
37338 if(this.indicator == this.files.length){
37339 this.nextIndicator.hide();
37342 this.thumbEl.scrollTo('top');
37344 this.fireEvent('update', this);
37347 onClick : function(e)
37349 e.preventDefault();
37351 this.fireEvent('click', this);
37356 e.preventDefault();
37358 this.indicator = Math.max(1, this.indicator - 1);
37365 e.preventDefault();
37367 this.indicator = Math.min(this.files.length, this.indicator + 1);
37381 * @class Roo.bootstrap.RadioSet
37382 * @extends Roo.bootstrap.Input
37383 * Bootstrap RadioSet class
37384 * @cfg {String} indicatorpos (left|right) default left
37385 * @cfg {Boolean} inline (true|false) inline the element (default true)
37386 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37388 * Create a new RadioSet
37389 * @param {Object} config The config object
37392 Roo.bootstrap.RadioSet = function(config){
37394 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37398 Roo.bootstrap.RadioSet.register(this);
37403 * Fires when the element is checked or unchecked.
37404 * @param {Roo.bootstrap.RadioSet} this This radio
37405 * @param {Roo.bootstrap.Radio} item The checked item
37410 * Fires when the element is click.
37411 * @param {Roo.bootstrap.RadioSet} this This radio set
37412 * @param {Roo.bootstrap.Radio} item The checked item
37413 * @param {Roo.EventObject} e The event object
37420 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37428 indicatorpos : 'left',
37430 getAutoCreate : function()
37434 cls : 'roo-radio-set-label',
37438 html : this.fieldLabel
37442 if (Roo.bootstrap.version == 3) {
37445 if(this.indicatorpos == 'left'){
37448 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37449 tooltip : 'This field is required'
37454 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37455 tooltip : 'This field is required'
37461 cls : 'roo-radio-set-items'
37464 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37466 if (align === 'left' && this.fieldLabel.length) {
37469 cls : "roo-radio-set-right",
37475 if(this.labelWidth > 12){
37476 label.style = "width: " + this.labelWidth + 'px';
37479 if(this.labelWidth < 13 && this.labelmd == 0){
37480 this.labelmd = this.labelWidth;
37483 if(this.labellg > 0){
37484 label.cls += ' col-lg-' + this.labellg;
37485 items.cls += ' col-lg-' + (12 - this.labellg);
37488 if(this.labelmd > 0){
37489 label.cls += ' col-md-' + this.labelmd;
37490 items.cls += ' col-md-' + (12 - this.labelmd);
37493 if(this.labelsm > 0){
37494 label.cls += ' col-sm-' + this.labelsm;
37495 items.cls += ' col-sm-' + (12 - this.labelsm);
37498 if(this.labelxs > 0){
37499 label.cls += ' col-xs-' + this.labelxs;
37500 items.cls += ' col-xs-' + (12 - this.labelxs);
37506 cls : 'roo-radio-set',
37510 cls : 'roo-radio-set-input',
37513 value : this.value ? this.value : ''
37520 if(this.weight.length){
37521 cfg.cls += ' roo-radio-' + this.weight;
37525 cfg.cls += ' roo-radio-set-inline';
37529 ['xs','sm','md','lg'].map(function(size){
37530 if (settings[size]) {
37531 cfg.cls += ' col-' + size + '-' + settings[size];
37539 initEvents : function()
37541 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37542 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37544 if(!this.fieldLabel.length){
37545 this.labelEl.hide();
37548 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37549 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37551 this.indicator = this.indicatorEl();
37553 if(this.indicator){
37554 this.indicator.addClass('invisible');
37557 this.originalValue = this.getValue();
37561 inputEl: function ()
37563 return this.el.select('.roo-radio-set-input', true).first();
37566 getChildContainer : function()
37568 return this.itemsEl;
37571 register : function(item)
37573 this.radioes.push(item);
37577 validate : function()
37579 if(this.getVisibilityEl().hasClass('hidden')){
37585 Roo.each(this.radioes, function(i){
37594 if(this.allowBlank) {
37598 if(this.disabled || valid){
37603 this.markInvalid();
37608 markValid : function()
37610 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37611 this.indicatorEl().removeClass('visible');
37612 this.indicatorEl().addClass('invisible');
37616 if (Roo.bootstrap.version == 3) {
37617 this.el.removeClass([this.invalidClass, this.validClass]);
37618 this.el.addClass(this.validClass);
37620 this.el.removeClass(['is-invalid','is-valid']);
37621 this.el.addClass(['is-valid']);
37623 this.fireEvent('valid', this);
37626 markInvalid : function(msg)
37628 if(this.allowBlank || this.disabled){
37632 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37633 this.indicatorEl().removeClass('invisible');
37634 this.indicatorEl().addClass('visible');
37636 if (Roo.bootstrap.version == 3) {
37637 this.el.removeClass([this.invalidClass, this.validClass]);
37638 this.el.addClass(this.invalidClass);
37640 this.el.removeClass(['is-invalid','is-valid']);
37641 this.el.addClass(['is-invalid']);
37644 this.fireEvent('invalid', this, msg);
37648 setValue : function(v, suppressEvent)
37650 if(this.value === v){
37657 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37660 Roo.each(this.radioes, function(i){
37662 i.el.removeClass('checked');
37665 Roo.each(this.radioes, function(i){
37667 if(i.value === v || i.value.toString() === v.toString()){
37669 i.el.addClass('checked');
37671 if(suppressEvent !== true){
37672 this.fireEvent('check', this, i);
37683 clearInvalid : function(){
37685 if(!this.el || this.preventMark){
37689 this.el.removeClass([this.invalidClass]);
37691 this.fireEvent('valid', this);
37696 Roo.apply(Roo.bootstrap.RadioSet, {
37700 register : function(set)
37702 this.groups[set.name] = set;
37705 get: function(name)
37707 if (typeof(this.groups[name]) == 'undefined') {
37711 return this.groups[name] ;
37717 * Ext JS Library 1.1.1
37718 * Copyright(c) 2006-2007, Ext JS, LLC.
37720 * Originally Released Under LGPL - original licence link has changed is not relivant.
37723 * <script type="text/javascript">
37728 * @class Roo.bootstrap.SplitBar
37729 * @extends Roo.util.Observable
37730 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37734 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37735 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37736 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37737 split.minSize = 100;
37738 split.maxSize = 600;
37739 split.animate = true;
37740 split.on('moved', splitterMoved);
37743 * Create a new SplitBar
37744 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37745 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37746 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37747 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37748 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37749 position of the SplitBar).
37751 Roo.bootstrap.SplitBar = function(cfg){
37756 // dragElement : elm
37757 // resizingElement: el,
37759 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37760 // placement : Roo.bootstrap.SplitBar.LEFT ,
37761 // existingProxy ???
37764 this.el = Roo.get(cfg.dragElement, true);
37765 this.el.dom.unselectable = "on";
37767 this.resizingEl = Roo.get(cfg.resizingElement, true);
37771 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37772 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37775 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37778 * The minimum size of the resizing element. (Defaults to 0)
37784 * The maximum size of the resizing element. (Defaults to 2000)
37787 this.maxSize = 2000;
37790 * Whether to animate the transition to the new size
37793 this.animate = false;
37796 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37799 this.useShim = false;
37804 if(!cfg.existingProxy){
37806 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37808 this.proxy = Roo.get(cfg.existingProxy).dom;
37811 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37814 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37817 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37820 this.dragSpecs = {};
37823 * @private The adapter to use to positon and resize elements
37825 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37826 this.adapter.init(this);
37828 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37830 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37831 this.el.addClass("roo-splitbar-h");
37834 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37835 this.el.addClass("roo-splitbar-v");
37841 * Fires when the splitter is moved (alias for {@link #event-moved})
37842 * @param {Roo.bootstrap.SplitBar} this
37843 * @param {Number} newSize the new width or height
37848 * Fires when the splitter is moved
37849 * @param {Roo.bootstrap.SplitBar} this
37850 * @param {Number} newSize the new width or height
37854 * @event beforeresize
37855 * Fires before the splitter is dragged
37856 * @param {Roo.bootstrap.SplitBar} this
37858 "beforeresize" : true,
37860 "beforeapply" : true
37863 Roo.util.Observable.call(this);
37866 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37867 onStartProxyDrag : function(x, y){
37868 this.fireEvent("beforeresize", this);
37870 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37872 o.enableDisplayMode("block");
37873 // all splitbars share the same overlay
37874 Roo.bootstrap.SplitBar.prototype.overlay = o;
37876 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37877 this.overlay.show();
37878 Roo.get(this.proxy).setDisplayed("block");
37879 var size = this.adapter.getElementSize(this);
37880 this.activeMinSize = this.getMinimumSize();;
37881 this.activeMaxSize = this.getMaximumSize();;
37882 var c1 = size - this.activeMinSize;
37883 var c2 = Math.max(this.activeMaxSize - size, 0);
37884 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37885 this.dd.resetConstraints();
37886 this.dd.setXConstraint(
37887 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37888 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37890 this.dd.setYConstraint(0, 0);
37892 this.dd.resetConstraints();
37893 this.dd.setXConstraint(0, 0);
37894 this.dd.setYConstraint(
37895 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37896 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37899 this.dragSpecs.startSize = size;
37900 this.dragSpecs.startPoint = [x, y];
37901 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37905 * @private Called after the drag operation by the DDProxy
37907 onEndProxyDrag : function(e){
37908 Roo.get(this.proxy).setDisplayed(false);
37909 var endPoint = Roo.lib.Event.getXY(e);
37911 this.overlay.hide();
37914 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37915 newSize = this.dragSpecs.startSize +
37916 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37917 endPoint[0] - this.dragSpecs.startPoint[0] :
37918 this.dragSpecs.startPoint[0] - endPoint[0]
37921 newSize = this.dragSpecs.startSize +
37922 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37923 endPoint[1] - this.dragSpecs.startPoint[1] :
37924 this.dragSpecs.startPoint[1] - endPoint[1]
37927 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37928 if(newSize != this.dragSpecs.startSize){
37929 if(this.fireEvent('beforeapply', this, newSize) !== false){
37930 this.adapter.setElementSize(this, newSize);
37931 this.fireEvent("moved", this, newSize);
37932 this.fireEvent("resize", this, newSize);
37938 * Get the adapter this SplitBar uses
37939 * @return The adapter object
37941 getAdapter : function(){
37942 return this.adapter;
37946 * Set the adapter this SplitBar uses
37947 * @param {Object} adapter A SplitBar adapter object
37949 setAdapter : function(adapter){
37950 this.adapter = adapter;
37951 this.adapter.init(this);
37955 * Gets the minimum size for the resizing element
37956 * @return {Number} The minimum size
37958 getMinimumSize : function(){
37959 return this.minSize;
37963 * Sets the minimum size for the resizing element
37964 * @param {Number} minSize The minimum size
37966 setMinimumSize : function(minSize){
37967 this.minSize = minSize;
37971 * Gets the maximum size for the resizing element
37972 * @return {Number} The maximum size
37974 getMaximumSize : function(){
37975 return this.maxSize;
37979 * Sets the maximum size for the resizing element
37980 * @param {Number} maxSize The maximum size
37982 setMaximumSize : function(maxSize){
37983 this.maxSize = maxSize;
37987 * Sets the initialize size for the resizing element
37988 * @param {Number} size The initial size
37990 setCurrentSize : function(size){
37991 var oldAnimate = this.animate;
37992 this.animate = false;
37993 this.adapter.setElementSize(this, size);
37994 this.animate = oldAnimate;
37998 * Destroy this splitbar.
37999 * @param {Boolean} removeEl True to remove the element
38001 destroy : function(removeEl){
38003 this.shim.remove();
38006 this.proxy.parentNode.removeChild(this.proxy);
38014 * @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.
38016 Roo.bootstrap.SplitBar.createProxy = function(dir){
38017 var proxy = new Roo.Element(document.createElement("div"));
38018 proxy.unselectable();
38019 var cls = 'roo-splitbar-proxy';
38020 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38021 document.body.appendChild(proxy.dom);
38026 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38027 * Default Adapter. It assumes the splitter and resizing element are not positioned
38028 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38030 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38033 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38034 // do nothing for now
38035 init : function(s){
38039 * Called before drag operations to get the current size of the resizing element.
38040 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38042 getElementSize : function(s){
38043 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38044 return s.resizingEl.getWidth();
38046 return s.resizingEl.getHeight();
38051 * Called after drag operations to set the size of the resizing element.
38052 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38053 * @param {Number} newSize The new size to set
38054 * @param {Function} onComplete A function to be invoked when resizing is complete
38056 setElementSize : function(s, newSize, onComplete){
38057 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38059 s.resizingEl.setWidth(newSize);
38061 onComplete(s, newSize);
38064 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38069 s.resizingEl.setHeight(newSize);
38071 onComplete(s, newSize);
38074 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38081 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38082 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38083 * Adapter that moves the splitter element to align with the resized sizing element.
38084 * Used with an absolute positioned SplitBar.
38085 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38086 * document.body, make sure you assign an id to the body element.
38088 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38089 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38090 this.container = Roo.get(container);
38093 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38094 init : function(s){
38095 this.basic.init(s);
38098 getElementSize : function(s){
38099 return this.basic.getElementSize(s);
38102 setElementSize : function(s, newSize, onComplete){
38103 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38106 moveSplitter : function(s){
38107 var yes = Roo.bootstrap.SplitBar;
38108 switch(s.placement){
38110 s.el.setX(s.resizingEl.getRight());
38113 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38116 s.el.setY(s.resizingEl.getBottom());
38119 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38126 * Orientation constant - Create a vertical SplitBar
38130 Roo.bootstrap.SplitBar.VERTICAL = 1;
38133 * Orientation constant - Create a horizontal SplitBar
38137 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38140 * Placement constant - The resizing element is to the left of the splitter element
38144 Roo.bootstrap.SplitBar.LEFT = 1;
38147 * Placement constant - The resizing element is to the right of the splitter element
38151 Roo.bootstrap.SplitBar.RIGHT = 2;
38154 * Placement constant - The resizing element is positioned above the splitter element
38158 Roo.bootstrap.SplitBar.TOP = 3;
38161 * Placement constant - The resizing element is positioned under splitter element
38165 Roo.bootstrap.SplitBar.BOTTOM = 4;
38166 Roo.namespace("Roo.bootstrap.layout");/*
38168 * Ext JS Library 1.1.1
38169 * Copyright(c) 2006-2007, Ext JS, LLC.
38171 * Originally Released Under LGPL - original licence link has changed is not relivant.
38174 * <script type="text/javascript">
38178 * @class Roo.bootstrap.layout.Manager
38179 * @extends Roo.bootstrap.Component
38180 * Base class for layout managers.
38182 Roo.bootstrap.layout.Manager = function(config)
38184 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38190 /** false to disable window resize monitoring @type Boolean */
38191 this.monitorWindowResize = true;
38196 * Fires when a layout is performed.
38197 * @param {Roo.LayoutManager} this
38201 * @event regionresized
38202 * Fires when the user resizes a region.
38203 * @param {Roo.LayoutRegion} region The resized region
38204 * @param {Number} newSize The new size (width for east/west, height for north/south)
38206 "regionresized" : true,
38208 * @event regioncollapsed
38209 * Fires when a region is collapsed.
38210 * @param {Roo.LayoutRegion} region The collapsed region
38212 "regioncollapsed" : true,
38214 * @event regionexpanded
38215 * Fires when a region is expanded.
38216 * @param {Roo.LayoutRegion} region The expanded region
38218 "regionexpanded" : true
38220 this.updating = false;
38223 this.el = Roo.get(config.el);
38229 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38234 monitorWindowResize : true,
38240 onRender : function(ct, position)
38243 this.el = Roo.get(ct);
38246 //this.fireEvent('render',this);
38250 initEvents: function()
38254 // ie scrollbar fix
38255 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38256 document.body.scroll = "no";
38257 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38258 this.el.position('relative');
38260 this.id = this.el.id;
38261 this.el.addClass("roo-layout-container");
38262 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38263 if(this.el.dom != document.body ) {
38264 this.el.on('resize', this.layout,this);
38265 this.el.on('show', this.layout,this);
38271 * Returns true if this layout is currently being updated
38272 * @return {Boolean}
38274 isUpdating : function(){
38275 return this.updating;
38279 * Suspend the LayoutManager from doing auto-layouts while
38280 * making multiple add or remove calls
38282 beginUpdate : function(){
38283 this.updating = true;
38287 * Restore auto-layouts and optionally disable the manager from performing a layout
38288 * @param {Boolean} noLayout true to disable a layout update
38290 endUpdate : function(noLayout){
38291 this.updating = false;
38297 layout: function(){
38301 onRegionResized : function(region, newSize){
38302 this.fireEvent("regionresized", region, newSize);
38306 onRegionCollapsed : function(region){
38307 this.fireEvent("regioncollapsed", region);
38310 onRegionExpanded : function(region){
38311 this.fireEvent("regionexpanded", region);
38315 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38316 * performs box-model adjustments.
38317 * @return {Object} The size as an object {width: (the width), height: (the height)}
38319 getViewSize : function()
38322 if(this.el.dom != document.body){
38323 size = this.el.getSize();
38325 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38327 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38328 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38333 * Returns the Element this layout is bound to.
38334 * @return {Roo.Element}
38336 getEl : function(){
38341 * Returns the specified region.
38342 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38343 * @return {Roo.LayoutRegion}
38345 getRegion : function(target){
38346 return this.regions[target.toLowerCase()];
38349 onWindowResize : function(){
38350 if(this.monitorWindowResize){
38357 * Ext JS Library 1.1.1
38358 * Copyright(c) 2006-2007, Ext JS, LLC.
38360 * Originally Released Under LGPL - original licence link has changed is not relivant.
38363 * <script type="text/javascript">
38366 * @class Roo.bootstrap.layout.Border
38367 * @extends Roo.bootstrap.layout.Manager
38368 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38369 * please see: examples/bootstrap/nested.html<br><br>
38371 <b>The container the layout is rendered into can be either the body element or any other element.
38372 If it is not the body element, the container needs to either be an absolute positioned element,
38373 or you will need to add "position:relative" to the css of the container. You will also need to specify
38374 the container size if it is not the body element.</b>
38377 * Create a new Border
38378 * @param {Object} config Configuration options
38380 Roo.bootstrap.layout.Border = function(config){
38381 config = config || {};
38382 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38386 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38387 if(config[region]){
38388 config[region].region = region;
38389 this.addRegion(config[region]);
38395 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38397 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38399 parent : false, // this might point to a 'nest' or a ???
38402 * Creates and adds a new region if it doesn't already exist.
38403 * @param {String} target The target region key (north, south, east, west or center).
38404 * @param {Object} config The regions config object
38405 * @return {BorderLayoutRegion} The new region
38407 addRegion : function(config)
38409 if(!this.regions[config.region]){
38410 var r = this.factory(config);
38411 this.bindRegion(r);
38413 return this.regions[config.region];
38417 bindRegion : function(r){
38418 this.regions[r.config.region] = r;
38420 r.on("visibilitychange", this.layout, this);
38421 r.on("paneladded", this.layout, this);
38422 r.on("panelremoved", this.layout, this);
38423 r.on("invalidated", this.layout, this);
38424 r.on("resized", this.onRegionResized, this);
38425 r.on("collapsed", this.onRegionCollapsed, this);
38426 r.on("expanded", this.onRegionExpanded, this);
38430 * Performs a layout update.
38432 layout : function()
38434 if(this.updating) {
38438 // render all the rebions if they have not been done alreayd?
38439 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38440 if(this.regions[region] && !this.regions[region].bodyEl){
38441 this.regions[region].onRender(this.el)
38445 var size = this.getViewSize();
38446 var w = size.width;
38447 var h = size.height;
38452 //var x = 0, y = 0;
38454 var rs = this.regions;
38455 var north = rs["north"];
38456 var south = rs["south"];
38457 var west = rs["west"];
38458 var east = rs["east"];
38459 var center = rs["center"];
38460 //if(this.hideOnLayout){ // not supported anymore
38461 //c.el.setStyle("display", "none");
38463 if(north && north.isVisible()){
38464 var b = north.getBox();
38465 var m = north.getMargins();
38466 b.width = w - (m.left+m.right);
38469 centerY = b.height + b.y + m.bottom;
38470 centerH -= centerY;
38471 north.updateBox(this.safeBox(b));
38473 if(south && south.isVisible()){
38474 var b = south.getBox();
38475 var m = south.getMargins();
38476 b.width = w - (m.left+m.right);
38478 var totalHeight = (b.height + m.top + m.bottom);
38479 b.y = h - totalHeight + m.top;
38480 centerH -= totalHeight;
38481 south.updateBox(this.safeBox(b));
38483 if(west && west.isVisible()){
38484 var b = west.getBox();
38485 var m = west.getMargins();
38486 b.height = centerH - (m.top+m.bottom);
38488 b.y = centerY + m.top;
38489 var totalWidth = (b.width + m.left + m.right);
38490 centerX += totalWidth;
38491 centerW -= totalWidth;
38492 west.updateBox(this.safeBox(b));
38494 if(east && east.isVisible()){
38495 var b = east.getBox();
38496 var m = east.getMargins();
38497 b.height = centerH - (m.top+m.bottom);
38498 var totalWidth = (b.width + m.left + m.right);
38499 b.x = w - totalWidth + m.left;
38500 b.y = centerY + m.top;
38501 centerW -= totalWidth;
38502 east.updateBox(this.safeBox(b));
38505 var m = center.getMargins();
38507 x: centerX + m.left,
38508 y: centerY + m.top,
38509 width: centerW - (m.left+m.right),
38510 height: centerH - (m.top+m.bottom)
38512 //if(this.hideOnLayout){
38513 //center.el.setStyle("display", "block");
38515 center.updateBox(this.safeBox(centerBox));
38518 this.fireEvent("layout", this);
38522 safeBox : function(box){
38523 box.width = Math.max(0, box.width);
38524 box.height = Math.max(0, box.height);
38529 * Adds a ContentPanel (or subclass) to this layout.
38530 * @param {String} target The target region key (north, south, east, west or center).
38531 * @param {Roo.ContentPanel} panel The panel to add
38532 * @return {Roo.ContentPanel} The added panel
38534 add : function(target, panel){
38536 target = target.toLowerCase();
38537 return this.regions[target].add(panel);
38541 * Remove a ContentPanel (or subclass) to this layout.
38542 * @param {String} target The target region key (north, south, east, west or center).
38543 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38544 * @return {Roo.ContentPanel} The removed panel
38546 remove : function(target, panel){
38547 target = target.toLowerCase();
38548 return this.regions[target].remove(panel);
38552 * Searches all regions for a panel with the specified id
38553 * @param {String} panelId
38554 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38556 findPanel : function(panelId){
38557 var rs = this.regions;
38558 for(var target in rs){
38559 if(typeof rs[target] != "function"){
38560 var p = rs[target].getPanel(panelId);
38570 * Searches all regions for a panel with the specified id and activates (shows) it.
38571 * @param {String/ContentPanel} panelId The panels id or the panel itself
38572 * @return {Roo.ContentPanel} The shown panel or null
38574 showPanel : function(panelId) {
38575 var rs = this.regions;
38576 for(var target in rs){
38577 var r = rs[target];
38578 if(typeof r != "function"){
38579 if(r.hasPanel(panelId)){
38580 return r.showPanel(panelId);
38588 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38589 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38592 restoreState : function(provider){
38594 provider = Roo.state.Manager;
38596 var sm = new Roo.LayoutStateManager();
38597 sm.init(this, provider);
38603 * Adds a xtype elements to the layout.
38607 xtype : 'ContentPanel',
38614 xtype : 'NestedLayoutPanel',
38620 items : [ ... list of content panels or nested layout panels.. ]
38624 * @param {Object} cfg Xtype definition of item to add.
38626 addxtype : function(cfg)
38628 // basically accepts a pannel...
38629 // can accept a layout region..!?!?
38630 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38633 // theory? children can only be panels??
38635 //if (!cfg.xtype.match(/Panel$/)) {
38640 if (typeof(cfg.region) == 'undefined') {
38641 Roo.log("Failed to add Panel, region was not set");
38645 var region = cfg.region;
38651 xitems = cfg.items;
38656 if ( region == 'center') {
38657 Roo.log("Center: " + cfg.title);
38663 case 'Content': // ContentPanel (el, cfg)
38664 case 'Scroll': // ContentPanel (el, cfg)
38666 cfg.autoCreate = cfg.autoCreate || true;
38667 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38669 // var el = this.el.createChild();
38670 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38673 this.add(region, ret);
38677 case 'TreePanel': // our new panel!
38678 cfg.el = this.el.createChild();
38679 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38680 this.add(region, ret);
38685 // create a new Layout (which is a Border Layout...
38687 var clayout = cfg.layout;
38688 clayout.el = this.el.createChild();
38689 clayout.items = clayout.items || [];
38693 // replace this exitems with the clayout ones..
38694 xitems = clayout.items;
38696 // force background off if it's in center...
38697 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38698 cfg.background = false;
38700 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38703 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38704 //console.log('adding nested layout panel ' + cfg.toSource());
38705 this.add(region, ret);
38706 nb = {}; /// find first...
38711 // needs grid and region
38713 //var el = this.getRegion(region).el.createChild();
38715 *var el = this.el.createChild();
38716 // create the grid first...
38717 cfg.grid.container = el;
38718 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38721 if (region == 'center' && this.active ) {
38722 cfg.background = false;
38725 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38727 this.add(region, ret);
38729 if (cfg.background) {
38730 // render grid on panel activation (if panel background)
38731 ret.on('activate', function(gp) {
38732 if (!gp.grid.rendered) {
38733 // gp.grid.render(el);
38737 // cfg.grid.render(el);
38743 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38744 // it was the old xcomponent building that caused this before.
38745 // espeically if border is the top element in the tree.
38755 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38757 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38758 this.add(region, ret);
38762 throw "Can not add '" + cfg.xtype + "' to Border";
38768 this.beginUpdate();
38772 Roo.each(xitems, function(i) {
38773 region = nb && i.region ? i.region : false;
38775 var add = ret.addxtype(i);
38778 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38779 if (!i.background) {
38780 abn[region] = nb[region] ;
38787 // make the last non-background panel active..
38788 //if (nb) { Roo.log(abn); }
38791 for(var r in abn) {
38792 region = this.getRegion(r);
38794 // tried using nb[r], but it does not work..
38796 region.showPanel(abn[r]);
38807 factory : function(cfg)
38810 var validRegions = Roo.bootstrap.layout.Border.regions;
38812 var target = cfg.region;
38815 var r = Roo.bootstrap.layout;
38819 return new r.North(cfg);
38821 return new r.South(cfg);
38823 return new r.East(cfg);
38825 return new r.West(cfg);
38827 return new r.Center(cfg);
38829 throw 'Layout region "'+target+'" not supported.';
38836 * Ext JS Library 1.1.1
38837 * Copyright(c) 2006-2007, Ext JS, LLC.
38839 * Originally Released Under LGPL - original licence link has changed is not relivant.
38842 * <script type="text/javascript">
38846 * @class Roo.bootstrap.layout.Basic
38847 * @extends Roo.util.Observable
38848 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38849 * and does not have a titlebar, tabs or any other features. All it does is size and position
38850 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38851 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38852 * @cfg {string} region the region that it inhabits..
38853 * @cfg {bool} skipConfig skip config?
38857 Roo.bootstrap.layout.Basic = function(config){
38859 this.mgr = config.mgr;
38861 this.position = config.region;
38863 var skipConfig = config.skipConfig;
38867 * @scope Roo.BasicLayoutRegion
38871 * @event beforeremove
38872 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38873 * @param {Roo.LayoutRegion} this
38874 * @param {Roo.ContentPanel} panel The panel
38875 * @param {Object} e The cancel event object
38877 "beforeremove" : true,
38879 * @event invalidated
38880 * Fires when the layout for this region is changed.
38881 * @param {Roo.LayoutRegion} this
38883 "invalidated" : true,
38885 * @event visibilitychange
38886 * Fires when this region is shown or hidden
38887 * @param {Roo.LayoutRegion} this
38888 * @param {Boolean} visibility true or false
38890 "visibilitychange" : true,
38892 * @event paneladded
38893 * Fires when a panel is added.
38894 * @param {Roo.LayoutRegion} this
38895 * @param {Roo.ContentPanel} panel The panel
38897 "paneladded" : true,
38899 * @event panelremoved
38900 * Fires when a panel is removed.
38901 * @param {Roo.LayoutRegion} this
38902 * @param {Roo.ContentPanel} panel The panel
38904 "panelremoved" : true,
38906 * @event beforecollapse
38907 * Fires when this region before collapse.
38908 * @param {Roo.LayoutRegion} this
38910 "beforecollapse" : true,
38913 * Fires when this region is collapsed.
38914 * @param {Roo.LayoutRegion} this
38916 "collapsed" : true,
38919 * Fires when this region is expanded.
38920 * @param {Roo.LayoutRegion} this
38925 * Fires when this region is slid into view.
38926 * @param {Roo.LayoutRegion} this
38928 "slideshow" : true,
38931 * Fires when this region slides out of view.
38932 * @param {Roo.LayoutRegion} this
38934 "slidehide" : true,
38936 * @event panelactivated
38937 * Fires when a panel is activated.
38938 * @param {Roo.LayoutRegion} this
38939 * @param {Roo.ContentPanel} panel The activated panel
38941 "panelactivated" : true,
38944 * Fires when the user resizes this region.
38945 * @param {Roo.LayoutRegion} this
38946 * @param {Number} newSize The new size (width for east/west, height for north/south)
38950 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38951 this.panels = new Roo.util.MixedCollection();
38952 this.panels.getKey = this.getPanelId.createDelegate(this);
38954 this.activePanel = null;
38955 // ensure listeners are added...
38957 if (config.listeners || config.events) {
38958 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38959 listeners : config.listeners || {},
38960 events : config.events || {}
38964 if(skipConfig !== true){
38965 this.applyConfig(config);
38969 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38971 getPanelId : function(p){
38975 applyConfig : function(config){
38976 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38977 this.config = config;
38982 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38983 * the width, for horizontal (north, south) the height.
38984 * @param {Number} newSize The new width or height
38986 resizeTo : function(newSize){
38987 var el = this.el ? this.el :
38988 (this.activePanel ? this.activePanel.getEl() : null);
38990 switch(this.position){
38993 el.setWidth(newSize);
38994 this.fireEvent("resized", this, newSize);
38998 el.setHeight(newSize);
38999 this.fireEvent("resized", this, newSize);
39005 getBox : function(){
39006 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39009 getMargins : function(){
39010 return this.margins;
39013 updateBox : function(box){
39015 var el = this.activePanel.getEl();
39016 el.dom.style.left = box.x + "px";
39017 el.dom.style.top = box.y + "px";
39018 this.activePanel.setSize(box.width, box.height);
39022 * Returns the container element for this region.
39023 * @return {Roo.Element}
39025 getEl : function(){
39026 return this.activePanel;
39030 * Returns true if this region is currently visible.
39031 * @return {Boolean}
39033 isVisible : function(){
39034 return this.activePanel ? true : false;
39037 setActivePanel : function(panel){
39038 panel = this.getPanel(panel);
39039 if(this.activePanel && this.activePanel != panel){
39040 this.activePanel.setActiveState(false);
39041 this.activePanel.getEl().setLeftTop(-10000,-10000);
39043 this.activePanel = panel;
39044 panel.setActiveState(true);
39046 panel.setSize(this.box.width, this.box.height);
39048 this.fireEvent("panelactivated", this, panel);
39049 this.fireEvent("invalidated");
39053 * Show the specified panel.
39054 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39055 * @return {Roo.ContentPanel} The shown panel or null
39057 showPanel : function(panel){
39058 panel = this.getPanel(panel);
39060 this.setActivePanel(panel);
39066 * Get the active panel for this region.
39067 * @return {Roo.ContentPanel} The active panel or null
39069 getActivePanel : function(){
39070 return this.activePanel;
39074 * Add the passed ContentPanel(s)
39075 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39076 * @return {Roo.ContentPanel} The panel added (if only one was added)
39078 add : function(panel){
39079 if(arguments.length > 1){
39080 for(var i = 0, len = arguments.length; i < len; i++) {
39081 this.add(arguments[i]);
39085 if(this.hasPanel(panel)){
39086 this.showPanel(panel);
39089 var el = panel.getEl();
39090 if(el.dom.parentNode != this.mgr.el.dom){
39091 this.mgr.el.dom.appendChild(el.dom);
39093 if(panel.setRegion){
39094 panel.setRegion(this);
39096 this.panels.add(panel);
39097 el.setStyle("position", "absolute");
39098 if(!panel.background){
39099 this.setActivePanel(panel);
39100 if(this.config.initialSize && this.panels.getCount()==1){
39101 this.resizeTo(this.config.initialSize);
39104 this.fireEvent("paneladded", this, panel);
39109 * Returns true if the panel is in this region.
39110 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39111 * @return {Boolean}
39113 hasPanel : function(panel){
39114 if(typeof panel == "object"){ // must be panel obj
39115 panel = panel.getId();
39117 return this.getPanel(panel) ? true : false;
39121 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39122 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39123 * @param {Boolean} preservePanel Overrides the config preservePanel option
39124 * @return {Roo.ContentPanel} The panel that was removed
39126 remove : function(panel, preservePanel){
39127 panel = this.getPanel(panel);
39132 this.fireEvent("beforeremove", this, panel, e);
39133 if(e.cancel === true){
39136 var panelId = panel.getId();
39137 this.panels.removeKey(panelId);
39142 * Returns the panel specified or null if it's not in this region.
39143 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39144 * @return {Roo.ContentPanel}
39146 getPanel : function(id){
39147 if(typeof id == "object"){ // must be panel obj
39150 return this.panels.get(id);
39154 * Returns this regions position (north/south/east/west/center).
39157 getPosition: function(){
39158 return this.position;
39162 * Ext JS Library 1.1.1
39163 * Copyright(c) 2006-2007, Ext JS, LLC.
39165 * Originally Released Under LGPL - original licence link has changed is not relivant.
39168 * <script type="text/javascript">
39172 * @class Roo.bootstrap.layout.Region
39173 * @extends Roo.bootstrap.layout.Basic
39174 * This class represents a region in a layout manager.
39176 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39177 * @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})
39178 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39179 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39180 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39181 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39182 * @cfg {String} title The title for the region (overrides panel titles)
39183 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39184 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39185 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39186 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39187 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39188 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39189 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39190 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39191 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39192 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39194 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39195 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39196 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39197 * @cfg {Number} width For East/West panels
39198 * @cfg {Number} height For North/South panels
39199 * @cfg {Boolean} split To show the splitter
39200 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39202 * @cfg {string} cls Extra CSS classes to add to region
39204 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39205 * @cfg {string} region the region that it inhabits..
39208 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39209 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39211 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39212 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39213 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39215 Roo.bootstrap.layout.Region = function(config)
39217 this.applyConfig(config);
39219 var mgr = config.mgr;
39220 var pos = config.region;
39221 config.skipConfig = true;
39222 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39225 this.onRender(mgr.el);
39228 this.visible = true;
39229 this.collapsed = false;
39230 this.unrendered_panels = [];
39233 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39235 position: '', // set by wrapper (eg. north/south etc..)
39236 unrendered_panels : null, // unrendered panels.
39238 tabPosition : false,
39240 mgr: false, // points to 'Border'
39243 createBody : function(){
39244 /** This region's body element
39245 * @type Roo.Element */
39246 this.bodyEl = this.el.createChild({
39248 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39252 onRender: function(ctr, pos)
39254 var dh = Roo.DomHelper;
39255 /** This region's container element
39256 * @type Roo.Element */
39257 this.el = dh.append(ctr.dom, {
39259 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39261 /** This region's title element
39262 * @type Roo.Element */
39264 this.titleEl = dh.append(this.el.dom, {
39266 unselectable: "on",
39267 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39269 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39270 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39274 this.titleEl.enableDisplayMode();
39275 /** This region's title text element
39276 * @type HTMLElement */
39277 this.titleTextEl = this.titleEl.dom.firstChild;
39278 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39280 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39281 this.closeBtn.enableDisplayMode();
39282 this.closeBtn.on("click", this.closeClicked, this);
39283 this.closeBtn.hide();
39285 this.createBody(this.config);
39286 if(this.config.hideWhenEmpty){
39288 this.on("paneladded", this.validateVisibility, this);
39289 this.on("panelremoved", this.validateVisibility, this);
39291 if(this.autoScroll){
39292 this.bodyEl.setStyle("overflow", "auto");
39294 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39296 //if(c.titlebar !== false){
39297 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39298 this.titleEl.hide();
39300 this.titleEl.show();
39301 if(this.config.title){
39302 this.titleTextEl.innerHTML = this.config.title;
39306 if(this.config.collapsed){
39307 this.collapse(true);
39309 if(this.config.hidden){
39313 if (this.unrendered_panels && this.unrendered_panels.length) {
39314 for (var i =0;i< this.unrendered_panels.length; i++) {
39315 this.add(this.unrendered_panels[i]);
39317 this.unrendered_panels = null;
39323 applyConfig : function(c)
39326 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39327 var dh = Roo.DomHelper;
39328 if(c.titlebar !== false){
39329 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39330 this.collapseBtn.on("click", this.collapse, this);
39331 this.collapseBtn.enableDisplayMode();
39333 if(c.showPin === true || this.showPin){
39334 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39335 this.stickBtn.enableDisplayMode();
39336 this.stickBtn.on("click", this.expand, this);
39337 this.stickBtn.hide();
39342 /** This region's collapsed element
39343 * @type Roo.Element */
39346 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39347 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39350 if(c.floatable !== false){
39351 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39352 this.collapsedEl.on("click", this.collapseClick, this);
39355 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39356 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39357 id: "message", unselectable: "on", style:{"float":"left"}});
39358 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39360 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39361 this.expandBtn.on("click", this.expand, this);
39365 if(this.collapseBtn){
39366 this.collapseBtn.setVisible(c.collapsible == true);
39369 this.cmargins = c.cmargins || this.cmargins ||
39370 (this.position == "west" || this.position == "east" ?
39371 {top: 0, left: 2, right:2, bottom: 0} :
39372 {top: 2, left: 0, right:0, bottom: 2});
39374 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39377 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39379 this.autoScroll = c.autoScroll || false;
39384 this.duration = c.duration || .30;
39385 this.slideDuration = c.slideDuration || .45;
39390 * Returns true if this region is currently visible.
39391 * @return {Boolean}
39393 isVisible : function(){
39394 return this.visible;
39398 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39399 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39401 //setCollapsedTitle : function(title){
39402 // title = title || " ";
39403 // if(this.collapsedTitleTextEl){
39404 // this.collapsedTitleTextEl.innerHTML = title;
39408 getBox : function(){
39410 // if(!this.collapsed){
39411 b = this.el.getBox(false, true);
39413 // b = this.collapsedEl.getBox(false, true);
39418 getMargins : function(){
39419 return this.margins;
39420 //return this.collapsed ? this.cmargins : this.margins;
39423 highlight : function(){
39424 this.el.addClass("x-layout-panel-dragover");
39427 unhighlight : function(){
39428 this.el.removeClass("x-layout-panel-dragover");
39431 updateBox : function(box)
39433 if (!this.bodyEl) {
39434 return; // not rendered yet..
39438 if(!this.collapsed){
39439 this.el.dom.style.left = box.x + "px";
39440 this.el.dom.style.top = box.y + "px";
39441 this.updateBody(box.width, box.height);
39443 this.collapsedEl.dom.style.left = box.x + "px";
39444 this.collapsedEl.dom.style.top = box.y + "px";
39445 this.collapsedEl.setSize(box.width, box.height);
39448 this.tabs.autoSizeTabs();
39452 updateBody : function(w, h)
39455 this.el.setWidth(w);
39456 w -= this.el.getBorderWidth("rl");
39457 if(this.config.adjustments){
39458 w += this.config.adjustments[0];
39461 if(h !== null && h > 0){
39462 this.el.setHeight(h);
39463 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39464 h -= this.el.getBorderWidth("tb");
39465 if(this.config.adjustments){
39466 h += this.config.adjustments[1];
39468 this.bodyEl.setHeight(h);
39470 h = this.tabs.syncHeight(h);
39473 if(this.panelSize){
39474 w = w !== null ? w : this.panelSize.width;
39475 h = h !== null ? h : this.panelSize.height;
39477 if(this.activePanel){
39478 var el = this.activePanel.getEl();
39479 w = w !== null ? w : el.getWidth();
39480 h = h !== null ? h : el.getHeight();
39481 this.panelSize = {width: w, height: h};
39482 this.activePanel.setSize(w, h);
39484 if(Roo.isIE && this.tabs){
39485 this.tabs.el.repaint();
39490 * Returns the container element for this region.
39491 * @return {Roo.Element}
39493 getEl : function(){
39498 * Hides this region.
39501 //if(!this.collapsed){
39502 this.el.dom.style.left = "-2000px";
39505 // this.collapsedEl.dom.style.left = "-2000px";
39506 // this.collapsedEl.hide();
39508 this.visible = false;
39509 this.fireEvent("visibilitychange", this, false);
39513 * Shows this region if it was previously hidden.
39516 //if(!this.collapsed){
39519 // this.collapsedEl.show();
39521 this.visible = true;
39522 this.fireEvent("visibilitychange", this, true);
39525 closeClicked : function(){
39526 if(this.activePanel){
39527 this.remove(this.activePanel);
39531 collapseClick : function(e){
39533 e.stopPropagation();
39536 e.stopPropagation();
39542 * Collapses this region.
39543 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39546 collapse : function(skipAnim, skipCheck = false){
39547 if(this.collapsed) {
39551 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39553 this.collapsed = true;
39555 this.split.el.hide();
39557 if(this.config.animate && skipAnim !== true){
39558 this.fireEvent("invalidated", this);
39559 this.animateCollapse();
39561 this.el.setLocation(-20000,-20000);
39563 this.collapsedEl.show();
39564 this.fireEvent("collapsed", this);
39565 this.fireEvent("invalidated", this);
39571 animateCollapse : function(){
39576 * Expands this region if it was previously collapsed.
39577 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39578 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39581 expand : function(e, skipAnim){
39583 e.stopPropagation();
39585 if(!this.collapsed || this.el.hasActiveFx()) {
39589 this.afterSlideIn();
39592 this.collapsed = false;
39593 if(this.config.animate && skipAnim !== true){
39594 this.animateExpand();
39598 this.split.el.show();
39600 this.collapsedEl.setLocation(-2000,-2000);
39601 this.collapsedEl.hide();
39602 this.fireEvent("invalidated", this);
39603 this.fireEvent("expanded", this);
39607 animateExpand : function(){
39611 initTabs : function()
39613 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39615 var ts = new Roo.bootstrap.panel.Tabs({
39616 el: this.bodyEl.dom,
39618 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39619 disableTooltips: this.config.disableTabTips,
39620 toolbar : this.config.toolbar
39623 if(this.config.hideTabs){
39624 ts.stripWrap.setDisplayed(false);
39627 ts.resizeTabs = this.config.resizeTabs === true;
39628 ts.minTabWidth = this.config.minTabWidth || 40;
39629 ts.maxTabWidth = this.config.maxTabWidth || 250;
39630 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39631 ts.monitorResize = false;
39632 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39633 ts.bodyEl.addClass('roo-layout-tabs-body');
39634 this.panels.each(this.initPanelAsTab, this);
39637 initPanelAsTab : function(panel){
39638 var ti = this.tabs.addTab(
39642 this.config.closeOnTab && panel.isClosable(),
39645 if(panel.tabTip !== undefined){
39646 ti.setTooltip(panel.tabTip);
39648 ti.on("activate", function(){
39649 this.setActivePanel(panel);
39652 if(this.config.closeOnTab){
39653 ti.on("beforeclose", function(t, e){
39655 this.remove(panel);
39659 panel.tabItem = ti;
39664 updatePanelTitle : function(panel, title)
39666 if(this.activePanel == panel){
39667 this.updateTitle(title);
39670 var ti = this.tabs.getTab(panel.getEl().id);
39672 if(panel.tabTip !== undefined){
39673 ti.setTooltip(panel.tabTip);
39678 updateTitle : function(title){
39679 if(this.titleTextEl && !this.config.title){
39680 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39684 setActivePanel : function(panel)
39686 panel = this.getPanel(panel);
39687 if(this.activePanel && this.activePanel != panel){
39688 if(this.activePanel.setActiveState(false) === false){
39692 this.activePanel = panel;
39693 panel.setActiveState(true);
39694 if(this.panelSize){
39695 panel.setSize(this.panelSize.width, this.panelSize.height);
39698 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39700 this.updateTitle(panel.getTitle());
39702 this.fireEvent("invalidated", this);
39704 this.fireEvent("panelactivated", this, panel);
39708 * Shows the specified panel.
39709 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39710 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39712 showPanel : function(panel)
39714 panel = this.getPanel(panel);
39717 var tab = this.tabs.getTab(panel.getEl().id);
39718 if(tab.isHidden()){
39719 this.tabs.unhideTab(tab.id);
39723 this.setActivePanel(panel);
39730 * Get the active panel for this region.
39731 * @return {Roo.ContentPanel} The active panel or null
39733 getActivePanel : function(){
39734 return this.activePanel;
39737 validateVisibility : function(){
39738 if(this.panels.getCount() < 1){
39739 this.updateTitle(" ");
39740 this.closeBtn.hide();
39743 if(!this.isVisible()){
39750 * Adds the passed ContentPanel(s) to this region.
39751 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39752 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39754 add : function(panel)
39756 if(arguments.length > 1){
39757 for(var i = 0, len = arguments.length; i < len; i++) {
39758 this.add(arguments[i]);
39763 // if we have not been rendered yet, then we can not really do much of this..
39764 if (!this.bodyEl) {
39765 this.unrendered_panels.push(panel);
39772 if(this.hasPanel(panel)){
39773 this.showPanel(panel);
39776 panel.setRegion(this);
39777 this.panels.add(panel);
39778 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39779 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39780 // and hide them... ???
39781 this.bodyEl.dom.appendChild(panel.getEl().dom);
39782 if(panel.background !== true){
39783 this.setActivePanel(panel);
39785 this.fireEvent("paneladded", this, panel);
39792 this.initPanelAsTab(panel);
39796 if(panel.background !== true){
39797 this.tabs.activate(panel.getEl().id);
39799 this.fireEvent("paneladded", this, panel);
39804 * Hides the tab for the specified panel.
39805 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39807 hidePanel : function(panel){
39808 if(this.tabs && (panel = this.getPanel(panel))){
39809 this.tabs.hideTab(panel.getEl().id);
39814 * Unhides the tab for a previously hidden panel.
39815 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39817 unhidePanel : function(panel){
39818 if(this.tabs && (panel = this.getPanel(panel))){
39819 this.tabs.unhideTab(panel.getEl().id);
39823 clearPanels : function(){
39824 while(this.panels.getCount() > 0){
39825 this.remove(this.panels.first());
39830 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39831 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39832 * @param {Boolean} preservePanel Overrides the config preservePanel option
39833 * @return {Roo.ContentPanel} The panel that was removed
39835 remove : function(panel, preservePanel)
39837 panel = this.getPanel(panel);
39842 this.fireEvent("beforeremove", this, panel, e);
39843 if(e.cancel === true){
39846 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39847 var panelId = panel.getId();
39848 this.panels.removeKey(panelId);
39850 document.body.appendChild(panel.getEl().dom);
39853 this.tabs.removeTab(panel.getEl().id);
39854 }else if (!preservePanel){
39855 this.bodyEl.dom.removeChild(panel.getEl().dom);
39857 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39858 var p = this.panels.first();
39859 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39860 tempEl.appendChild(p.getEl().dom);
39861 this.bodyEl.update("");
39862 this.bodyEl.dom.appendChild(p.getEl().dom);
39864 this.updateTitle(p.getTitle());
39866 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39867 this.setActivePanel(p);
39869 panel.setRegion(null);
39870 if(this.activePanel == panel){
39871 this.activePanel = null;
39873 if(this.config.autoDestroy !== false && preservePanel !== true){
39874 try{panel.destroy();}catch(e){}
39876 this.fireEvent("panelremoved", this, panel);
39881 * Returns the TabPanel component used by this region
39882 * @return {Roo.TabPanel}
39884 getTabs : function(){
39888 createTool : function(parentEl, className){
39889 var btn = Roo.DomHelper.append(parentEl, {
39891 cls: "x-layout-tools-button",
39894 cls: "roo-layout-tools-button-inner " + className,
39898 btn.addClassOnOver("roo-layout-tools-button-over");
39903 * Ext JS Library 1.1.1
39904 * Copyright(c) 2006-2007, Ext JS, LLC.
39906 * Originally Released Under LGPL - original licence link has changed is not relivant.
39909 * <script type="text/javascript">
39915 * @class Roo.SplitLayoutRegion
39916 * @extends Roo.LayoutRegion
39917 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39919 Roo.bootstrap.layout.Split = function(config){
39920 this.cursor = config.cursor;
39921 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39924 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39926 splitTip : "Drag to resize.",
39927 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39928 useSplitTips : false,
39930 applyConfig : function(config){
39931 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39934 onRender : function(ctr,pos) {
39936 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39937 if(!this.config.split){
39942 var splitEl = Roo.DomHelper.append(ctr.dom, {
39944 id: this.el.id + "-split",
39945 cls: "roo-layout-split roo-layout-split-"+this.position,
39948 /** The SplitBar for this region
39949 * @type Roo.SplitBar */
39950 // does not exist yet...
39951 Roo.log([this.position, this.orientation]);
39953 this.split = new Roo.bootstrap.SplitBar({
39954 dragElement : splitEl,
39955 resizingElement: this.el,
39956 orientation : this.orientation
39959 this.split.on("moved", this.onSplitMove, this);
39960 this.split.useShim = this.config.useShim === true;
39961 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39962 if(this.useSplitTips){
39963 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39965 //if(config.collapsible){
39966 // this.split.el.on("dblclick", this.collapse, this);
39969 if(typeof this.config.minSize != "undefined"){
39970 this.split.minSize = this.config.minSize;
39972 if(typeof this.config.maxSize != "undefined"){
39973 this.split.maxSize = this.config.maxSize;
39975 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39976 this.hideSplitter();
39981 getHMaxSize : function(){
39982 var cmax = this.config.maxSize || 10000;
39983 var center = this.mgr.getRegion("center");
39984 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39987 getVMaxSize : function(){
39988 var cmax = this.config.maxSize || 10000;
39989 var center = this.mgr.getRegion("center");
39990 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39993 onSplitMove : function(split, newSize){
39994 this.fireEvent("resized", this, newSize);
39998 * Returns the {@link Roo.SplitBar} for this region.
39999 * @return {Roo.SplitBar}
40001 getSplitBar : function(){
40006 this.hideSplitter();
40007 Roo.bootstrap.layout.Split.superclass.hide.call(this);
40010 hideSplitter : function(){
40012 this.split.el.setLocation(-2000,-2000);
40013 this.split.el.hide();
40019 this.split.el.show();
40021 Roo.bootstrap.layout.Split.superclass.show.call(this);
40024 beforeSlide: function(){
40025 if(Roo.isGecko){// firefox overflow auto bug workaround
40026 this.bodyEl.clip();
40028 this.tabs.bodyEl.clip();
40030 if(this.activePanel){
40031 this.activePanel.getEl().clip();
40033 if(this.activePanel.beforeSlide){
40034 this.activePanel.beforeSlide();
40040 afterSlide : function(){
40041 if(Roo.isGecko){// firefox overflow auto bug workaround
40042 this.bodyEl.unclip();
40044 this.tabs.bodyEl.unclip();
40046 if(this.activePanel){
40047 this.activePanel.getEl().unclip();
40048 if(this.activePanel.afterSlide){
40049 this.activePanel.afterSlide();
40055 initAutoHide : function(){
40056 if(this.autoHide !== false){
40057 if(!this.autoHideHd){
40058 var st = new Roo.util.DelayedTask(this.slideIn, this);
40059 this.autoHideHd = {
40060 "mouseout": function(e){
40061 if(!e.within(this.el, true)){
40065 "mouseover" : function(e){
40071 this.el.on(this.autoHideHd);
40075 clearAutoHide : function(){
40076 if(this.autoHide !== false){
40077 this.el.un("mouseout", this.autoHideHd.mouseout);
40078 this.el.un("mouseover", this.autoHideHd.mouseover);
40082 clearMonitor : function(){
40083 Roo.get(document).un("click", this.slideInIf, this);
40086 // these names are backwards but not changed for compat
40087 slideOut : function(){
40088 if(this.isSlid || this.el.hasActiveFx()){
40091 this.isSlid = true;
40092 if(this.collapseBtn){
40093 this.collapseBtn.hide();
40095 this.closeBtnState = this.closeBtn.getStyle('display');
40096 this.closeBtn.hide();
40098 this.stickBtn.show();
40101 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40102 this.beforeSlide();
40103 this.el.setStyle("z-index", 10001);
40104 this.el.slideIn(this.getSlideAnchor(), {
40105 callback: function(){
40107 this.initAutoHide();
40108 Roo.get(document).on("click", this.slideInIf, this);
40109 this.fireEvent("slideshow", this);
40116 afterSlideIn : function(){
40117 this.clearAutoHide();
40118 this.isSlid = false;
40119 this.clearMonitor();
40120 this.el.setStyle("z-index", "");
40121 if(this.collapseBtn){
40122 this.collapseBtn.show();
40124 this.closeBtn.setStyle('display', this.closeBtnState);
40126 this.stickBtn.hide();
40128 this.fireEvent("slidehide", this);
40131 slideIn : function(cb){
40132 if(!this.isSlid || this.el.hasActiveFx()){
40136 this.isSlid = false;
40137 this.beforeSlide();
40138 this.el.slideOut(this.getSlideAnchor(), {
40139 callback: function(){
40140 this.el.setLeftTop(-10000, -10000);
40142 this.afterSlideIn();
40150 slideInIf : function(e){
40151 if(!e.within(this.el)){
40156 animateCollapse : function(){
40157 this.beforeSlide();
40158 this.el.setStyle("z-index", 20000);
40159 var anchor = this.getSlideAnchor();
40160 this.el.slideOut(anchor, {
40161 callback : function(){
40162 this.el.setStyle("z-index", "");
40163 this.collapsedEl.slideIn(anchor, {duration:.3});
40165 this.el.setLocation(-10000,-10000);
40167 this.fireEvent("collapsed", this);
40174 animateExpand : function(){
40175 this.beforeSlide();
40176 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40177 this.el.setStyle("z-index", 20000);
40178 this.collapsedEl.hide({
40181 this.el.slideIn(this.getSlideAnchor(), {
40182 callback : function(){
40183 this.el.setStyle("z-index", "");
40186 this.split.el.show();
40188 this.fireEvent("invalidated", this);
40189 this.fireEvent("expanded", this);
40217 getAnchor : function(){
40218 return this.anchors[this.position];
40221 getCollapseAnchor : function(){
40222 return this.canchors[this.position];
40225 getSlideAnchor : function(){
40226 return this.sanchors[this.position];
40229 getAlignAdj : function(){
40230 var cm = this.cmargins;
40231 switch(this.position){
40247 getExpandAdj : function(){
40248 var c = this.collapsedEl, cm = this.cmargins;
40249 switch(this.position){
40251 return [-(cm.right+c.getWidth()+cm.left), 0];
40254 return [cm.right+c.getWidth()+cm.left, 0];
40257 return [0, -(cm.top+cm.bottom+c.getHeight())];
40260 return [0, cm.top+cm.bottom+c.getHeight()];
40266 * Ext JS Library 1.1.1
40267 * Copyright(c) 2006-2007, Ext JS, LLC.
40269 * Originally Released Under LGPL - original licence link has changed is not relivant.
40272 * <script type="text/javascript">
40275 * These classes are private internal classes
40277 Roo.bootstrap.layout.Center = function(config){
40278 config.region = "center";
40279 Roo.bootstrap.layout.Region.call(this, config);
40280 this.visible = true;
40281 this.minWidth = config.minWidth || 20;
40282 this.minHeight = config.minHeight || 20;
40285 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40287 // center panel can't be hidden
40291 // center panel can't be hidden
40294 getMinWidth: function(){
40295 return this.minWidth;
40298 getMinHeight: function(){
40299 return this.minHeight;
40313 Roo.bootstrap.layout.North = function(config)
40315 config.region = 'north';
40316 config.cursor = 'n-resize';
40318 Roo.bootstrap.layout.Split.call(this, config);
40322 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40323 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40324 this.split.el.addClass("roo-layout-split-v");
40326 //var size = config.initialSize || config.height;
40327 //if(this.el && typeof size != "undefined"){
40328 // this.el.setHeight(size);
40331 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40333 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40336 onRender : function(ctr, pos)
40338 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40339 var size = this.config.initialSize || this.config.height;
40340 if(this.el && typeof size != "undefined"){
40341 this.el.setHeight(size);
40346 getBox : function(){
40347 if(this.collapsed){
40348 return this.collapsedEl.getBox();
40350 var box = this.el.getBox();
40352 box.height += this.split.el.getHeight();
40357 updateBox : function(box){
40358 if(this.split && !this.collapsed){
40359 box.height -= this.split.el.getHeight();
40360 this.split.el.setLeft(box.x);
40361 this.split.el.setTop(box.y+box.height);
40362 this.split.el.setWidth(box.width);
40364 if(this.collapsed){
40365 this.updateBody(box.width, null);
40367 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40375 Roo.bootstrap.layout.South = function(config){
40376 config.region = 'south';
40377 config.cursor = 's-resize';
40378 Roo.bootstrap.layout.Split.call(this, config);
40380 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40381 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40382 this.split.el.addClass("roo-layout-split-v");
40387 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40388 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40390 onRender : function(ctr, pos)
40392 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40393 var size = this.config.initialSize || this.config.height;
40394 if(this.el && typeof size != "undefined"){
40395 this.el.setHeight(size);
40400 getBox : function(){
40401 if(this.collapsed){
40402 return this.collapsedEl.getBox();
40404 var box = this.el.getBox();
40406 var sh = this.split.el.getHeight();
40413 updateBox : function(box){
40414 if(this.split && !this.collapsed){
40415 var sh = this.split.el.getHeight();
40418 this.split.el.setLeft(box.x);
40419 this.split.el.setTop(box.y-sh);
40420 this.split.el.setWidth(box.width);
40422 if(this.collapsed){
40423 this.updateBody(box.width, null);
40425 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40429 Roo.bootstrap.layout.East = function(config){
40430 config.region = "east";
40431 config.cursor = "e-resize";
40432 Roo.bootstrap.layout.Split.call(this, config);
40434 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40435 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40436 this.split.el.addClass("roo-layout-split-h");
40440 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40441 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40443 onRender : function(ctr, pos)
40445 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40446 var size = this.config.initialSize || this.config.width;
40447 if(this.el && typeof size != "undefined"){
40448 this.el.setWidth(size);
40453 getBox : function(){
40454 if(this.collapsed){
40455 return this.collapsedEl.getBox();
40457 var box = this.el.getBox();
40459 var sw = this.split.el.getWidth();
40466 updateBox : function(box){
40467 if(this.split && !this.collapsed){
40468 var sw = this.split.el.getWidth();
40470 this.split.el.setLeft(box.x);
40471 this.split.el.setTop(box.y);
40472 this.split.el.setHeight(box.height);
40475 if(this.collapsed){
40476 this.updateBody(null, box.height);
40478 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40482 Roo.bootstrap.layout.West = function(config){
40483 config.region = "west";
40484 config.cursor = "w-resize";
40486 Roo.bootstrap.layout.Split.call(this, config);
40488 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40489 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40490 this.split.el.addClass("roo-layout-split-h");
40494 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40495 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40497 onRender: function(ctr, pos)
40499 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40500 var size = this.config.initialSize || this.config.width;
40501 if(typeof size != "undefined"){
40502 this.el.setWidth(size);
40506 getBox : function(){
40507 if(this.collapsed){
40508 return this.collapsedEl.getBox();
40510 var box = this.el.getBox();
40511 if (box.width == 0) {
40512 box.width = this.config.width; // kludge?
40515 box.width += this.split.el.getWidth();
40520 updateBox : function(box){
40521 if(this.split && !this.collapsed){
40522 var sw = this.split.el.getWidth();
40524 this.split.el.setLeft(box.x+box.width);
40525 this.split.el.setTop(box.y);
40526 this.split.el.setHeight(box.height);
40528 if(this.collapsed){
40529 this.updateBody(null, box.height);
40531 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40533 });Roo.namespace("Roo.bootstrap.panel");/*
40535 * Ext JS Library 1.1.1
40536 * Copyright(c) 2006-2007, Ext JS, LLC.
40538 * Originally Released Under LGPL - original licence link has changed is not relivant.
40541 * <script type="text/javascript">
40544 * @class Roo.ContentPanel
40545 * @extends Roo.util.Observable
40546 * A basic ContentPanel element.
40547 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40548 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40549 * @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
40550 * @cfg {Boolean} closable True if the panel can be closed/removed
40551 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40552 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40553 * @cfg {Toolbar} toolbar A toolbar for this panel
40554 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40555 * @cfg {String} title The title for this panel
40556 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40557 * @cfg {String} url Calls {@link #setUrl} with this value
40558 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40559 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40560 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40561 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40562 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40563 * @cfg {Boolean} badges render the badges
40564 * @cfg {String} cls extra classes to use
40565 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40568 * Create a new ContentPanel.
40569 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40570 * @param {String/Object} config A string to set only the title or a config object
40571 * @param {String} content (optional) Set the HTML content for this panel
40572 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40574 Roo.bootstrap.panel.Content = function( config){
40576 this.tpl = config.tpl || false;
40578 var el = config.el;
40579 var content = config.content;
40581 if(config.autoCreate){ // xtype is available if this is called from factory
40584 this.el = Roo.get(el);
40585 if(!this.el && config && config.autoCreate){
40586 if(typeof config.autoCreate == "object"){
40587 if(!config.autoCreate.id){
40588 config.autoCreate.id = config.id||el;
40590 this.el = Roo.DomHelper.append(document.body,
40591 config.autoCreate, true);
40595 cls: (config.cls || '') +
40596 (config.background ? ' bg-' + config.background : '') +
40597 " roo-layout-inactive-content",
40600 if (config.iframe) {
40604 style : 'border: 0px',
40605 src : 'about:blank'
40611 elcfg.html = config.html;
40615 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40616 if (config.iframe) {
40617 this.iframeEl = this.el.select('iframe',true).first();
40622 this.closable = false;
40623 this.loaded = false;
40624 this.active = false;
40627 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40629 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40631 this.wrapEl = this.el; //this.el.wrap();
40633 if (config.toolbar.items) {
40634 ti = config.toolbar.items ;
40635 delete config.toolbar.items ;
40639 this.toolbar.render(this.wrapEl, 'before');
40640 for(var i =0;i < ti.length;i++) {
40641 // Roo.log(['add child', items[i]]);
40642 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40644 this.toolbar.items = nitems;
40645 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40646 delete config.toolbar;
40650 // xtype created footer. - not sure if will work as we normally have to render first..
40651 if (this.footer && !this.footer.el && this.footer.xtype) {
40652 if (!this.wrapEl) {
40653 this.wrapEl = this.el.wrap();
40656 this.footer.container = this.wrapEl.createChild();
40658 this.footer = Roo.factory(this.footer, Roo);
40663 if(typeof config == "string"){
40664 this.title = config;
40666 Roo.apply(this, config);
40670 this.resizeEl = Roo.get(this.resizeEl, true);
40672 this.resizeEl = this.el;
40674 // handle view.xtype
40682 * Fires when this panel is activated.
40683 * @param {Roo.ContentPanel} this
40687 * @event deactivate
40688 * Fires when this panel is activated.
40689 * @param {Roo.ContentPanel} this
40691 "deactivate" : true,
40695 * Fires when this panel is resized if fitToFrame is true.
40696 * @param {Roo.ContentPanel} this
40697 * @param {Number} width The width after any component adjustments
40698 * @param {Number} height The height after any component adjustments
40704 * Fires when this tab is created
40705 * @param {Roo.ContentPanel} this
40711 * Fires when this content is scrolled
40712 * @param {Roo.ContentPanel} this
40713 * @param {Event} scrollEvent
40724 if(this.autoScroll && !this.iframe){
40725 this.resizeEl.setStyle("overflow", "auto");
40726 this.resizeEl.on('scroll', this.onScroll, this);
40728 // fix randome scrolling
40729 //this.el.on('scroll', function() {
40730 // Roo.log('fix random scolling');
40731 // this.scrollTo('top',0);
40734 content = content || this.content;
40736 this.setContent(content);
40738 if(config && config.url){
40739 this.setUrl(this.url, this.params, this.loadOnce);
40744 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40746 if (this.view && typeof(this.view.xtype) != 'undefined') {
40747 this.view.el = this.el.appendChild(document.createElement("div"));
40748 this.view = Roo.factory(this.view);
40749 this.view.render && this.view.render(false, '');
40753 this.fireEvent('render', this);
40756 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40766 /* Resize Element - use this to work out scroll etc. */
40769 setRegion : function(region){
40770 this.region = region;
40771 this.setActiveClass(region && !this.background);
40775 setActiveClass: function(state)
40778 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40779 this.el.setStyle('position','relative');
40781 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40782 this.el.setStyle('position', 'absolute');
40787 * Returns the toolbar for this Panel if one was configured.
40788 * @return {Roo.Toolbar}
40790 getToolbar : function(){
40791 return this.toolbar;
40794 setActiveState : function(active)
40796 this.active = active;
40797 this.setActiveClass(active);
40799 if(this.fireEvent("deactivate", this) === false){
40804 this.fireEvent("activate", this);
40808 * Updates this panel's element (not for iframe)
40809 * @param {String} content The new content
40810 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40812 setContent : function(content, loadScripts){
40817 this.el.update(content, loadScripts);
40820 ignoreResize : function(w, h){
40821 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40824 this.lastSize = {width: w, height: h};
40829 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40830 * @return {Roo.UpdateManager} The UpdateManager
40832 getUpdateManager : function(){
40836 return this.el.getUpdateManager();
40839 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40840 * Does not work with IFRAME contents
40841 * @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:
40844 url: "your-url.php",
40845 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40846 callback: yourFunction,
40847 scope: yourObject, //(optional scope)
40850 text: "Loading...",
40856 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40857 * 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.
40858 * @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}
40859 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40860 * @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.
40861 * @return {Roo.ContentPanel} this
40869 var um = this.el.getUpdateManager();
40870 um.update.apply(um, arguments);
40876 * 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.
40877 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40878 * @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)
40879 * @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)
40880 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40882 setUrl : function(url, params, loadOnce){
40884 this.iframeEl.dom.src = url;
40888 if(this.refreshDelegate){
40889 this.removeListener("activate", this.refreshDelegate);
40891 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40892 this.on("activate", this.refreshDelegate);
40893 return this.el.getUpdateManager();
40896 _handleRefresh : function(url, params, loadOnce){
40897 if(!loadOnce || !this.loaded){
40898 var updater = this.el.getUpdateManager();
40899 updater.update(url, params, this._setLoaded.createDelegate(this));
40903 _setLoaded : function(){
40904 this.loaded = true;
40908 * Returns this panel's id
40911 getId : function(){
40916 * Returns this panel's element - used by regiosn to add.
40917 * @return {Roo.Element}
40919 getEl : function(){
40920 return this.wrapEl || this.el;
40925 adjustForComponents : function(width, height)
40927 //Roo.log('adjustForComponents ');
40928 if(this.resizeEl != this.el){
40929 width -= this.el.getFrameWidth('lr');
40930 height -= this.el.getFrameWidth('tb');
40933 var te = this.toolbar.getEl();
40934 te.setWidth(width);
40935 height -= te.getHeight();
40938 var te = this.footer.getEl();
40939 te.setWidth(width);
40940 height -= te.getHeight();
40944 if(this.adjustments){
40945 width += this.adjustments[0];
40946 height += this.adjustments[1];
40948 return {"width": width, "height": height};
40951 setSize : function(width, height){
40952 if(this.fitToFrame && !this.ignoreResize(width, height)){
40953 if(this.fitContainer && this.resizeEl != this.el){
40954 this.el.setSize(width, height);
40956 var size = this.adjustForComponents(width, height);
40958 this.iframeEl.setSize(width,height);
40961 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40962 this.fireEvent('resize', this, size.width, size.height);
40969 * Returns this panel's title
40972 getTitle : function(){
40974 if (typeof(this.title) != 'object') {
40979 for (var k in this.title) {
40980 if (!this.title.hasOwnProperty(k)) {
40984 if (k.indexOf('-') >= 0) {
40985 var s = k.split('-');
40986 for (var i = 0; i<s.length; i++) {
40987 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40990 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40997 * Set this panel's title
40998 * @param {String} title
41000 setTitle : function(title){
41001 this.title = title;
41003 this.region.updatePanelTitle(this, title);
41008 * Returns true is this panel was configured to be closable
41009 * @return {Boolean}
41011 isClosable : function(){
41012 return this.closable;
41015 beforeSlide : function(){
41017 this.resizeEl.clip();
41020 afterSlide : function(){
41022 this.resizeEl.unclip();
41026 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41027 * Will fail silently if the {@link #setUrl} method has not been called.
41028 * This does not activate the panel, just updates its content.
41030 refresh : function(){
41031 if(this.refreshDelegate){
41032 this.loaded = false;
41033 this.refreshDelegate();
41038 * Destroys this panel
41040 destroy : function(){
41041 this.el.removeAllListeners();
41042 var tempEl = document.createElement("span");
41043 tempEl.appendChild(this.el.dom);
41044 tempEl.innerHTML = "";
41050 * form - if the content panel contains a form - this is a reference to it.
41051 * @type {Roo.form.Form}
41055 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41056 * This contains a reference to it.
41062 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41072 * @param {Object} cfg Xtype definition of item to add.
41076 getChildContainer: function () {
41077 return this.getEl();
41081 onScroll : function(e)
41083 this.fireEvent('scroll', this, e);
41088 var ret = new Roo.factory(cfg);
41093 if (cfg.xtype.match(/^Form$/)) {
41096 //if (this.footer) {
41097 // el = this.footer.container.insertSibling(false, 'before');
41099 el = this.el.createChild();
41102 this.form = new Roo.form.Form(cfg);
41105 if ( this.form.allItems.length) {
41106 this.form.render(el.dom);
41110 // should only have one of theses..
41111 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41112 // views.. should not be just added - used named prop 'view''
41114 cfg.el = this.el.appendChild(document.createElement("div"));
41117 var ret = new Roo.factory(cfg);
41119 ret.render && ret.render(false, ''); // render blank..
41129 * @class Roo.bootstrap.panel.Grid
41130 * @extends Roo.bootstrap.panel.Content
41132 * Create a new GridPanel.
41133 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41134 * @param {Object} config A the config object
41140 Roo.bootstrap.panel.Grid = function(config)
41144 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41145 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41147 config.el = this.wrapper;
41148 //this.el = this.wrapper;
41150 if (config.container) {
41151 // ctor'ed from a Border/panel.grid
41154 this.wrapper.setStyle("overflow", "hidden");
41155 this.wrapper.addClass('roo-grid-container');
41160 if(config.toolbar){
41161 var tool_el = this.wrapper.createChild();
41162 this.toolbar = Roo.factory(config.toolbar);
41164 if (config.toolbar.items) {
41165 ti = config.toolbar.items ;
41166 delete config.toolbar.items ;
41170 this.toolbar.render(tool_el);
41171 for(var i =0;i < ti.length;i++) {
41172 // Roo.log(['add child', items[i]]);
41173 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41175 this.toolbar.items = nitems;
41177 delete config.toolbar;
41180 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41181 config.grid.scrollBody = true;;
41182 config.grid.monitorWindowResize = false; // turn off autosizing
41183 config.grid.autoHeight = false;
41184 config.grid.autoWidth = false;
41186 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41188 if (config.background) {
41189 // render grid on panel activation (if panel background)
41190 this.on('activate', function(gp) {
41191 if (!gp.grid.rendered) {
41192 gp.grid.render(this.wrapper);
41193 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41198 this.grid.render(this.wrapper);
41199 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41202 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41203 // ??? needed ??? config.el = this.wrapper;
41208 // xtype created footer. - not sure if will work as we normally have to render first..
41209 if (this.footer && !this.footer.el && this.footer.xtype) {
41211 var ctr = this.grid.getView().getFooterPanel(true);
41212 this.footer.dataSource = this.grid.dataSource;
41213 this.footer = Roo.factory(this.footer, Roo);
41214 this.footer.render(ctr);
41224 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41225 getId : function(){
41226 return this.grid.id;
41230 * Returns the grid for this panel
41231 * @return {Roo.bootstrap.Table}
41233 getGrid : function(){
41237 setSize : function(width, height){
41238 if(!this.ignoreResize(width, height)){
41239 var grid = this.grid;
41240 var size = this.adjustForComponents(width, height);
41241 // tfoot is not a footer?
41244 var gridel = grid.getGridEl();
41245 gridel.setSize(size.width, size.height);
41247 var tbd = grid.getGridEl().select('tbody', true).first();
41248 var thd = grid.getGridEl().select('thead',true).first();
41249 var tbf= grid.getGridEl().select('tfoot', true).first();
41252 size.height -= tbf.getHeight();
41255 size.height -= thd.getHeight();
41258 tbd.setSize(size.width, size.height );
41259 // this is for the account management tab -seems to work there.
41260 var thd = grid.getGridEl().select('thead',true).first();
41262 // tbd.setSize(size.width, size.height - thd.getHeight());
41271 beforeSlide : function(){
41272 this.grid.getView().scroller.clip();
41275 afterSlide : function(){
41276 this.grid.getView().scroller.unclip();
41279 destroy : function(){
41280 this.grid.destroy();
41282 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41287 * @class Roo.bootstrap.panel.Nest
41288 * @extends Roo.bootstrap.panel.Content
41290 * Create a new Panel, that can contain a layout.Border.
41293 * @param {Roo.BorderLayout} layout The layout for this panel
41294 * @param {String/Object} config A string to set only the title or a config object
41296 Roo.bootstrap.panel.Nest = function(config)
41298 // construct with only one argument..
41299 /* FIXME - implement nicer consturctors
41300 if (layout.layout) {
41302 layout = config.layout;
41303 delete config.layout;
41305 if (layout.xtype && !layout.getEl) {
41306 // then layout needs constructing..
41307 layout = Roo.factory(layout, Roo);
41311 config.el = config.layout.getEl();
41313 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41315 config.layout.monitorWindowResize = false; // turn off autosizing
41316 this.layout = config.layout;
41317 this.layout.getEl().addClass("roo-layout-nested-layout");
41318 this.layout.parent = this;
41325 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41327 setSize : function(width, height){
41328 if(!this.ignoreResize(width, height)){
41329 var size = this.adjustForComponents(width, height);
41330 var el = this.layout.getEl();
41331 if (size.height < 1) {
41332 el.setWidth(size.width);
41334 el.setSize(size.width, size.height);
41336 var touch = el.dom.offsetWidth;
41337 this.layout.layout();
41338 // ie requires a double layout on the first pass
41339 if(Roo.isIE && !this.initialized){
41340 this.initialized = true;
41341 this.layout.layout();
41346 // activate all subpanels if not currently active..
41348 setActiveState : function(active){
41349 this.active = active;
41350 this.setActiveClass(active);
41353 this.fireEvent("deactivate", this);
41357 this.fireEvent("activate", this);
41358 // not sure if this should happen before or after..
41359 if (!this.layout) {
41360 return; // should not happen..
41363 for (var r in this.layout.regions) {
41364 reg = this.layout.getRegion(r);
41365 if (reg.getActivePanel()) {
41366 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41367 reg.setActivePanel(reg.getActivePanel());
41370 if (!reg.panels.length) {
41373 reg.showPanel(reg.getPanel(0));
41382 * Returns the nested BorderLayout for this panel
41383 * @return {Roo.BorderLayout}
41385 getLayout : function(){
41386 return this.layout;
41390 * Adds a xtype elements to the layout of the nested panel
41394 xtype : 'ContentPanel',
41401 xtype : 'NestedLayoutPanel',
41407 items : [ ... list of content panels or nested layout panels.. ]
41411 * @param {Object} cfg Xtype definition of item to add.
41413 addxtype : function(cfg) {
41414 return this.layout.addxtype(cfg);
41419 * Ext JS Library 1.1.1
41420 * Copyright(c) 2006-2007, Ext JS, LLC.
41422 * Originally Released Under LGPL - original licence link has changed is not relivant.
41425 * <script type="text/javascript">
41428 * @class Roo.TabPanel
41429 * @extends Roo.util.Observable
41430 * A lightweight tab container.
41434 // basic tabs 1, built from existing content
41435 var tabs = new Roo.TabPanel("tabs1");
41436 tabs.addTab("script", "View Script");
41437 tabs.addTab("markup", "View Markup");
41438 tabs.activate("script");
41440 // more advanced tabs, built from javascript
41441 var jtabs = new Roo.TabPanel("jtabs");
41442 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41444 // set up the UpdateManager
41445 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41446 var updater = tab2.getUpdateManager();
41447 updater.setDefaultUrl("ajax1.htm");
41448 tab2.on('activate', updater.refresh, updater, true);
41450 // Use setUrl for Ajax loading
41451 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41452 tab3.setUrl("ajax2.htm", null, true);
41455 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41458 jtabs.activate("jtabs-1");
41461 * Create a new TabPanel.
41462 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41463 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41465 Roo.bootstrap.panel.Tabs = function(config){
41467 * The container element for this TabPanel.
41468 * @type Roo.Element
41470 this.el = Roo.get(config.el);
41473 if(typeof config == "boolean"){
41474 this.tabPosition = config ? "bottom" : "top";
41476 Roo.apply(this, config);
41480 if(this.tabPosition == "bottom"){
41481 // if tabs are at the bottom = create the body first.
41482 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41483 this.el.addClass("roo-tabs-bottom");
41485 // next create the tabs holders
41487 if (this.tabPosition == "west"){
41489 var reg = this.region; // fake it..
41491 if (!reg.mgr.parent) {
41494 reg = reg.mgr.parent.region;
41496 Roo.log("got nest?");
41498 if (reg.mgr.getRegion('west')) {
41499 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41500 this.stripWrap = Roo.get(this.createStrip(ctrdom ), 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);
41511 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41512 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41513 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41514 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41519 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41522 // finally - if tabs are at the top, then create the body last..
41523 if(this.tabPosition != "bottom"){
41524 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41525 * @type Roo.Element
41527 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41528 this.el.addClass("roo-tabs-top");
41532 this.bodyEl.setStyle("position", "relative");
41534 this.active = null;
41535 this.activateDelegate = this.activate.createDelegate(this);
41540 * Fires when the active tab changes
41541 * @param {Roo.TabPanel} this
41542 * @param {Roo.TabPanelItem} activePanel The new active tab
41546 * @event beforetabchange
41547 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41548 * @param {Roo.TabPanel} this
41549 * @param {Object} e Set cancel to true on this object to cancel the tab change
41550 * @param {Roo.TabPanelItem} tab The tab being changed to
41552 "beforetabchange" : true
41555 Roo.EventManager.onWindowResize(this.onResize, this);
41556 this.cpad = this.el.getPadding("lr");
41557 this.hiddenCount = 0;
41560 // toolbar on the tabbar support...
41561 if (this.toolbar) {
41562 alert("no toolbar support yet");
41563 this.toolbar = false;
41565 var tcfg = this.toolbar;
41566 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41567 this.toolbar = new Roo.Toolbar(tcfg);
41568 if (Roo.isSafari) {
41569 var tbl = tcfg.container.child('table', true);
41570 tbl.setAttribute('width', '100%');
41578 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41581 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41583 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41585 tabPosition : "top",
41587 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41589 currentTabWidth : 0,
41591 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41595 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41599 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41601 preferredTabWidth : 175,
41603 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41605 resizeTabs : false,
41607 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41609 monitorResize : true,
41611 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41613 toolbar : false, // set by caller..
41615 region : false, /// set by caller
41617 disableTooltips : true, // not used yet...
41620 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41621 * @param {String} id The id of the div to use <b>or create</b>
41622 * @param {String} text The text for the tab
41623 * @param {String} content (optional) Content to put in the TabPanelItem body
41624 * @param {Boolean} closable (optional) True to create a close icon on the tab
41625 * @return {Roo.TabPanelItem} The created TabPanelItem
41627 addTab : function(id, text, content, closable, tpl)
41629 var item = new Roo.bootstrap.panel.TabItem({
41633 closable : closable,
41636 this.addTabItem(item);
41638 item.setContent(content);
41644 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41645 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41646 * @return {Roo.TabPanelItem}
41648 getTab : function(id){
41649 return this.items[id];
41653 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41654 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41656 hideTab : function(id){
41657 var t = this.items[id];
41660 this.hiddenCount++;
41661 this.autoSizeTabs();
41666 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41667 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41669 unhideTab : function(id){
41670 var t = this.items[id];
41672 t.setHidden(false);
41673 this.hiddenCount--;
41674 this.autoSizeTabs();
41679 * Adds an existing {@link Roo.TabPanelItem}.
41680 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41682 addTabItem : function(item)
41684 this.items[item.id] = item;
41685 this.items.push(item);
41686 this.autoSizeTabs();
41687 // if(this.resizeTabs){
41688 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41689 // this.autoSizeTabs();
41691 // item.autoSize();
41696 * Removes a {@link Roo.TabPanelItem}.
41697 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41699 removeTab : function(id){
41700 var items = this.items;
41701 var tab = items[id];
41702 if(!tab) { return; }
41703 var index = items.indexOf(tab);
41704 if(this.active == tab && items.length > 1){
41705 var newTab = this.getNextAvailable(index);
41710 this.stripEl.dom.removeChild(tab.pnode.dom);
41711 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41712 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41714 items.splice(index, 1);
41715 delete this.items[tab.id];
41716 tab.fireEvent("close", tab);
41717 tab.purgeListeners();
41718 this.autoSizeTabs();
41721 getNextAvailable : function(start){
41722 var items = this.items;
41724 // look for a next tab that will slide over to
41725 // replace the one being removed
41726 while(index < items.length){
41727 var item = items[++index];
41728 if(item && !item.isHidden()){
41732 // if one isn't found select the previous tab (on the left)
41735 var item = items[--index];
41736 if(item && !item.isHidden()){
41744 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41745 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41747 disableTab : function(id){
41748 var tab = this.items[id];
41749 if(tab && this.active != tab){
41755 * Enables a {@link Roo.TabPanelItem} that is disabled.
41756 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41758 enableTab : function(id){
41759 var tab = this.items[id];
41764 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41765 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41766 * @return {Roo.TabPanelItem} The TabPanelItem.
41768 activate : function(id)
41770 //Roo.log('activite:' + id);
41772 var tab = this.items[id];
41776 if(tab == this.active || tab.disabled){
41780 this.fireEvent("beforetabchange", this, e, tab);
41781 if(e.cancel !== true && !tab.disabled){
41783 this.active.hide();
41785 this.active = this.items[id];
41786 this.active.show();
41787 this.fireEvent("tabchange", this, this.active);
41793 * Gets the active {@link Roo.TabPanelItem}.
41794 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41796 getActiveTab : function(){
41797 return this.active;
41801 * Updates the tab body element to fit the height of the container element
41802 * for overflow scrolling
41803 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41805 syncHeight : function(targetHeight){
41806 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41807 var bm = this.bodyEl.getMargins();
41808 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41809 this.bodyEl.setHeight(newHeight);
41813 onResize : function(){
41814 if(this.monitorResize){
41815 this.autoSizeTabs();
41820 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41822 beginUpdate : function(){
41823 this.updating = true;
41827 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41829 endUpdate : function(){
41830 this.updating = false;
41831 this.autoSizeTabs();
41835 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41837 autoSizeTabs : function()
41839 var count = this.items.length;
41840 var vcount = count - this.hiddenCount;
41843 this.stripEl.hide();
41845 this.stripEl.show();
41848 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41853 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41854 var availWidth = Math.floor(w / vcount);
41855 var b = this.stripBody;
41856 if(b.getWidth() > w){
41857 var tabs = this.items;
41858 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41859 if(availWidth < this.minTabWidth){
41860 /*if(!this.sleft){ // incomplete scrolling code
41861 this.createScrollButtons();
41864 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41867 if(this.currentTabWidth < this.preferredTabWidth){
41868 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41874 * Returns the number of tabs in this TabPanel.
41877 getCount : function(){
41878 return this.items.length;
41882 * Resizes all the tabs to the passed width
41883 * @param {Number} The new width
41885 setTabWidth : function(width){
41886 this.currentTabWidth = width;
41887 for(var i = 0, len = this.items.length; i < len; i++) {
41888 if(!this.items[i].isHidden()) {
41889 this.items[i].setWidth(width);
41895 * Destroys this TabPanel
41896 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41898 destroy : function(removeEl){
41899 Roo.EventManager.removeResizeListener(this.onResize, this);
41900 for(var i = 0, len = this.items.length; i < len; i++){
41901 this.items[i].purgeListeners();
41903 if(removeEl === true){
41904 this.el.update("");
41909 createStrip : function(container)
41911 var strip = document.createElement("nav");
41912 strip.className = Roo.bootstrap.version == 4 ?
41913 "navbar-light bg-light" :
41914 "navbar navbar-default"; //"x-tabs-wrap";
41915 container.appendChild(strip);
41919 createStripList : function(strip)
41921 // div wrapper for retard IE
41922 // returns the "tr" element.
41923 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41924 //'<div class="x-tabs-strip-wrap">'+
41925 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41926 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41927 return strip.firstChild; //.firstChild.firstChild.firstChild;
41929 createBody : function(container)
41931 var body = document.createElement("div");
41932 Roo.id(body, "tab-body");
41933 //Roo.fly(body).addClass("x-tabs-body");
41934 Roo.fly(body).addClass("tab-content");
41935 container.appendChild(body);
41938 createItemBody :function(bodyEl, id){
41939 var body = Roo.getDom(id);
41941 body = document.createElement("div");
41944 //Roo.fly(body).addClass("x-tabs-item-body");
41945 Roo.fly(body).addClass("tab-pane");
41946 bodyEl.insertBefore(body, bodyEl.firstChild);
41950 createStripElements : function(stripEl, text, closable, tpl)
41952 var td = document.createElement("li"); // was td..
41953 td.className = 'nav-item';
41955 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41958 stripEl.appendChild(td);
41960 td.className = "x-tabs-closable";
41961 if(!this.closeTpl){
41962 this.closeTpl = new Roo.Template(
41963 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41964 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41965 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41968 var el = this.closeTpl.overwrite(td, {"text": text});
41969 var close = el.getElementsByTagName("div")[0];
41970 var inner = el.getElementsByTagName("em")[0];
41971 return {"el": el, "close": close, "inner": inner};
41974 // not sure what this is..
41975 // if(!this.tabTpl){
41976 //this.tabTpl = new Roo.Template(
41977 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41978 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41980 // this.tabTpl = new Roo.Template(
41981 // '<a href="#">' +
41982 // '<span unselectable="on"' +
41983 // (this.disableTooltips ? '' : ' title="{text}"') +
41984 // ' >{text}</span></a>'
41990 var template = tpl || this.tabTpl || false;
41993 template = new Roo.Template(
41994 Roo.bootstrap.version == 4 ?
41996 '<a class="nav-link" href="#" unselectable="on"' +
41997 (this.disableTooltips ? '' : ' title="{text}"') +
42000 '<a class="nav-link" href="#">' +
42001 '<span unselectable="on"' +
42002 (this.disableTooltips ? '' : ' title="{text}"') +
42003 ' >{text}</span></a>'
42008 switch (typeof(template)) {
42012 template = new Roo.Template(template);
42018 var el = template.overwrite(td, {"text": text});
42020 var inner = el.getElementsByTagName("span")[0];
42022 return {"el": el, "inner": inner};
42030 * @class Roo.TabPanelItem
42031 * @extends Roo.util.Observable
42032 * Represents an individual item (tab plus body) in a TabPanel.
42033 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42034 * @param {String} id The id of this TabPanelItem
42035 * @param {String} text The text for the tab of this TabPanelItem
42036 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42038 Roo.bootstrap.panel.TabItem = function(config){
42040 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42041 * @type Roo.TabPanel
42043 this.tabPanel = config.panel;
42045 * The id for this TabPanelItem
42048 this.id = config.id;
42050 this.disabled = false;
42052 this.text = config.text;
42054 this.loaded = false;
42055 this.closable = config.closable;
42058 * The body element for this TabPanelItem.
42059 * @type Roo.Element
42061 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42062 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42063 this.bodyEl.setStyle("display", "block");
42064 this.bodyEl.setStyle("zoom", "1");
42065 //this.hideAction();
42067 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42069 this.el = Roo.get(els.el);
42070 this.inner = Roo.get(els.inner, true);
42071 this.textEl = Roo.bootstrap.version == 4 ?
42072 this.el : Roo.get(this.el.dom.firstChild, true);
42074 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42075 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42078 // this.el.on("mousedown", this.onTabMouseDown, this);
42079 this.el.on("click", this.onTabClick, this);
42081 if(config.closable){
42082 var c = Roo.get(els.close, true);
42083 c.dom.title = this.closeText;
42084 c.addClassOnOver("close-over");
42085 c.on("click", this.closeClick, this);
42091 * Fires when this tab becomes the active tab.
42092 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42093 * @param {Roo.TabPanelItem} this
42097 * @event beforeclose
42098 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42099 * @param {Roo.TabPanelItem} this
42100 * @param {Object} e Set cancel to true on this object to cancel the close.
42102 "beforeclose": true,
42105 * Fires when this tab is closed.
42106 * @param {Roo.TabPanelItem} this
42110 * @event deactivate
42111 * Fires when this tab is no longer the active tab.
42112 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42113 * @param {Roo.TabPanelItem} this
42115 "deactivate" : true
42117 this.hidden = false;
42119 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42122 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42124 purgeListeners : function(){
42125 Roo.util.Observable.prototype.purgeListeners.call(this);
42126 this.el.removeAllListeners();
42129 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42132 this.status_node.addClass("active");
42135 this.tabPanel.stripWrap.repaint();
42137 this.fireEvent("activate", this.tabPanel, this);
42141 * Returns true if this tab is the active tab.
42142 * @return {Boolean}
42144 isActive : function(){
42145 return this.tabPanel.getActiveTab() == this;
42149 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42152 this.status_node.removeClass("active");
42154 this.fireEvent("deactivate", this.tabPanel, this);
42157 hideAction : function(){
42158 this.bodyEl.hide();
42159 this.bodyEl.setStyle("position", "absolute");
42160 this.bodyEl.setLeft("-20000px");
42161 this.bodyEl.setTop("-20000px");
42164 showAction : function(){
42165 this.bodyEl.setStyle("position", "relative");
42166 this.bodyEl.setTop("");
42167 this.bodyEl.setLeft("");
42168 this.bodyEl.show();
42172 * Set the tooltip for the tab.
42173 * @param {String} tooltip The tab's tooltip
42175 setTooltip : function(text){
42176 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42177 this.textEl.dom.qtip = text;
42178 this.textEl.dom.removeAttribute('title');
42180 this.textEl.dom.title = text;
42184 onTabClick : function(e){
42185 e.preventDefault();
42186 this.tabPanel.activate(this.id);
42189 onTabMouseDown : function(e){
42190 e.preventDefault();
42191 this.tabPanel.activate(this.id);
42194 getWidth : function(){
42195 return this.inner.getWidth();
42198 setWidth : function(width){
42199 var iwidth = width - this.linode.getPadding("lr");
42200 this.inner.setWidth(iwidth);
42201 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42202 this.linode.setWidth(width);
42206 * Show or hide the tab
42207 * @param {Boolean} hidden True to hide or false to show.
42209 setHidden : function(hidden){
42210 this.hidden = hidden;
42211 this.linode.setStyle("display", hidden ? "none" : "");
42215 * Returns true if this tab is "hidden"
42216 * @return {Boolean}
42218 isHidden : function(){
42219 return this.hidden;
42223 * Returns the text for this tab
42226 getText : function(){
42230 autoSize : function(){
42231 //this.el.beginMeasure();
42232 this.textEl.setWidth(1);
42234 * #2804 [new] Tabs in Roojs
42235 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42237 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42238 //this.el.endMeasure();
42242 * Sets the text for the tab (Note: this also sets the tooltip text)
42243 * @param {String} text The tab's text and tooltip
42245 setText : function(text){
42247 this.textEl.update(text);
42248 this.setTooltip(text);
42249 //if(!this.tabPanel.resizeTabs){
42250 // this.autoSize();
42254 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42256 activate : function(){
42257 this.tabPanel.activate(this.id);
42261 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42263 disable : function(){
42264 if(this.tabPanel.active != this){
42265 this.disabled = true;
42266 this.status_node.addClass("disabled");
42271 * Enables this TabPanelItem if it was previously disabled.
42273 enable : function(){
42274 this.disabled = false;
42275 this.status_node.removeClass("disabled");
42279 * Sets the content for this TabPanelItem.
42280 * @param {String} content The content
42281 * @param {Boolean} loadScripts true to look for and load scripts
42283 setContent : function(content, loadScripts){
42284 this.bodyEl.update(content, loadScripts);
42288 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42289 * @return {Roo.UpdateManager} The UpdateManager
42291 getUpdateManager : function(){
42292 return this.bodyEl.getUpdateManager();
42296 * Set a URL to be used to load the content for this TabPanelItem.
42297 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42298 * @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)
42299 * @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)
42300 * @return {Roo.UpdateManager} The UpdateManager
42302 setUrl : function(url, params, loadOnce){
42303 if(this.refreshDelegate){
42304 this.un('activate', this.refreshDelegate);
42306 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42307 this.on("activate", this.refreshDelegate);
42308 return this.bodyEl.getUpdateManager();
42312 _handleRefresh : function(url, params, loadOnce){
42313 if(!loadOnce || !this.loaded){
42314 var updater = this.bodyEl.getUpdateManager();
42315 updater.update(url, params, this._setLoaded.createDelegate(this));
42320 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42321 * Will fail silently if the setUrl method has not been called.
42322 * This does not activate the panel, just updates its content.
42324 refresh : function(){
42325 if(this.refreshDelegate){
42326 this.loaded = false;
42327 this.refreshDelegate();
42332 _setLoaded : function(){
42333 this.loaded = true;
42337 closeClick : function(e){
42340 this.fireEvent("beforeclose", this, o);
42341 if(o.cancel !== true){
42342 this.tabPanel.removeTab(this.id);
42346 * The text displayed in the tooltip for the close icon.
42349 closeText : "Close this tab"
42352 * This script refer to:
42353 * Title: International Telephone Input
42354 * Author: Jack O'Connor
42355 * Code version: v12.1.12
42356 * Availability: https://github.com/jackocnr/intl-tel-input.git
42359 Roo.bootstrap.PhoneInputData = function() {
42362 "Afghanistan (افغانستان)",
42367 "Albania (Shqipëri)",
42372 "Algeria (الجزائر)",
42397 "Antigua and Barbuda",
42407 "Armenia (Հայաստան)",
42423 "Austria (Österreich)",
42428 "Azerbaijan (Azərbaycan)",
42438 "Bahrain (البحرين)",
42443 "Bangladesh (বাংলাদেশ)",
42453 "Belarus (Беларусь)",
42458 "Belgium (België)",
42488 "Bosnia and Herzegovina (Босна и Херцеговина)",
42503 "British Indian Ocean Territory",
42508 "British Virgin Islands",
42518 "Bulgaria (България)",
42528 "Burundi (Uburundi)",
42533 "Cambodia (កម្ពុជា)",
42538 "Cameroon (Cameroun)",
42547 ["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"]
42550 "Cape Verde (Kabu Verdi)",
42555 "Caribbean Netherlands",
42566 "Central African Republic (République centrafricaine)",
42586 "Christmas Island",
42592 "Cocos (Keeling) Islands",
42603 "Comoros (جزر القمر)",
42608 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42613 "Congo (Republic) (Congo-Brazzaville)",
42633 "Croatia (Hrvatska)",
42654 "Czech Republic (Česká republika)",
42659 "Denmark (Danmark)",
42674 "Dominican Republic (República Dominicana)",
42678 ["809", "829", "849"]
42696 "Equatorial Guinea (Guinea Ecuatorial)",
42716 "Falkland Islands (Islas Malvinas)",
42721 "Faroe Islands (Føroyar)",
42742 "French Guiana (Guyane française)",
42747 "French Polynesia (Polynésie française)",
42762 "Georgia (საქართველო)",
42767 "Germany (Deutschland)",
42787 "Greenland (Kalaallit Nunaat)",
42824 "Guinea-Bissau (Guiné Bissau)",
42849 "Hungary (Magyarország)",
42854 "Iceland (Ísland)",
42874 "Iraq (العراق)",
42890 "Israel (ישראל)",
42917 "Jordan (الأردن)",
42922 "Kazakhstan (Казахстан)",
42943 "Kuwait (الكويت)",
42948 "Kyrgyzstan (Кыргызстан)",
42958 "Latvia (Latvija)",
42963 "Lebanon (لبنان)",
42978 "Libya (ليبيا)",
42988 "Lithuania (Lietuva)",
43003 "Macedonia (FYROM) (Македонија)",
43008 "Madagascar (Madagasikara)",
43038 "Marshall Islands",
43048 "Mauritania (موريتانيا)",
43053 "Mauritius (Moris)",
43074 "Moldova (Republica Moldova)",
43084 "Mongolia (Монгол)",
43089 "Montenegro (Crna Gora)",
43099 "Morocco (المغرب)",
43105 "Mozambique (Moçambique)",
43110 "Myanmar (Burma) (မြန်မာ)",
43115 "Namibia (Namibië)",
43130 "Netherlands (Nederland)",
43135 "New Caledonia (Nouvelle-Calédonie)",
43170 "North Korea (조선 민주주의 인민 공화국)",
43175 "Northern Mariana Islands",
43191 "Pakistan (پاکستان)",
43201 "Palestine (فلسطين)",
43211 "Papua New Guinea",
43253 "Réunion (La Réunion)",
43259 "Romania (România)",
43275 "Saint Barthélemy",
43286 "Saint Kitts and Nevis",
43296 "Saint Martin (Saint-Martin (partie française))",
43302 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43307 "Saint Vincent and the Grenadines",
43322 "São Tomé and Príncipe (São Tomé e Príncipe)",
43327 "Saudi Arabia (المملكة العربية السعودية)",
43332 "Senegal (Sénégal)",
43362 "Slovakia (Slovensko)",
43367 "Slovenia (Slovenija)",
43377 "Somalia (Soomaaliya)",
43387 "South Korea (대한민국)",
43392 "South Sudan (جنوب السودان)",
43402 "Sri Lanka (ශ්රී ලංකාව)",
43407 "Sudan (السودان)",
43417 "Svalbard and Jan Mayen",
43428 "Sweden (Sverige)",
43433 "Switzerland (Schweiz)",
43438 "Syria (سوريا)",
43483 "Trinidad and Tobago",
43488 "Tunisia (تونس)",
43493 "Turkey (Türkiye)",
43503 "Turks and Caicos Islands",
43513 "U.S. Virgin Islands",
43523 "Ukraine (Україна)",
43528 "United Arab Emirates (الإمارات العربية المتحدة)",
43550 "Uzbekistan (Oʻzbekiston)",
43560 "Vatican City (Città del Vaticano)",
43571 "Vietnam (Việt Nam)",
43576 "Wallis and Futuna (Wallis-et-Futuna)",
43581 "Western Sahara (الصحراء الغربية)",
43587 "Yemen (اليمن)",
43611 * This script refer to:
43612 * Title: International Telephone Input
43613 * Author: Jack O'Connor
43614 * Code version: v12.1.12
43615 * Availability: https://github.com/jackocnr/intl-tel-input.git
43619 * @class Roo.bootstrap.PhoneInput
43620 * @extends Roo.bootstrap.TriggerField
43621 * An input with International dial-code selection
43623 * @cfg {String} defaultDialCode default '+852'
43624 * @cfg {Array} preferedCountries default []
43627 * Create a new PhoneInput.
43628 * @param {Object} config Configuration options
43631 Roo.bootstrap.PhoneInput = function(config) {
43632 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43635 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43637 listWidth: undefined,
43639 selectedClass: 'active',
43641 invalidClass : "has-warning",
43643 validClass: 'has-success',
43645 allowed: '0123456789',
43650 * @cfg {String} defaultDialCode The default dial code when initializing the input
43652 defaultDialCode: '+852',
43655 * @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
43657 preferedCountries: false,
43659 getAutoCreate : function()
43661 var data = Roo.bootstrap.PhoneInputData();
43662 var align = this.labelAlign || this.parentLabelAlign();
43665 this.allCountries = [];
43666 this.dialCodeMapping = [];
43668 for (var i = 0; i < data.length; i++) {
43670 this.allCountries[i] = {
43674 priority: c[3] || 0,
43675 areaCodes: c[4] || null
43677 this.dialCodeMapping[c[2]] = {
43680 priority: c[3] || 0,
43681 areaCodes: c[4] || null
43693 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43694 maxlength: this.max_length,
43695 cls : 'form-control tel-input',
43696 autocomplete: 'new-password'
43699 var hiddenInput = {
43702 cls: 'hidden-tel-input'
43706 hiddenInput.name = this.name;
43709 if (this.disabled) {
43710 input.disabled = true;
43713 var flag_container = {
43730 cls: this.hasFeedback ? 'has-feedback' : '',
43736 cls: 'dial-code-holder',
43743 cls: 'roo-select2-container input-group',
43750 if (this.fieldLabel.length) {
43753 tooltip: 'This field is required'
43759 cls: 'control-label',
43765 html: this.fieldLabel
43768 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43774 if(this.indicatorpos == 'right') {
43775 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43782 if(align == 'left') {
43790 if(this.labelWidth > 12){
43791 label.style = "width: " + this.labelWidth + 'px';
43793 if(this.labelWidth < 13 && this.labelmd == 0){
43794 this.labelmd = this.labelWidth;
43796 if(this.labellg > 0){
43797 label.cls += ' col-lg-' + this.labellg;
43798 input.cls += ' col-lg-' + (12 - this.labellg);
43800 if(this.labelmd > 0){
43801 label.cls += ' col-md-' + this.labelmd;
43802 container.cls += ' col-md-' + (12 - this.labelmd);
43804 if(this.labelsm > 0){
43805 label.cls += ' col-sm-' + this.labelsm;
43806 container.cls += ' col-sm-' + (12 - this.labelsm);
43808 if(this.labelxs > 0){
43809 label.cls += ' col-xs-' + this.labelxs;
43810 container.cls += ' col-xs-' + (12 - this.labelxs);
43820 var settings = this;
43822 ['xs','sm','md','lg'].map(function(size){
43823 if (settings[size]) {
43824 cfg.cls += ' col-' + size + '-' + settings[size];
43828 this.store = new Roo.data.Store({
43829 proxy : new Roo.data.MemoryProxy({}),
43830 reader : new Roo.data.JsonReader({
43841 'name' : 'dialCode',
43845 'name' : 'priority',
43849 'name' : 'areaCodes',
43856 if(!this.preferedCountries) {
43857 this.preferedCountries = [
43864 var p = this.preferedCountries.reverse();
43867 for (var i = 0; i < p.length; i++) {
43868 for (var j = 0; j < this.allCountries.length; j++) {
43869 if(this.allCountries[j].iso2 == p[i]) {
43870 var t = this.allCountries[j];
43871 this.allCountries.splice(j,1);
43872 this.allCountries.unshift(t);
43878 this.store.proxy.data = {
43880 data: this.allCountries
43886 initEvents : function()
43889 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43891 this.indicator = this.indicatorEl();
43892 this.flag = this.flagEl();
43893 this.dialCodeHolder = this.dialCodeHolderEl();
43895 this.trigger = this.el.select('div.flag-box',true).first();
43896 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43901 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43902 _this.list.setWidth(lw);
43905 this.list.on('mouseover', this.onViewOver, this);
43906 this.list.on('mousemove', this.onViewMove, this);
43907 this.inputEl().on("keyup", this.onKeyUp, this);
43908 this.inputEl().on("keypress", this.onKeyPress, this);
43910 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43912 this.view = new Roo.View(this.list, this.tpl, {
43913 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43916 this.view.on('click', this.onViewClick, this);
43917 this.setValue(this.defaultDialCode);
43920 onTriggerClick : function(e)
43922 Roo.log('trigger click');
43927 if(this.isExpanded()){
43929 this.hasFocus = false;
43931 this.store.load({});
43932 this.hasFocus = true;
43937 isExpanded : function()
43939 return this.list.isVisible();
43942 collapse : function()
43944 if(!this.isExpanded()){
43948 Roo.get(document).un('mousedown', this.collapseIf, this);
43949 Roo.get(document).un('mousewheel', this.collapseIf, this);
43950 this.fireEvent('collapse', this);
43954 expand : function()
43958 if(this.isExpanded() || !this.hasFocus){
43962 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43963 this.list.setWidth(lw);
43966 this.restrictHeight();
43968 Roo.get(document).on('mousedown', this.collapseIf, this);
43969 Roo.get(document).on('mousewheel', this.collapseIf, this);
43971 this.fireEvent('expand', this);
43974 restrictHeight : function()
43976 this.list.alignTo(this.inputEl(), this.listAlign);
43977 this.list.alignTo(this.inputEl(), this.listAlign);
43980 onViewOver : function(e, t)
43982 if(this.inKeyMode){
43985 var item = this.view.findItemFromChild(t);
43988 var index = this.view.indexOf(item);
43989 this.select(index, false);
43994 onViewClick : function(view, doFocus, el, e)
43996 var index = this.view.getSelectedIndexes()[0];
43998 var r = this.store.getAt(index);
44001 this.onSelect(r, index);
44003 if(doFocus !== false && !this.blockFocus){
44004 this.inputEl().focus();
44008 onViewMove : function(e, t)
44010 this.inKeyMode = false;
44013 select : function(index, scrollIntoView)
44015 this.selectedIndex = index;
44016 this.view.select(index);
44017 if(scrollIntoView !== false){
44018 var el = this.view.getNode(index);
44020 this.list.scrollChildIntoView(el, false);
44025 createList : function()
44027 this.list = Roo.get(document.body).createChild({
44029 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44030 style: 'display:none'
44033 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44036 collapseIf : function(e)
44038 var in_combo = e.within(this.el);
44039 var in_list = e.within(this.list);
44040 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44042 if (in_combo || in_list || is_list) {
44048 onSelect : function(record, index)
44050 if(this.fireEvent('beforeselect', this, record, index) !== false){
44052 this.setFlagClass(record.data.iso2);
44053 this.setDialCode(record.data.dialCode);
44054 this.hasFocus = false;
44056 this.fireEvent('select', this, record, index);
44060 flagEl : function()
44062 var flag = this.el.select('div.flag',true).first();
44069 dialCodeHolderEl : function()
44071 var d = this.el.select('input.dial-code-holder',true).first();
44078 setDialCode : function(v)
44080 this.dialCodeHolder.dom.value = '+'+v;
44083 setFlagClass : function(n)
44085 this.flag.dom.className = 'flag '+n;
44088 getValue : function()
44090 var v = this.inputEl().getValue();
44091 if(this.dialCodeHolder) {
44092 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44097 setValue : function(v)
44099 var d = this.getDialCode(v);
44101 //invalid dial code
44102 if(v.length == 0 || !d || d.length == 0) {
44104 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44105 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44111 this.setFlagClass(this.dialCodeMapping[d].iso2);
44112 this.setDialCode(d);
44113 this.inputEl().dom.value = v.replace('+'+d,'');
44114 this.hiddenEl().dom.value = this.getValue();
44119 getDialCode : function(v)
44123 if (v.length == 0) {
44124 return this.dialCodeHolder.dom.value;
44128 if (v.charAt(0) != "+") {
44131 var numericChars = "";
44132 for (var i = 1; i < v.length; i++) {
44133 var c = v.charAt(i);
44136 if (this.dialCodeMapping[numericChars]) {
44137 dialCode = v.substr(1, i);
44139 if (numericChars.length == 4) {
44149 this.setValue(this.defaultDialCode);
44153 hiddenEl : function()
44155 return this.el.select('input.hidden-tel-input',true).first();
44158 // after setting val
44159 onKeyUp : function(e){
44160 this.setValue(this.getValue());
44163 onKeyPress : function(e){
44164 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44171 * @class Roo.bootstrap.MoneyField
44172 * @extends Roo.bootstrap.ComboBox
44173 * Bootstrap MoneyField class
44176 * Create a new MoneyField.
44177 * @param {Object} config Configuration options
44180 Roo.bootstrap.MoneyField = function(config) {
44182 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44186 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44189 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44191 allowDecimals : true,
44193 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44195 decimalSeparator : ".",
44197 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44199 decimalPrecision : 0,
44201 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44203 allowNegative : true,
44205 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44209 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44211 minValue : Number.NEGATIVE_INFINITY,
44213 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44215 maxValue : Number.MAX_VALUE,
44217 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44219 minText : "The minimum value for this field is {0}",
44221 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44223 maxText : "The maximum value for this field is {0}",
44225 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44226 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44228 nanText : "{0} is not a valid number",
44230 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44234 * @cfg {String} defaults currency of the MoneyField
44235 * value should be in lkey
44237 defaultCurrency : false,
44239 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44241 thousandsDelimiter : false,
44243 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44254 getAutoCreate : function()
44256 var align = this.labelAlign || this.parentLabelAlign();
44268 cls : 'form-control roo-money-amount-input',
44269 autocomplete: 'new-password'
44272 var hiddenInput = {
44276 cls: 'hidden-number-input'
44279 if(this.max_length) {
44280 input.maxlength = this.max_length;
44284 hiddenInput.name = this.name;
44287 if (this.disabled) {
44288 input.disabled = true;
44291 var clg = 12 - this.inputlg;
44292 var cmd = 12 - this.inputmd;
44293 var csm = 12 - this.inputsm;
44294 var cxs = 12 - this.inputxs;
44298 cls : 'row roo-money-field',
44302 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44306 cls: 'roo-select2-container input-group',
44310 cls : 'form-control roo-money-currency-input',
44311 autocomplete: 'new-password',
44313 name : this.currencyName
44317 cls : 'input-group-addon',
44331 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44335 cls: this.hasFeedback ? 'has-feedback' : '',
44346 if (this.fieldLabel.length) {
44349 tooltip: 'This field is required'
44355 cls: 'control-label',
44361 html: this.fieldLabel
44364 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44370 if(this.indicatorpos == 'right') {
44371 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44378 if(align == 'left') {
44386 if(this.labelWidth > 12){
44387 label.style = "width: " + this.labelWidth + 'px';
44389 if(this.labelWidth < 13 && this.labelmd == 0){
44390 this.labelmd = this.labelWidth;
44392 if(this.labellg > 0){
44393 label.cls += ' col-lg-' + this.labellg;
44394 input.cls += ' col-lg-' + (12 - this.labellg);
44396 if(this.labelmd > 0){
44397 label.cls += ' col-md-' + this.labelmd;
44398 container.cls += ' col-md-' + (12 - this.labelmd);
44400 if(this.labelsm > 0){
44401 label.cls += ' col-sm-' + this.labelsm;
44402 container.cls += ' col-sm-' + (12 - this.labelsm);
44404 if(this.labelxs > 0){
44405 label.cls += ' col-xs-' + this.labelxs;
44406 container.cls += ' col-xs-' + (12 - this.labelxs);
44417 var settings = this;
44419 ['xs','sm','md','lg'].map(function(size){
44420 if (settings[size]) {
44421 cfg.cls += ' col-' + size + '-' + settings[size];
44428 initEvents : function()
44430 this.indicator = this.indicatorEl();
44432 this.initCurrencyEvent();
44434 this.initNumberEvent();
44437 initCurrencyEvent : function()
44440 throw "can not find store for combo";
44443 this.store = Roo.factory(this.store, Roo.data);
44444 this.store.parent = this;
44448 this.triggerEl = this.el.select('.input-group-addon', true).first();
44450 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44455 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44456 _this.list.setWidth(lw);
44459 this.list.on('mouseover', this.onViewOver, this);
44460 this.list.on('mousemove', this.onViewMove, this);
44461 this.list.on('scroll', this.onViewScroll, this);
44464 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44467 this.view = new Roo.View(this.list, this.tpl, {
44468 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44471 this.view.on('click', this.onViewClick, this);
44473 this.store.on('beforeload', this.onBeforeLoad, this);
44474 this.store.on('load', this.onLoad, this);
44475 this.store.on('loadexception', this.onLoadException, this);
44477 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44478 "up" : function(e){
44479 this.inKeyMode = true;
44483 "down" : function(e){
44484 if(!this.isExpanded()){
44485 this.onTriggerClick();
44487 this.inKeyMode = true;
44492 "enter" : function(e){
44495 if(this.fireEvent("specialkey", this, e)){
44496 this.onViewClick(false);
44502 "esc" : function(e){
44506 "tab" : function(e){
44509 if(this.fireEvent("specialkey", this, e)){
44510 this.onViewClick(false);
44518 doRelay : function(foo, bar, hname){
44519 if(hname == 'down' || this.scope.isExpanded()){
44520 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44528 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44532 initNumberEvent : function(e)
44534 this.inputEl().on("keydown" , this.fireKey, this);
44535 this.inputEl().on("focus", this.onFocus, this);
44536 this.inputEl().on("blur", this.onBlur, this);
44538 this.inputEl().relayEvent('keyup', this);
44540 if(this.indicator){
44541 this.indicator.addClass('invisible');
44544 this.originalValue = this.getValue();
44546 if(this.validationEvent == 'keyup'){
44547 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44548 this.inputEl().on('keyup', this.filterValidation, this);
44550 else if(this.validationEvent !== false){
44551 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44554 if(this.selectOnFocus){
44555 this.on("focus", this.preFocus, this);
44558 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44559 this.inputEl().on("keypress", this.filterKeys, this);
44561 this.inputEl().relayEvent('keypress', this);
44564 var allowed = "0123456789";
44566 if(this.allowDecimals){
44567 allowed += this.decimalSeparator;
44570 if(this.allowNegative){
44574 if(this.thousandsDelimiter) {
44578 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44580 var keyPress = function(e){
44582 var k = e.getKey();
44584 var c = e.getCharCode();
44587 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44588 allowed.indexOf(String.fromCharCode(c)) === -1
44594 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44598 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44603 this.inputEl().on("keypress", keyPress, this);
44607 onTriggerClick : function(e)
44614 this.loadNext = false;
44616 if(this.isExpanded()){
44621 this.hasFocus = true;
44623 if(this.triggerAction == 'all') {
44624 this.doQuery(this.allQuery, true);
44628 this.doQuery(this.getRawValue());
44631 getCurrency : function()
44633 var v = this.currencyEl().getValue();
44638 restrictHeight : function()
44640 this.list.alignTo(this.currencyEl(), this.listAlign);
44641 this.list.alignTo(this.currencyEl(), this.listAlign);
44644 onViewClick : function(view, doFocus, el, e)
44646 var index = this.view.getSelectedIndexes()[0];
44648 var r = this.store.getAt(index);
44651 this.onSelect(r, index);
44655 onSelect : function(record, index){
44657 if(this.fireEvent('beforeselect', this, record, index) !== false){
44659 this.setFromCurrencyData(index > -1 ? record.data : false);
44663 this.fireEvent('select', this, record, index);
44667 setFromCurrencyData : function(o)
44671 this.lastCurrency = o;
44673 if (this.currencyField) {
44674 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44676 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44679 this.lastSelectionText = currency;
44681 //setting default currency
44682 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44683 this.setCurrency(this.defaultCurrency);
44687 this.setCurrency(currency);
44690 setFromData : function(o)
44694 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44696 this.setFromCurrencyData(c);
44701 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44703 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44706 this.setValue(value);
44710 setCurrency : function(v)
44712 this.currencyValue = v;
44715 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44720 setValue : function(v)
44722 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44728 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44730 this.inputEl().dom.value = (v == '') ? '' :
44731 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44733 if(!this.allowZero && v === '0') {
44734 this.hiddenEl().dom.value = '';
44735 this.inputEl().dom.value = '';
44742 getRawValue : function()
44744 var v = this.inputEl().getValue();
44749 getValue : function()
44751 return this.fixPrecision(this.parseValue(this.getRawValue()));
44754 parseValue : function(value)
44756 if(this.thousandsDelimiter) {
44758 r = new RegExp(",", "g");
44759 value = value.replace(r, "");
44762 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44763 return isNaN(value) ? '' : value;
44767 fixPrecision : function(value)
44769 if(this.thousandsDelimiter) {
44771 r = new RegExp(",", "g");
44772 value = value.replace(r, "");
44775 var nan = isNaN(value);
44777 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44778 return nan ? '' : value;
44780 return parseFloat(value).toFixed(this.decimalPrecision);
44783 decimalPrecisionFcn : function(v)
44785 return Math.floor(v);
44788 validateValue : function(value)
44790 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44794 var num = this.parseValue(value);
44797 this.markInvalid(String.format(this.nanText, value));
44801 if(num < this.minValue){
44802 this.markInvalid(String.format(this.minText, this.minValue));
44806 if(num > this.maxValue){
44807 this.markInvalid(String.format(this.maxText, this.maxValue));
44814 validate : function()
44816 if(this.disabled || this.allowBlank){
44821 var currency = this.getCurrency();
44823 if(this.validateValue(this.getRawValue()) && currency.length){
44828 this.markInvalid();
44832 getName: function()
44837 beforeBlur : function()
44843 var v = this.parseValue(this.getRawValue());
44850 onBlur : function()
44854 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44855 //this.el.removeClass(this.focusClass);
44858 this.hasFocus = false;
44860 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44864 var v = this.getValue();
44866 if(String(v) !== String(this.startValue)){
44867 this.fireEvent('change', this, v, this.startValue);
44870 this.fireEvent("blur", this);
44873 inputEl : function()
44875 return this.el.select('.roo-money-amount-input', true).first();
44878 currencyEl : function()
44880 return this.el.select('.roo-money-currency-input', true).first();
44883 hiddenEl : function()
44885 return this.el.select('input.hidden-number-input',true).first();
44889 * @class Roo.bootstrap.BezierSignature
44890 * @extends Roo.bootstrap.Component
44891 * Bootstrap BezierSignature class
44892 * This script refer to:
44893 * Title: Signature Pad
44895 * Availability: https://github.com/szimek/signature_pad
44898 * Create a new BezierSignature
44899 * @param {Object} config The config object
44902 Roo.bootstrap.BezierSignature = function(config){
44903 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44909 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44916 mouse_btn_down: true,
44919 * @cfg {int} canvas height
44921 canvas_height: '200px',
44924 * @cfg {float|function} Radius of a single dot.
44929 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44934 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44939 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44944 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44949 * @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.
44951 bg_color: 'rgba(0, 0, 0, 0)',
44954 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44956 dot_color: 'black',
44959 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44961 velocity_filter_weight: 0.7,
44964 * @cfg {function} Callback when stroke begin.
44969 * @cfg {function} Callback when stroke end.
44973 getAutoCreate : function()
44975 var cls = 'roo-signature column';
44978 cls += ' ' + this.cls;
44988 for(var i = 0; i < col_sizes.length; i++) {
44989 if(this[col_sizes[i]]) {
44990 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45000 cls: 'roo-signature-body',
45004 cls: 'roo-signature-body-canvas',
45005 height: this.canvas_height,
45006 width: this.canvas_width
45013 style: 'display: none'
45021 initEvents: function()
45023 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45025 var canvas = this.canvasEl();
45027 // mouse && touch event swapping...
45028 canvas.dom.style.touchAction = 'none';
45029 canvas.dom.style.msTouchAction = 'none';
45031 this.mouse_btn_down = false;
45032 canvas.on('mousedown', this._handleMouseDown, this);
45033 canvas.on('mousemove', this._handleMouseMove, this);
45034 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45036 if (window.PointerEvent) {
45037 canvas.on('pointerdown', this._handleMouseDown, this);
45038 canvas.on('pointermove', this._handleMouseMove, this);
45039 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45042 if ('ontouchstart' in window) {
45043 canvas.on('touchstart', this._handleTouchStart, this);
45044 canvas.on('touchmove', this._handleTouchMove, this);
45045 canvas.on('touchend', this._handleTouchEnd, this);
45048 Roo.EventManager.onWindowResize(this.resize, this, true);
45050 // file input event
45051 this.fileEl().on('change', this.uploadImage, this);
45058 resize: function(){
45060 var canvas = this.canvasEl().dom;
45061 var ctx = this.canvasElCtx();
45062 var img_data = false;
45064 if(canvas.width > 0) {
45065 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45067 // setting canvas width will clean img data
45070 var style = window.getComputedStyle ?
45071 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45073 var padding_left = parseInt(style.paddingLeft) || 0;
45074 var padding_right = parseInt(style.paddingRight) || 0;
45076 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45079 ctx.putImageData(img_data, 0, 0);
45083 _handleMouseDown: function(e)
45085 if (e.browserEvent.which === 1) {
45086 this.mouse_btn_down = true;
45087 this.strokeBegin(e);
45091 _handleMouseMove: function (e)
45093 if (this.mouse_btn_down) {
45094 this.strokeMoveUpdate(e);
45098 _handleMouseUp: function (e)
45100 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45101 this.mouse_btn_down = false;
45106 _handleTouchStart: function (e) {
45108 e.preventDefault();
45109 if (e.browserEvent.targetTouches.length === 1) {
45110 // var touch = e.browserEvent.changedTouches[0];
45111 // this.strokeBegin(touch);
45113 this.strokeBegin(e); // assume e catching the correct xy...
45117 _handleTouchMove: function (e) {
45118 e.preventDefault();
45119 // var touch = event.targetTouches[0];
45120 // _this._strokeMoveUpdate(touch);
45121 this.strokeMoveUpdate(e);
45124 _handleTouchEnd: function (e) {
45125 var wasCanvasTouched = e.target === this.canvasEl().dom;
45126 if (wasCanvasTouched) {
45127 e.preventDefault();
45128 // var touch = event.changedTouches[0];
45129 // _this._strokeEnd(touch);
45134 reset: function () {
45135 this._lastPoints = [];
45136 this._lastVelocity = 0;
45137 this._lastWidth = (this.min_width + this.max_width) / 2;
45138 this.canvasElCtx().fillStyle = this.dot_color;
45141 strokeMoveUpdate: function(e)
45143 this.strokeUpdate(e);
45145 if (this.throttle) {
45146 this.throttleStroke(this.strokeUpdate, this.throttle);
45149 this.strokeUpdate(e);
45153 strokeBegin: function(e)
45155 var newPointGroup = {
45156 color: this.dot_color,
45160 if (typeof this.onBegin === 'function') {
45164 this.curve_data.push(newPointGroup);
45166 this.strokeUpdate(e);
45169 strokeUpdate: function(e)
45171 var rect = this.canvasEl().dom.getBoundingClientRect();
45172 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45173 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45174 var lastPoints = lastPointGroup.points;
45175 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45176 var isLastPointTooClose = lastPoint
45177 ? point.distanceTo(lastPoint) <= this.min_distance
45179 var color = lastPointGroup.color;
45180 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45181 var curve = this.addPoint(point);
45183 this.drawDot({color: color, point: point});
45186 this.drawCurve({color: color, curve: curve});
45196 strokeEnd: function(e)
45198 this.strokeUpdate(e);
45199 if (typeof this.onEnd === 'function') {
45204 addPoint: function (point) {
45205 var _lastPoints = this._lastPoints;
45206 _lastPoints.push(point);
45207 if (_lastPoints.length > 2) {
45208 if (_lastPoints.length === 3) {
45209 _lastPoints.unshift(_lastPoints[0]);
45211 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45212 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45213 _lastPoints.shift();
45219 calculateCurveWidths: function (startPoint, endPoint) {
45220 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45221 (1 - this.velocity_filter_weight) * this._lastVelocity;
45223 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45226 start: this._lastWidth
45229 this._lastVelocity = velocity;
45230 this._lastWidth = newWidth;
45234 drawDot: function (_a) {
45235 var color = _a.color, point = _a.point;
45236 var ctx = this.canvasElCtx();
45237 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45239 this.drawCurveSegment(point.x, point.y, width);
45241 ctx.fillStyle = color;
45245 drawCurve: function (_a) {
45246 var color = _a.color, curve = _a.curve;
45247 var ctx = this.canvasElCtx();
45248 var widthDelta = curve.endWidth - curve.startWidth;
45249 var drawSteps = Math.floor(curve.length()) * 2;
45251 ctx.fillStyle = color;
45252 for (var i = 0; i < drawSteps; i += 1) {
45253 var t = i / drawSteps;
45259 var x = uuu * curve.startPoint.x;
45260 x += 3 * uu * t * curve.control1.x;
45261 x += 3 * u * tt * curve.control2.x;
45262 x += ttt * curve.endPoint.x;
45263 var y = uuu * curve.startPoint.y;
45264 y += 3 * uu * t * curve.control1.y;
45265 y += 3 * u * tt * curve.control2.y;
45266 y += ttt * curve.endPoint.y;
45267 var width = curve.startWidth + ttt * widthDelta;
45268 this.drawCurveSegment(x, y, width);
45274 drawCurveSegment: function (x, y, width) {
45275 var ctx = this.canvasElCtx();
45277 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45278 this.is_empty = false;
45283 var ctx = this.canvasElCtx();
45284 var canvas = this.canvasEl().dom;
45285 ctx.fillStyle = this.bg_color;
45286 ctx.clearRect(0, 0, canvas.width, canvas.height);
45287 ctx.fillRect(0, 0, canvas.width, canvas.height);
45288 this.curve_data = [];
45290 this.is_empty = true;
45295 return this.el.select('input',true).first();
45298 canvasEl: function()
45300 return this.el.select('canvas',true).first();
45303 canvasElCtx: function()
45305 return this.el.select('canvas',true).first().dom.getContext('2d');
45308 getImage: function(type)
45310 if(this.is_empty) {
45315 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45318 drawFromImage: function(img_src)
45320 var img = new Image();
45322 img.onload = function(){
45323 this.canvasElCtx().drawImage(img, 0, 0);
45328 this.is_empty = false;
45331 selectImage: function()
45333 this.fileEl().dom.click();
45336 uploadImage: function(e)
45338 var reader = new FileReader();
45340 reader.onload = function(e){
45341 var img = new Image();
45342 img.onload = function(){
45344 this.canvasElCtx().drawImage(img, 0, 0);
45346 img.src = e.target.result;
45349 reader.readAsDataURL(e.target.files[0]);
45352 // Bezier Point Constructor
45353 Point: (function () {
45354 function Point(x, y, time) {
45357 this.time = time || Date.now();
45359 Point.prototype.distanceTo = function (start) {
45360 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45362 Point.prototype.equals = function (other) {
45363 return this.x === other.x && this.y === other.y && this.time === other.time;
45365 Point.prototype.velocityFrom = function (start) {
45366 return this.time !== start.time
45367 ? this.distanceTo(start) / (this.time - start.time)
45374 // Bezier Constructor
45375 Bezier: (function () {
45376 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45377 this.startPoint = startPoint;
45378 this.control2 = control2;
45379 this.control1 = control1;
45380 this.endPoint = endPoint;
45381 this.startWidth = startWidth;
45382 this.endWidth = endWidth;
45384 Bezier.fromPoints = function (points, widths, scope) {
45385 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45386 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45387 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45389 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45390 var dx1 = s1.x - s2.x;
45391 var dy1 = s1.y - s2.y;
45392 var dx2 = s2.x - s3.x;
45393 var dy2 = s2.y - s3.y;
45394 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45395 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45396 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45397 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45398 var dxm = m1.x - m2.x;
45399 var dym = m1.y - m2.y;
45400 var k = l2 / (l1 + l2);
45401 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45402 var tx = s2.x - cm.x;
45403 var ty = s2.y - cm.y;
45405 c1: new scope.Point(m1.x + tx, m1.y + ty),
45406 c2: new scope.Point(m2.x + tx, m2.y + ty)
45409 Bezier.prototype.length = function () {
45414 for (var i = 0; i <= steps; i += 1) {
45416 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45417 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45419 var xdiff = cx - px;
45420 var ydiff = cy - py;
45421 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45428 Bezier.prototype.point = function (t, start, c1, c2, end) {
45429 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45430 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45431 + (3.0 * c2 * (1.0 - t) * t * t)
45432 + (end * t * t * t);
45437 throttleStroke: function(fn, wait) {
45438 if (wait === void 0) { wait = 250; }
45440 var timeout = null;
45444 var later = function () {
45445 previous = Date.now();
45447 result = fn.apply(storedContext, storedArgs);
45449 storedContext = null;
45453 return function wrapper() {
45455 for (var _i = 0; _i < arguments.length; _i++) {
45456 args[_i] = arguments[_i];
45458 var now = Date.now();
45459 var remaining = wait - (now - previous);
45460 storedContext = this;
45462 if (remaining <= 0 || remaining > wait) {
45464 clearTimeout(timeout);
45468 result = fn.apply(storedContext, storedArgs);
45470 storedContext = null;
45474 else if (!timeout) {
45475 timeout = window.setTimeout(later, remaining);